├── .github
├── ISSUE_TEMPLATE
│ ├── bug-issue-template.md
│ ├── config.yml
│ └── feature-request-template.md
└── workflows
│ ├── debug.yml
│ └── lint.yml
├── .gitignore
├── LICENSE
├── README.md
├── READMEME.md
├── app
├── .gitignore
├── build.gradle.kts
├── google-services.json
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── vanced
│ │ └── manager
│ │ ├── ManagerApplication.kt
│ │ ├── di
│ │ ├── APIModule.kt
│ │ ├── CustomTabsModule.kt
│ │ ├── DatasourceModule.kt
│ │ ├── DownloaderModule.kt
│ │ ├── InstallerModuke.kt
│ │ ├── ManagerModule.kt
│ │ ├── NetworkModule.kt
│ │ ├── RepositoryModule.kt
│ │ ├── ServiceModule.kt
│ │ └── ViewModelModule.kt
│ │ ├── domain
│ │ └── model
│ │ │ ├── App.kt
│ │ │ └── InstallationOption.kt
│ │ ├── downloader
│ │ ├── api
│ │ │ ├── MicrogAPI.kt
│ │ │ ├── MusicAPI.kt
│ │ │ └── VancedAPI.kt
│ │ ├── base
│ │ │ └── AppDownloader.kt
│ │ ├── impl
│ │ │ ├── MicrogDownloader.kt
│ │ │ ├── MusicDownloader.kt
│ │ │ └── VancedDownloader.kt
│ │ └── util
│ │ │ └── DownloadPath.kt
│ │ ├── installer
│ │ ├── base
│ │ │ └── AppInstaller.kt
│ │ ├── impl
│ │ │ ├── MicrogInstaller.kt
│ │ │ ├── MusicInstaller.kt
│ │ │ └── VancedInstaller.kt
│ │ ├── service
│ │ │ ├── AppInstallService.kt
│ │ │ └── AppUninstallService.kt
│ │ └── util
│ │ │ ├── PM.kt
│ │ │ ├── PMRoot.kt
│ │ │ ├── PMRootResult.kt
│ │ │ ├── Patcher.kt
│ │ │ └── RootPatchHelper.kt
│ │ ├── io
│ │ ├── ManagerSuFile.kt
│ │ └── SUIOException.kt
│ │ ├── network
│ │ ├── GithubService.kt
│ │ ├── dto
│ │ │ └── GithubDto.kt
│ │ └── util
│ │ │ └── Constants.kt
│ │ ├── preferences
│ │ ├── ManagerPreference.kt
│ │ └── holder
│ │ │ ├── PreferenceDefaultValueHolder.kt
│ │ │ ├── PreferenceHolder.kt
│ │ │ └── PreferenceKeyHolder.kt
│ │ ├── repository
│ │ ├── AppRepository.kt
│ │ ├── PreferenceRepository.kt
│ │ ├── manager
│ │ │ └── PackageManager.kt
│ │ └── source
│ │ │ └── PreferenceDatasource.kt
│ │ ├── ui
│ │ ├── MainActivity.kt
│ │ ├── SplashScreenActivity.kt
│ │ ├── component
│ │ │ ├── ManagerButton.kt
│ │ │ ├── ManagerCard.kt
│ │ │ ├── ManagerDialog.kt
│ │ │ ├── ManagerDropdownMenu.kt
│ │ │ ├── ManagerLazyDsl.kt
│ │ │ ├── ManagerListItem.kt
│ │ │ ├── ManagerNavigator.kt
│ │ │ ├── ManagerPreference.kt
│ │ │ ├── ManagerProgressIndicator.kt
│ │ │ ├── ManagerScaffold.kt
│ │ │ ├── ManagerSwipeRefresh.kt
│ │ │ ├── ManagerText.kt
│ │ │ └── ManagerTopAppBar.kt
│ │ ├── resource
│ │ │ └── ManagerString.kt
│ │ ├── screen
│ │ │ ├── AboutScreen.kt
│ │ │ ├── ConfigurationScreen.kt
│ │ │ ├── HomeScreen.kt
│ │ │ ├── InstallScreen.kt
│ │ │ └── SettingsScreen.kt
│ │ ├── theme
│ │ │ ├── Color.kt
│ │ │ ├── Shape.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ │ ├── util
│ │ │ ├── Color.kt
│ │ │ ├── Const.kt
│ │ │ └── Screen.kt
│ │ ├── viewmodel
│ │ │ ├── ConfigurationViewModel.kt
│ │ │ ├── InstallViewModel.kt
│ │ │ ├── MainViewModel.kt
│ │ │ └── SettingsViewModel.kt
│ │ └── widget
│ │ │ ├── AppCard.kt
│ │ │ └── LinkCard.kt
│ │ └── util
│ │ ├── AppHelper.kt
│ │ ├── Arch.kt
│ │ ├── Coroutines.kt
│ │ ├── IO.kt
│ │ ├── Safety.kt
│ │ └── SuShell.kt
│ └── res
│ ├── animator
│ ├── fragment_enter.xml
│ ├── fragment_enter_pop.xml
│ ├── fragment_exit.xml
│ └── fragment_exit_pop.xml
│ ├── drawable-anydpi-v24
│ └── ic_stat_name.xml
│ ├── drawable-hdpi
│ ├── ic_splash_logo.png
│ └── ic_stat_name.png
│ ├── drawable-ldpi
│ └── ic_splash_logo.png
│ ├── drawable-mdpi
│ ├── ic_splash_logo.png
│ └── ic_stat_name.png
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable-xhdpi
│ ├── ic_splash_logo.png
│ └── ic_stat_name.png
│ ├── drawable-xxhdpi
│ ├── ic_splash_logo.png
│ └── ic_stat_name.png
│ ├── drawable-xxxhdpi
│ └── ic_splash_logo.png
│ ├── drawable
│ ├── ic_adguard.xml
│ ├── ic_android_black_24dp.xml
│ ├── ic_app_icon_placeholder.xml
│ ├── ic_brave.xml
│ ├── ic_brave_light.xml
│ ├── ic_discord.xml
│ ├── ic_github.xml
│ ├── ic_instagram.xml
│ ├── ic_launch_text.xml
│ ├── ic_magisk.xml
│ ├── ic_manager.xml
│ ├── ic_manager_monet_icon.xml
│ ├── ic_microg.xml
│ ├── ic_music.xml
│ ├── ic_reddit.xml
│ ├── ic_round_assignment_24.xml
│ ├── ic_round_close_24.xml
│ ├── ic_round_done_24.xml
│ ├── ic_splash.xml
│ ├── ic_telegram.xml
│ ├── ic_twitter.xml
│ ├── ic_vanced.xml
│ ├── ic_website.xml
│ └── ic_youtube.xml
│ ├── font
│ ├── inter_black.ttf
│ ├── inter_bold.ttf
│ ├── inter_extralight.ttf
│ ├── inter_extranold.ttf
│ ├── inter_light.ttf
│ ├── inter_medium.ttf
│ ├── inter_regular.ttf
│ ├── inter_semibold.ttf
│ └── inter_thin.ttf
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── values-af-rZA
│ └── strings.xml
│ ├── values-ar-rSA
│ └── strings.xml
│ ├── values-az-rAZ
│ └── strings.xml
│ ├── values-bg-rBG
│ └── strings.xml
│ ├── values-bn-rBD
│ └── strings.xml
│ ├── values-bn-rIN
│ └── strings.xml
│ ├── values-ca-rES
│ └── strings.xml
│ ├── values-ckb-rIR
│ └── strings.xml
│ ├── values-cs-rCZ
│ └── strings.xml
│ ├── values-da-rDK
│ └── strings.xml
│ ├── values-de-rDE
│ └── strings.xml
│ ├── values-el-rGR
│ └── strings.xml
│ ├── values-es-rES
│ └── strings.xml
│ ├── values-et-rEE
│ └── strings.xml
│ ├── values-fi-rFI
│ └── strings.xml
│ ├── values-fil-rPH
│ └── strings.xml
│ ├── values-fr-rFR
│ └── strings.xml
│ ├── values-hi-rIN
│ └── strings.xml
│ ├── values-hr-rHR
│ └── strings.xml
│ ├── values-hu-rHU
│ └── strings.xml
│ ├── values-in-rID
│ └── strings.xml
│ ├── values-it-rIT
│ └── strings.xml
│ ├── values-iw-rIL
│ └── strings.xml
│ ├── values-ja-rJP
│ └── strings.xml
│ ├── values-ka-rGE
│ └── strings.xml
│ ├── values-kmr-rTR
│ └── strings.xml
│ ├── values-ko-rKR
│ └── strings.xml
│ ├── values-ku-rTR
│ └── strings.xml
│ ├── values-lt-rLT
│ └── strings.xml
│ ├── values-ml-rIN
│ └── strings.xml
│ ├── values-mr-rIN
│ └── strings.xml
│ ├── values-night
│ └── colors.xml
│ ├── values-nl-rNL
│ └── strings.xml
│ ├── values-no-rNO
│ └── strings.xml
│ ├── values-pa-rIN
│ └── strings.xml
│ ├── values-pa-rPK
│ └── strings.xml
│ ├── values-pl-rPL
│ └── strings.xml
│ ├── values-ps-rAF
│ └── strings.xml
│ ├── values-pt-rBR
│ └── strings.xml
│ ├── values-pt-rPT
│ └── strings.xml
│ ├── values-ro-rRO
│ └── strings.xml
│ ├── values-ru-rRU
│ └── strings.xml
│ ├── values-si-rLK
│ └── strings.xml
│ ├── values-sk-rSK
│ └── strings.xml
│ ├── values-so-rSO
│ └── strings.xml
│ ├── values-sr-rSP
│ └── strings.xml
│ ├── values-sv-rSE
│ └── strings.xml
│ ├── values-ta-rIN
│ └── strings.xml
│ ├── values-th-rTH
│ └── strings.xml
│ ├── values-tr-rTR
│ └── strings.xml
│ ├── values-uk-rUA
│ └── strings.xml
│ ├── values-vi-rVN
│ └── strings.xml
│ ├── values-zh-rCN
│ └── strings.xml
│ ├── values-zh-rTW
│ └── strings.xml
│ ├── values
│ ├── arrays.xml
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ic_launcher_background.xml
│ ├── resources.xml
│ ├── strings.xml
│ └── themes.xml
│ └── xml
│ └── file_provider.xml
├── build.gradle.kts
├── crowdin.yml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.github/ISSUE_TEMPLATE/bug-issue-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug issue template
3 | about: Vanced Manager Bug template
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Please only open the issue if the following is true**
11 | - This is an issue in the Vanced Manager and ONLY Vanced Manager (NOT Youtube Vanced/Music/microG)
12 |
13 | **Phone Specifications:**
14 | - Brand:
15 | - Operating System:
16 | - Android Version:
17 | - Vanced Manager Version:
18 |
19 | **Please describe the problem you are having in as much detail as possible:**
20 |
21 |
22 | **Steps to reproduce:**
23 |
24 |
25 | **Further details:**
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request Template
3 | about: Vanced Manager Feature Request Template
4 | title: ''
5 | labels: 'enhancement'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Please only open the issue if the following is true**
11 | - This is an issue in the Vanced Manager and ONLY Vanced Manager (NOT Youtube Vanced/Music/microG)
12 |
13 | **Suggestion:**
14 |
15 | **Why is this suggestion relevant?**
16 |
17 | **Further details:**
18 |
--------------------------------------------------------------------------------
/.github/workflows/debug.yml:
--------------------------------------------------------------------------------
1 | name: Debug APK Builder
2 |
3 | on:
4 | push:
5 | branches:
6 | - dev
7 | paths-ignore:
8 | - '**.md'
9 | pull_request:
10 | branches:
11 | - dev
12 | paths-ignore:
13 | - '**.md'
14 |
15 | jobs:
16 | build:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@v1
20 |
21 | - name: set up JDK 1.8
22 | uses: actions/setup-java@v1
23 | with:
24 | java-version: 1.8
25 |
26 | - name: Grant rights
27 | run: chmod +x ./gradlew
28 |
29 | - name: Build project with Gradle
30 | run: ./gradlew build
31 |
32 | - name: Build Debug APK with Gradle
33 | run: ./gradlew assembleDebug
34 |
35 | - name: Upload Debug
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: 'Manager'
39 | path: app/build/outputs/apk/debug/app-debug.apk
40 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Code Linter
2 |
3 | on:
4 | push:
5 | branches:
6 | - dev
7 |
8 | jobs:
9 | qodana:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - uses: actions/cache@v2
15 | with:
16 | path: ~/work/_temp/_github_home/cache
17 | key: ${{ runner.os }}-qodana-${{ github.ref }}
18 | restore-keys: |
19 | ${{ runner.os }}-qodana-${{ github.ref }}
20 | ${{ runner.os }}-qodana-
21 | - uses: docker://jetbrains/qodana-jvm-android
22 | with:
23 | args: --cache-dir=/github/home/cache --results-dir=/github/workspace/qodana --save-report --report-dir=/github/workspace/qodana/report
24 | - uses: actions/upload-artifact@v2
25 | with:
26 | path: qodana
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle/
2 | .idea/
3 | build/
4 | local.properties
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vanced Manager
2 | [](https://github.com/YTVanced/VancedManager/releases/latest) [](https://github.com/YTVanced/VancedManager/releases/latest)
3 |
4 | # **Now discontinued https://twitter.com/YTVanced/status/1503052250268286980**
5 |
6 | Hi, when we released Vanced 15.05.54, people were upset because it used the .apks format, which was way harder to install than a traditional .apk file. Even though we wrote clear instructions on how to install the new Vanced build, people still couldn't figure it out.
7 | Then we thought, "why don't we make a manager for vanced, which will download, update and uninstall Vanced and MicroG, have an easy and understandable UI and be less than 10mb?" and that's how Vanced Manager was born.
8 |
9 | After 3 months of development, we are finally ready to introduce Vanced Manager to the masses. Vanced manager can easily install and uninstall vanced and microg, has various settings for customisation and better experience. The Manager comes with an easy-to-use interface
10 |
11 | ##### Background download/installation feature is no longer supported due to problems with some ROMs, please do NOT report issues regarding background activity.
12 |
13 | ## Contributions
14 | Pull requests should be made to the Dev branch as that is the working branch, master is for release code.
15 |
16 | For anyone who wants to provide translations please submit them to https://crowdin.com/project/vanced-manager as we also use it for Vanced. Any issues with translations should be posted there too.
17 |
18 | ## Building
19 |
20 |
21 |
22 | [](https://github.com/YTVanced/VancedManager/actions/workflows/debug.yml)
23 |
24 |
25 |
26 | ### Using Android Studio
27 | Clone the repo, open it in Android Studio and build the app.
28 |
29 | ### Using command line
30 | #### On Windows:
31 | ```powershell
32 | .\gradlew.bat assembleDebug
33 | ```
34 | #### On Linux/macOS:
35 | ```bash
36 | chmod +x gradlew
37 | ./gradlew assembleDebug
38 | ```
39 |
--------------------------------------------------------------------------------
/READMEME.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Prelude
4 | Hi, my name is Steve Cock, I'm the main developer for the upcoming Vanced Manager. When xfileFIN first published Vanced 15.05.54, people were upset because new Vanced used split apk files. The reason for that was pretty simple:
5 | 1) YouTube itself does that
6 | 2) Split apk files reduce the size of the downloaded file itself
7 |
8 | No one really thought there would be problems with this format, because installation was pretty simple, at least that's what xfile thought...
9 | ## Problems with .apks format
10 | Main problems with new format were either with device CPU architecture or MemeUI shit with MiUI optimisations. We wrote instructions for VancedHelper but no one used it for troubleshooting. Then some users complained about new format and refused to upgrade to newest version (We don't give a fuck about that) because "I dOn'T WaNT To HaVe OnE MoRE apP To insTalL VanCeD" so we decided to make an installer for Vanced
11 | # Vanced Manager
12 | Ladies and gentlemen, I'm very proud to introduce the new **Vancad Banger 2.0.0.0.0.0.0.0.0.0.0.0.0™** (typo intended)
13 | Vanced Manager is an universal utility for installing/updating Vanced and MicroG. It will push notifications once the update is ready (Now that's what I call pwetty epic).
14 | Vanced Manager comes with a slick UI ~~that was stolen from the new Magisk Manager (I'm very sorry John but I looked at your code for about 100 times). Actually, while UI may look very similar to new Magisk Manager's UI, It's still very different (that's a blatant lie, I know).~~ <- diz shit completely invalid now so suck my balls
15 |
16 | Main Menu screenshot taken from tablet
17 | 
18 | Isn't this lovely and beautiful?
19 |
20 | ## Manager (clap) Reviews (clap)
21 |
22 | - 1337Potato: shit
23 | - Response: Yes
24 |
25 | ===================
26 |
27 | - Noobbot: The app is not useful because I have YT Premium. Thank you bye
28 | - Response: I hope you get sucked by a di-
29 |
30 | ===================
31 |
32 | - Vortextriangle: The app is so useful that I uninstalled it after installing Vanced
33 | - Response: yo that's finna woke
34 |
35 | ## How does it suck?™
36 | Vanced Manager sucks 100% of your CPU to mine Bitcoins, this is a new technique called CryptocurrencySucker2077. Basically we load up your shit MediaTek MT 8163 with processes that help us mine cryptocurrency, this is how Vanced Team makes money (excluding BAT and AdGuard referrals)
37 |
38 | ## Credits
39 | ### Vanced Manager developers
40 | - X1nto (UI, UX, Downloader, Installer, Signature Checker, PussiSlayer69, Collector of 400 BAT, A great liar)
41 | - Koopah (Unix lord, Unmounter of /system, Code criticizer)
42 | ### The Vanced Team
43 | - xfileFIN
44 | 
45 | - KevinX8
46 | 
47 | - Zanezam
48 | 
49 | - Laura Almeida
50 | 
51 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /release
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "840099702732",
4 | "firebase_url": "https://vanced-manager-official.firebaseio.com",
5 | "project_id": "vanced-manager-official",
6 | "storage_bucket": "vanced-manager-official.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:840099702732:android:ca65567b49f622bc359f69",
12 | "android_client_info": {
13 | "package_name": "com.vanced.manager"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "840099702732-4fjjofq6on2bpd7jb6f96bk0mkrjkkf6.apps.googleusercontent.com",
19 | "client_type": 3
20 | }
21 | ],
22 | "api_key": [
23 | {
24 | "current_key": "AIzaSyAPI1RUaoCHmmWz9-TLvTKYPYs0ZVKkS2U"
25 | }
26 | ],
27 | "services": {
28 | "appinvite_service": {
29 | "other_platform_oauth_client": [
30 | {
31 | "client_id": "840099702732-4fjjofq6on2bpd7jb6f96bk0mkrjkkf6.apps.googleusercontent.com",
32 | "client_type": 3
33 | }
34 | ]
35 | }
36 | }
37 | }
38 | ],
39 | "configuration_version": "1"
40 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.kts.kts.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | -keep class com.vanced.manager.network.dto.GithubReleaseDto {
16 | *;
17 | }
18 |
19 | -keep class com.vanced.manager.network.dto.GithubReleaseAssetDto {
20 | *;
21 | }
22 |
23 | # Uncomment this to preserve the line number information for
24 | # debugging stack traces.
25 | -keepattributes SourceFile, LineNumberTable
26 |
27 | # If you keep the line number information, uncomment this to
28 | # hide the original source file name.
29 | #-renamesourcefileattribute SourceFile
30 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ManagerApplication.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager
2 |
3 | import android.app.Application
4 | import com.vanced.manager.di.*
5 | import org.koin.android.ext.koin.androidContext
6 | import org.koin.core.context.startKoin
7 |
8 | class ManagerApplication : Application() {
9 |
10 | override fun onCreate() {
11 | super.onCreate()
12 |
13 | startKoin {
14 | androidContext(this@ManagerApplication)
15 |
16 | modules(
17 | apiModule,
18 | customTabsModule,
19 | datasourceModule,
20 | downloaderModule,
21 | installerModule,
22 | managerModule,
23 | networkModule,
24 | repositoryModule,
25 | serviceModule,
26 | viewModelModule,
27 | )
28 | }
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/APIModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import com.vanced.manager.downloader.api.MicrogAPI
4 | import com.vanced.manager.downloader.api.MusicAPI
5 | import com.vanced.manager.downloader.api.VancedAPI
6 | import com.vanced.manager.network.util.BASE
7 | import okhttp3.OkHttpClient
8 | import org.koin.dsl.module
9 | import retrofit2.Retrofit
10 | import retrofit2.create
11 |
12 | //TODO Add mirror support
13 | val apiModule = module {
14 |
15 | fun provideVancedAPI(
16 | okHttpClient: OkHttpClient
17 | ): VancedAPI {
18 | return Retrofit.Builder()
19 | .baseUrl(BASE)
20 | .client(okHttpClient)
21 | .build()
22 | .create()
23 | }
24 |
25 | fun provideMusicAPI(
26 | okHttpClient: OkHttpClient
27 | ): MusicAPI {
28 | return Retrofit.Builder()
29 | .baseUrl(BASE)
30 | .client(okHttpClient)
31 | .build()
32 | .create()
33 | }
34 |
35 | fun provideMicrogAPI(
36 | okHttpClient: OkHttpClient
37 | ): MicrogAPI {
38 | return Retrofit.Builder()
39 | .baseUrl("https://github.com/YTVanced/VancedMicroG/")
40 | .client(okHttpClient)
41 | .build()
42 | .create(MicrogAPI::class.java)
43 | }
44 |
45 | single { provideVancedAPI(get()) }
46 | single { provideMusicAPI(get()) }
47 | single { provideMicrogAPI(get()) }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/CustomTabsModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import androidx.browser.customtabs.CustomTabsIntent
4 | import org.koin.dsl.module
5 |
6 | val customTabsModule = module {
7 |
8 | fun provideChromeCustomTabs(): CustomTabsIntent {
9 | return CustomTabsIntent.Builder()
10 | .build()
11 | }
12 |
13 | single { provideChromeCustomTabs() }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/DatasourceModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import android.content.Context
4 | import com.vanced.manager.repository.source.PreferenceDatasource
5 | import com.vanced.manager.repository.source.PreferenceDatasourceImpl
6 | import org.koin.android.ext.koin.androidContext
7 | import org.koin.dsl.module
8 |
9 | val datasourceModule = module {
10 |
11 | fun providePreferenceDatasource(
12 | context: Context
13 | ): PreferenceDatasource {
14 | return PreferenceDatasourceImpl(
15 | sharedPreferences = context.getSharedPreferences(
16 | "manager_settings",
17 | Context.MODE_PRIVATE
18 | )
19 | )
20 | }
21 |
22 | single { providePreferenceDatasource(androidContext()) }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/DownloaderModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import android.content.Context
4 | import com.vanced.manager.downloader.api.MicrogAPI
5 | import com.vanced.manager.downloader.api.MusicAPI
6 | import com.vanced.manager.downloader.api.VancedAPI
7 | import com.vanced.manager.downloader.impl.MicrogDownloader
8 | import com.vanced.manager.downloader.impl.MusicDownloader
9 | import com.vanced.manager.downloader.impl.VancedDownloader
10 | import org.koin.android.ext.koin.androidContext
11 | import org.koin.dsl.module
12 |
13 | val downloaderModule = module {
14 |
15 | fun provideVancedDownloader(
16 | vancedAPI: VancedAPI,
17 | context: Context,
18 | ): VancedDownloader {
19 | return VancedDownloader(
20 | vancedAPI = vancedAPI,
21 | context = context
22 | )
23 | }
24 |
25 | fun provideMusicDownloader(
26 | musicAPI: MusicAPI,
27 | context: Context,
28 | ): MusicDownloader {
29 | return MusicDownloader(
30 | musicAPI = musicAPI,
31 | context = context
32 | )
33 | }
34 |
35 | fun provideMicrogDownloader(
36 | microgAPI: MicrogAPI,
37 | context: Context,
38 | ): MicrogDownloader {
39 | return MicrogDownloader(
40 | microgAPI = microgAPI,
41 | context = context
42 | )
43 | }
44 |
45 | single { provideVancedDownloader(get(), androidContext()) }
46 | single { provideMusicDownloader(get(), androidContext()) }
47 | single { provideMicrogDownloader(get(), androidContext()) }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/InstallerModuke.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import android.content.Context
4 | import com.vanced.manager.installer.impl.MicrogInstaller
5 | import com.vanced.manager.installer.impl.MusicInstaller
6 | import com.vanced.manager.installer.impl.VancedInstaller
7 | import com.vanced.manager.repository.manager.NonrootPackageManager
8 | import com.vanced.manager.repository.manager.RootPackageManager
9 | import org.koin.android.ext.koin.androidContext
10 | import org.koin.dsl.module
11 |
12 | val installerModule = module {
13 |
14 | fun provideVancedInstaller(
15 | context: Context,
16 | nonrootPackageManager: NonrootPackageManager,
17 | rootPackageManager: RootPackageManager
18 | ): VancedInstaller {
19 | return VancedInstaller(
20 | context = context,
21 | nonrootPackageManager = nonrootPackageManager,
22 | rootPackageManager = rootPackageManager
23 | )
24 | }
25 |
26 | fun provideMusicInstaller(
27 | context: Context,
28 | nonrootPackageManager: NonrootPackageManager,
29 | rootPackageManager: RootPackageManager
30 | ): MusicInstaller {
31 | return MusicInstaller(
32 | context = context,
33 | nonrootPackageManager = nonrootPackageManager,
34 | rootPackageManager = rootPackageManager
35 | )
36 | }
37 |
38 | fun provideMicrogInstaller(
39 | context: Context,
40 | nonrootPackageManager: NonrootPackageManager,
41 | ): MicrogInstaller {
42 | return MicrogInstaller(
43 | context = context,
44 | nonrootPackageManager = nonrootPackageManager
45 | )
46 | }
47 |
48 | single { provideVancedInstaller(androidContext(), get(), get()) }
49 | single { provideMusicInstaller(androidContext(), get(), get()) }
50 | single { provideMicrogInstaller(androidContext(), get()) }
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/ManagerModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import android.content.Context
4 | import com.vanced.manager.repository.manager.NonrootPackageManager
5 | import com.vanced.manager.repository.manager.RootPackageManager
6 | import org.koin.android.ext.koin.androidContext
7 | import org.koin.dsl.module
8 |
9 | val managerModule = module {
10 |
11 | fun provideNonrootPackageManager(
12 | context: Context
13 | ): NonrootPackageManager {
14 | return NonrootPackageManager(
15 | context = context
16 | )
17 | }
18 |
19 | fun provideRootPackageManager(): RootPackageManager {
20 | return RootPackageManager()
21 | }
22 |
23 | single { provideNonrootPackageManager(androidContext()) }
24 | single { provideRootPackageManager() }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/NetworkModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import okhttp3.OkHttpClient
4 | import org.koin.dsl.module
5 |
6 | val networkModule = module {
7 |
8 | fun provideOkHttpClient(): OkHttpClient {
9 | return OkHttpClient.Builder()
10 | .build()
11 | }
12 |
13 | single { provideOkHttpClient() }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/RepositoryModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import com.vanced.manager.network.GithubService
4 | import com.vanced.manager.repository.AppRepository
5 | import com.vanced.manager.repository.AppRepositoryImpl
6 | import com.vanced.manager.repository.PreferenceRepository
7 | import com.vanced.manager.repository.PreferenceRepositoryImpl
8 | import com.vanced.manager.repository.manager.NonrootPackageManager
9 | import com.vanced.manager.repository.manager.RootPackageManager
10 | import com.vanced.manager.repository.source.PreferenceDatasource
11 | import org.koin.dsl.module
12 |
13 | val repositoryModule = module {
14 |
15 | fun provideGithubRepository(
16 | githubService: GithubService,
17 | nonrootPackageManager: NonrootPackageManager,
18 | rootPackageManager: RootPackageManager,
19 | ): AppRepository {
20 | return AppRepositoryImpl(
21 | githubService = githubService,
22 | nonrootPackageManager = nonrootPackageManager,
23 | rootPackageManager = rootPackageManager
24 | )
25 | }
26 |
27 | fun providePreferenceRepository(
28 | preferenceDatasource: PreferenceDatasource
29 | ): PreferenceRepository {
30 | return PreferenceRepositoryImpl(
31 | preferenceDatasource = preferenceDatasource
32 | )
33 | }
34 |
35 | single { provideGithubRepository(get(), get(), get()) }
36 | single { providePreferenceRepository(get()) }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/ServiceModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
4 | import com.vanced.manager.network.GithubService
5 | import com.vanced.manager.network.util.GITHUB_API_BASE
6 | import kotlinx.serialization.json.Json
7 | import okhttp3.MediaType
8 | import okhttp3.OkHttpClient
9 | import org.koin.dsl.module
10 | import retrofit2.Retrofit
11 | import retrofit2.create
12 |
13 | private val json = Json {
14 | ignoreUnknownKeys = true
15 | }
16 |
17 | val serviceModule = module {
18 |
19 | fun provideGithubService(
20 | okHttpClient: OkHttpClient
21 | ): GithubService {
22 | return Retrofit.Builder()
23 | .baseUrl(GITHUB_API_BASE)
24 | .addConverterFactory(json.asConverterFactory(MediaType.get("application/json")))
25 | .client(okHttpClient)
26 | .build()
27 | .create()
28 | }
29 |
30 | single { provideGithubService(get()) }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/di/ViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.di
2 |
3 | import android.app.Application
4 | import com.vanced.manager.downloader.impl.MicrogDownloader
5 | import com.vanced.manager.downloader.impl.MusicDownloader
6 | import com.vanced.manager.downloader.impl.VancedDownloader
7 | import com.vanced.manager.installer.impl.MicrogInstaller
8 | import com.vanced.manager.installer.impl.MusicInstaller
9 | import com.vanced.manager.installer.impl.VancedInstaller
10 | import com.vanced.manager.repository.AppRepository
11 | import com.vanced.manager.repository.PreferenceRepository
12 | import com.vanced.manager.ui.viewmodel.ConfigurationViewModel
13 | import com.vanced.manager.ui.viewmodel.InstallViewModel
14 | import com.vanced.manager.ui.viewmodel.MainViewModel
15 | import com.vanced.manager.ui.viewmodel.SettingsViewModel
16 | import org.koin.android.ext.koin.androidApplication
17 | import org.koin.androidx.viewmodel.dsl.viewModel
18 | import org.koin.dsl.module
19 |
20 | val viewModelModule = module {
21 |
22 | fun provideMainViewModel(
23 | appRepository: AppRepository,
24 | preferenceRepository: PreferenceRepository,
25 | app: Application,
26 | ): MainViewModel {
27 | return MainViewModel(
28 | appRepository = appRepository,
29 | preferenceRepository = preferenceRepository,
30 | app = app
31 | )
32 | }
33 |
34 | fun provideInstallViewModel(
35 | vancedDownloader: VancedDownloader,
36 | musicDownloader: MusicDownloader,
37 | microgDownloader: MicrogDownloader,
38 |
39 | vancedInstaller: VancedInstaller,
40 | musicInstaller: MusicInstaller,
41 | microgInstaller: MicrogInstaller,
42 | ): InstallViewModel {
43 | return InstallViewModel(
44 | vancedDownloader = vancedDownloader,
45 | musicDownloader = musicDownloader,
46 | microgDownloader = microgDownloader,
47 |
48 | vancedInstaller = vancedInstaller,
49 | musicInstaller = musicInstaller,
50 | microgInstaller = microgInstaller
51 | )
52 | }
53 |
54 | fun provideConfigurationViewModel(): ConfigurationViewModel {
55 | return ConfigurationViewModel()
56 | }
57 |
58 | fun provideSettingsViewModel(
59 | preferenceRepository: PreferenceRepository
60 | ): SettingsViewModel {
61 | return SettingsViewModel(
62 | preferenceRepository = preferenceRepository
63 | )
64 | }
65 |
66 | viewModel { provideMainViewModel(get(), get(), androidApplication()) }
67 | viewModel { provideInstallViewModel(get(), get(), get(), get(), get(), get()) }
68 | viewModel { provideConfigurationViewModel() }
69 | viewModel { provideSettingsViewModel(get()) }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/domain/model/App.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.domain.model
2 |
3 | import androidx.annotation.DrawableRes
4 | import com.vanced.manager.R
5 |
6 | data class App(
7 | val name: String,
8 | @DrawableRes val iconResId: Int,
9 | val changelog: String,
10 | val remoteVersionCode: Int,
11 | val remoteVersionName: String,
12 | val installedVersionCode: Int?,
13 | val installedVersionName: String?,
14 | val packageName: String,
15 | val launchActivity: String,
16 | val state: AppState,
17 | val app: AppType
18 | )
19 |
20 | object AppData {
21 | const val NAME_VANCED_YOUTUBE = "YouTube Vanced"
22 | const val NAME_VANCED_YOUTUBE_MUSIC = "YouTube Vanced Music"
23 | const val NAME_VANCED_MICROG = "Vanced microG"
24 | const val NAME_VANCED_MANAGER = "Vanced Manager"
25 |
26 | const val ICON_VANCED_YOUTUBE = R.drawable.ic_vanced
27 | const val ICON_VANCED_YOUTUBE_MUSIC = R.drawable.ic_music
28 | const val ICON_VANCED_MICROG = R.drawable.ic_microg
29 | const val ICON_VANCED_MANAGER = R.drawable.ic_manager
30 |
31 | const val PACKAGE_VANCED_YOUTUBE = "com.vanced.android.youtube"
32 | const val PACKAGE_VANCED_YOUTUBE_MUSIC = "com.vanced.android.youtube.apps.music"
33 | const val PACKAGE_VANCED_MICROG = "com.mgoogle.android.gms"
34 | const val PACKAGE_VANCED_MANAGER = "com.vanced.manager"
35 |
36 | const val PACKAGE_ROOT_VANCED_YOUTUBE = "com.google.android.youtube"
37 | const val PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC = "com.google.android.youtube.apps.music"
38 |
39 | const val LAUNCH_ACTIVITY_VANCED_YOUTUBE = "com.google.android.youtube.HomeActivity"
40 | const val LAUNCH_ACTIVITY_VANCED_YOUTUBE_MUSIC =
41 | "com.google.android.apps.youtube.music.activities.MusicActivity"
42 | const val LAUNCH_ACTIVITY_VANCED_MICROG = "org.microg.gms.ui.SettingsActivity"
43 | const val LAUNCH_ACTIVITY_VANCED_MANAGER = ""
44 | }
45 |
46 | enum class AppType {
47 | VANCED_YOUTUBE,
48 | VANCED_YOUTUBE_MUSIC,
49 | VANCED_MICROG,
50 | VANCED_MANAGER,
51 | }
52 |
53 | enum class AppState {
54 | NOT_INSTALLED,
55 | INSTALLED,
56 | NEEDS_UPDATE
57 | }
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/domain/model/InstallationOption.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.domain.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | sealed interface InstallationOption : Parcelable {
7 |
8 | val titleId: Int
9 | val items: List
10 |
11 | @Parcelize
12 | data class MultiSelect(
13 | override val titleId: Int,
14 | override val items: List,
15 | val getOption: () -> Set,
16 | val addOption: (String) -> Unit,
17 | val removeOption: (String) -> Unit
18 | ) : InstallationOption
19 |
20 | @Parcelize
21 | data class SingleSelect(
22 | override val titleId: Int,
23 | override val items: List,
24 | val getOption: () -> String,
25 | val setOption: (String) -> Unit,
26 | ) : InstallationOption
27 |
28 | }
29 |
30 | @Parcelize
31 | data class InstallationOptionItem(
32 | val key: String,
33 | val displayText: (key: String) -> String,
34 | ) : Parcelable
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/api/MicrogAPI.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.api
2 |
3 | import okhttp3.ResponseBody
4 | import retrofit2.Call
5 | import retrofit2.http.GET
6 | import retrofit2.http.Streaming
7 |
8 | interface MicrogAPI {
9 |
10 | @GET("releases/latest/download/microg.apk")
11 | @Streaming
12 | fun getFile(): Call
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/api/MusicAPI.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.api
2 |
3 | import okhttp3.ResponseBody
4 | import retrofit2.Call
5 | import retrofit2.http.GET
6 | import retrofit2.http.Path
7 | import retrofit2.http.Streaming
8 |
9 | interface MusicAPI {
10 |
11 | @GET("music/v{version}/{variant}.apk")
12 | @Streaming
13 | fun getFiles(
14 | @Path("version") version: String,
15 | @Path("variant") variant: String,
16 | ): Call
17 |
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/api/VancedAPI.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.api
2 |
3 | import okhttp3.ResponseBody
4 | import retrofit2.Call
5 | import retrofit2.http.GET
6 | import retrofit2.http.Path
7 | import retrofit2.http.Streaming
8 |
9 | interface VancedAPI {
10 |
11 | @GET("apks/v{version}/{variant}/{type}/{apkName}")
12 | @Streaming
13 | fun getFiles(
14 | @Path("version") version: String,
15 | @Path("variant") variant: String,
16 | @Path("type") type: String,
17 | @Path("apkName") apkName: String,
18 | ): Call
19 |
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/base/AppDownloader.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.base
2 |
3 | import com.vanced.manager.util.writeFile
4 | import okhttp3.ResponseBody
5 | import retrofit2.Call
6 | import retrofit2.awaitResponse
7 |
8 | abstract class AppDownloader {
9 |
10 | data class DownloadFile(
11 | val fileName: String,
12 | val call: Call,
13 | )
14 |
15 | sealed class DownloadStatus {
16 | object Success : DownloadStatus()
17 | data class Error(val error: String, val fileName: String) : DownloadStatus()
18 |
19 | val isSuccess
20 | get() = this is Success
21 |
22 | val isError
23 | get() = this is Error
24 | }
25 |
26 | abstract suspend fun download(
27 | appVersions: List?,
28 | onProgress: (Float) -> Unit,
29 | onFile: (String) -> Unit
30 | ): DownloadStatus
31 |
32 | abstract suspend fun downloadRoot(
33 | appVersions: List?,
34 | onProgress: (Float) -> Unit,
35 | onFile: (String) -> Unit
36 | ): DownloadStatus
37 |
38 | abstract fun getSavedFilePath(): String
39 |
40 | suspend inline fun downloadFiles(
41 | files: Array,
42 | onProgress: (Float) -> Unit,
43 | onFile: (String) -> Unit
44 | ): DownloadStatus {
45 | for (file in files) {
46 | try {
47 | onFile(file.fileName)
48 |
49 | val response = file.call.awaitResponse()
50 | if (response.isSuccessful) {
51 | response.body()?.writeFile(getSavedFilePath() + "/${file.fileName}", onProgress)
52 | continue
53 | }
54 |
55 | val error = response.errorBody()?.toString()
56 | if (error != null) {
57 | return DownloadStatus.Error(error, file.fileName)
58 | }
59 | } catch (e: Exception) {
60 | return DownloadStatus.Error(e.stackTraceToString(), file.fileName)
61 | }
62 | }
63 |
64 | return DownloadStatus.Success
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/impl/MicrogDownloader.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.impl
2 |
3 | import android.content.Context
4 | import com.vanced.manager.downloader.api.MicrogAPI
5 | import com.vanced.manager.downloader.base.AppDownloader
6 | import com.vanced.manager.downloader.util.getMicrogPath
7 | import java.io.File
8 |
9 | class MicrogDownloader(
10 | private val microgAPI: MicrogAPI,
11 | private val context: Context,
12 | ) : AppDownloader() {
13 |
14 | override suspend fun download(
15 | appVersions: List?,
16 | onProgress: (Float) -> Unit,
17 | onFile: (String) -> Unit
18 | ): DownloadStatus {
19 | val downloadStatus = downloadFiles(
20 | files = arrayOf(
21 | DownloadFile(
22 | call = microgAPI.getFile(),
23 | fileName = "microg.apk"
24 | )
25 | ),
26 | onProgress = onProgress,
27 | onFile = onFile
28 | )
29 | if (downloadStatus.isError)
30 | return downloadStatus
31 |
32 | return DownloadStatus.Success
33 | }
34 |
35 | override suspend fun downloadRoot(
36 | appVersions: List?,
37 | onProgress: (Float) -> Unit,
38 | onFile: (String) -> Unit
39 | ): DownloadStatus {
40 | throw IllegalAccessException("Vanced microG does not have a root downloader")
41 | }
42 |
43 | override fun getSavedFilePath(): String {
44 | val directory = File(getMicrogPath(context))
45 |
46 | if (!directory.exists())
47 | directory.mkdirs()
48 |
49 | return directory.path
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/impl/MusicDownloader.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.impl
2 |
3 | import android.content.Context
4 | import com.vanced.manager.downloader.api.MusicAPI
5 | import com.vanced.manager.downloader.base.AppDownloader
6 | import com.vanced.manager.downloader.util.getVancedYoutubeMusicPath
7 | import com.vanced.manager.preferences.holder.managerVariantPref
8 | import com.vanced.manager.preferences.holder.musicVersionPref
9 | import com.vanced.manager.util.getLatestOrProvidedAppVersion
10 | import java.io.File
11 |
12 | class MusicDownloader(
13 | private val musicAPI: MusicAPI,
14 | private val context: Context,
15 | ) : AppDownloader() {
16 |
17 | private lateinit var absoluteVersion: String
18 |
19 | override suspend fun download(
20 | appVersions: List?,
21 | onProgress: (Float) -> Unit,
22 | onFile: (String) -> Unit
23 | ): DownloadStatus {
24 | absoluteVersion = getLatestOrProvidedAppVersion(musicVersionPref, appVersions)
25 |
26 | val downloadStatus = downloadFiles(
27 | files = arrayOf(
28 | DownloadFile(
29 | call = musicAPI.getFiles(
30 | version = absoluteVersion,
31 | variant = managerVariantPref,
32 | ),
33 | fileName = "music.apk"
34 | )
35 | ),
36 | onProgress = onProgress,
37 | onFile = onFile
38 | )
39 | if (downloadStatus.isError)
40 | return downloadStatus
41 |
42 | return DownloadStatus.Success
43 | }
44 |
45 | override suspend fun downloadRoot(
46 | appVersions: List?,
47 | onProgress: (Float) -> Unit,
48 | onFile: (String) -> Unit
49 | ): DownloadStatus {
50 | return DownloadStatus.Success
51 | }
52 |
53 | override fun getSavedFilePath(): String {
54 | val directory =
55 | File(getVancedYoutubeMusicPath(absoluteVersion, managerVariantPref, context))
56 |
57 | if (!directory.exists())
58 | directory.mkdirs()
59 |
60 | return directory.path
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/impl/VancedDownloader.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.impl
2 |
3 | import android.content.Context
4 | import com.vanced.manager.downloader.api.VancedAPI
5 | import com.vanced.manager.downloader.base.AppDownloader
6 | import com.vanced.manager.downloader.util.getVancedYoutubePath
7 | import com.vanced.manager.preferences.holder.managerVariantPref
8 | import com.vanced.manager.preferences.holder.vancedLanguagesPref
9 | import com.vanced.manager.preferences.holder.vancedThemePref
10 | import com.vanced.manager.preferences.holder.vancedVersionPref
11 | import com.vanced.manager.util.arch
12 | import com.vanced.manager.util.getLatestOrProvidedAppVersion
13 | import java.io.File
14 |
15 | class VancedDownloader(
16 | private val vancedAPI: VancedAPI,
17 | private val context: Context,
18 | ) : AppDownloader() {
19 |
20 | private lateinit var absoluteVersion: String
21 |
22 | override suspend fun download(
23 | appVersions: List?,
24 | onProgress: (Float) -> Unit,
25 | onFile: (String) -> Unit
26 | ): DownloadStatus {
27 | absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
28 |
29 | val files = arrayOf(
30 | getFile(
31 | type = "Theme",
32 | apkName = "$vancedThemePref.apk",
33 | ),
34 | getFile(
35 | type = "Arch",
36 | apkName = "split_config.$arch.apk",
37 | )
38 | ) + vancedLanguagesPref.map { language ->
39 | getFile(
40 | type = "Language",
41 | apkName = "split_config.$language.apk",
42 | )
43 | }
44 |
45 | val downloadStatus = downloadFiles(
46 | files = files,
47 | onProgress = onProgress,
48 | onFile = onFile,
49 | )
50 | if (downloadStatus.isError)
51 | return downloadStatus
52 |
53 | return DownloadStatus.Success
54 | }
55 |
56 | override suspend fun downloadRoot(
57 | appVersions: List?,
58 | onProgress: (Float) -> Unit,
59 | onFile: (String) -> Unit
60 | ): DownloadStatus {
61 | return DownloadStatus.Success
62 | }
63 |
64 | override fun getSavedFilePath(): String {
65 | val directory = File(getVancedYoutubePath(absoluteVersion, managerVariantPref, context))
66 |
67 | if (!directory.exists())
68 | directory.mkdirs()
69 |
70 | return directory.path
71 | }
72 |
73 | private fun getFile(
74 | type: String,
75 | apkName: String,
76 | ) = DownloadFile(
77 | call = vancedAPI.getFiles(
78 | version = absoluteVersion,
79 | variant = managerVariantPref,
80 | type = type,
81 | apkName = apkName
82 | ),
83 | fileName = apkName
84 | )
85 |
86 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/downloader/util/DownloadPath.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.downloader.util
2 |
3 | import android.content.Context
4 |
5 | fun getVancedYoutubePath(
6 | version: String,
7 | variant: String,
8 | context: Context
9 | ) = context.getExternalFilesDirPath("vanced_youtube") + "/$version/$variant"
10 |
11 | fun getVancedYoutubeMusicPath(
12 | version: String,
13 | variant: String,
14 | context: Context
15 | ) = context.getExternalFilesDirPath("vanced_music") + "/$version/$variant"
16 |
17 | fun getMicrogPath(
18 | context: Context
19 | ) = context.getExternalFilesDirPath("microg")
20 |
21 | fun getStockYoutubePath(
22 | version: String,
23 | context: Context
24 | ) = context.getExternalFilesDirPath("stock_youtube") + "/$version"
25 |
26 | fun getStockYoutubeMusicPath(
27 | version: String,
28 | context: Context
29 | ) = context.getExternalFilesDirPath("stock_youtube_music") + "/$version"
30 |
31 | private fun Context.getExternalFilesDirPath(
32 | type: String
33 | ): String {
34 | val filesDir = getExternalFilesDir(type)!! //fuck null safety, amirite?
35 | if (!filesDir.exists())
36 | filesDir.mkdirs()
37 |
38 | return filesDir.path
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/base/AppInstaller.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.base
2 |
3 | import com.vanced.manager.repository.manager.PackageManagerResult
4 |
5 | abstract class AppInstaller {
6 |
7 | abstract suspend fun install(appVersions: List?)
8 |
9 | abstract suspend fun installRoot(appVersions: List?): PackageManagerResult
10 |
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/impl/MicrogInstaller.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.impl
2 |
3 | import android.content.Context
4 | import com.vanced.manager.downloader.util.getMicrogPath
5 | import com.vanced.manager.installer.base.AppInstaller
6 | import com.vanced.manager.repository.manager.NonrootPackageManager
7 | import com.vanced.manager.repository.manager.PackageManagerResult
8 | import java.io.File
9 |
10 | class MicrogInstaller(
11 | private val context: Context,
12 | private val nonrootPackageManager: NonrootPackageManager,
13 | ) : AppInstaller() {
14 |
15 | override suspend fun install(appVersions: List?) {
16 | val musicApk = File(getMicrogPath(context), "microg.apk")
17 |
18 | nonrootPackageManager.installApp(musicApk)
19 | }
20 |
21 | override suspend fun installRoot(appVersions: List?): PackageManagerResult {
22 | throw IllegalAccessException("Vanced microG does not have a root installer")
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/impl/MusicInstaller.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.impl
2 |
3 | import android.content.Context
4 | import com.vanced.manager.domain.model.AppData
5 | import com.vanced.manager.downloader.util.getStockYoutubeMusicPath
6 | import com.vanced.manager.downloader.util.getVancedYoutubeMusicPath
7 | import com.vanced.manager.installer.base.AppInstaller
8 | import com.vanced.manager.installer.util.RootPatchHelper
9 | import com.vanced.manager.preferences.holder.managerVariantPref
10 | import com.vanced.manager.preferences.holder.musicVersionPref
11 | import com.vanced.manager.preferences.holder.vancedVersionPref
12 | import com.vanced.manager.repository.manager.NonrootPackageManager
13 | import com.vanced.manager.repository.manager.PackageManagerResult
14 | import com.vanced.manager.repository.manager.RootPackageManager
15 | import com.vanced.manager.util.getLatestOrProvidedAppVersion
16 | import java.io.File
17 |
18 | class MusicInstaller(
19 | private val context: Context,
20 | private val rootPackageManager: RootPackageManager,
21 | private val nonrootPackageManager: NonrootPackageManager,
22 | ) : AppInstaller() {
23 |
24 | override suspend fun install(appVersions: List?) {
25 | val absoluteVersion = getLatestOrProvidedAppVersion(musicVersionPref, appVersions)
26 |
27 | val musicApk = File(
28 | getVancedYoutubeMusicPath(
29 | absoluteVersion,
30 | managerVariantPref,
31 | context
32 | ) + "/music.apk"
33 | )
34 |
35 | nonrootPackageManager.installApp(musicApk)
36 | }
37 |
38 | override suspend fun installRoot(appVersions: List?): PackageManagerResult {
39 | val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
40 |
41 | val stock = File(getStockYoutubeMusicPath(absoluteVersion, context), "base.apk")
42 | val vanced = File(getVancedYoutubeMusicPath(absoluteVersion, "root", context), "base.apk")
43 |
44 | val prepareStock = RootPatchHelper.prepareStock(
45 | stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC,
46 | stockVersion = absoluteVersion
47 | ) {
48 | rootPackageManager.installApp(stock)
49 | }
50 | if (prepareStock.isError)
51 | return prepareStock
52 |
53 | val patchStock = RootPatchHelper.patchStock(
54 | patchPath = vanced.absolutePath,
55 | stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC,
56 | app = APP_KEY
57 | )
58 | if (patchStock.isError)
59 | return patchStock
60 |
61 | return PackageManagerResult.Success(null)
62 | }
63 |
64 | companion object {
65 | const val APP_KEY = "youtube_music_vanced"
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/impl/VancedInstaller.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.impl
2 |
3 | import android.content.Context
4 | import com.vanced.manager.domain.model.AppData
5 | import com.vanced.manager.downloader.util.getStockYoutubePath
6 | import com.vanced.manager.downloader.util.getVancedYoutubePath
7 | import com.vanced.manager.installer.base.AppInstaller
8 | import com.vanced.manager.installer.util.RootPatchHelper
9 | import com.vanced.manager.preferences.holder.vancedVersionPref
10 | import com.vanced.manager.repository.manager.NonrootPackageManager
11 | import com.vanced.manager.repository.manager.PackageManagerResult
12 | import com.vanced.manager.repository.manager.RootPackageManager
13 | import com.vanced.manager.util.getLatestOrProvidedAppVersion
14 | import java.io.File
15 |
16 | class VancedInstaller(
17 | private val context: Context,
18 | private val rootPackageManager: RootPackageManager,
19 | private val nonrootPackageManager: NonrootPackageManager,
20 | ) : AppInstaller() {
21 |
22 | override suspend fun install(appVersions: List?) {
23 | val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
24 |
25 | val apks = File(getVancedYoutubePath(absoluteVersion, "nonroot", context))
26 | .listFiles()
27 |
28 | nonrootPackageManager.installSplitApp(apks!!)
29 | }
30 |
31 | override suspend fun installRoot(appVersions: List?): PackageManagerResult {
32 | val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
33 |
34 | val stockApks = File(getStockYoutubePath(absoluteVersion, context))
35 | .listFiles()
36 | val vancedBaseApk = getVancedYoutubePath(absoluteVersion, "root", context) + "/base.apk"
37 |
38 | val prepareStock = RootPatchHelper.prepareStock(
39 | stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE,
40 | stockVersion = absoluteVersion,
41 | ) {
42 | rootPackageManager.installSplitApp(stockApks!!)
43 | }
44 | if (prepareStock.isError)
45 | return prepareStock
46 |
47 | val patchStock = RootPatchHelper.patchStock(
48 | patchPath = vancedBaseApk,
49 | stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE,
50 | app = APP_KEY
51 | )
52 | if (patchStock.isError)
53 | return patchStock
54 |
55 | return PackageManagerResult.Success(null)
56 | }
57 |
58 | companion object {
59 | const val APP_KEY = "youtube_vanced"
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/service/AppInstallService.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.service
2 |
3 | import android.app.Service
4 | import android.content.Intent
5 | import android.content.pm.PackageInstaller
6 | import android.os.IBinder
7 |
8 | class AppInstallService : Service() {
9 |
10 | override fun onStartCommand(
11 | intent: Intent,
12 | flags: Int,
13 | startId: Int
14 | ): Int {
15 | val extraStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)
16 | val extraStatusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
17 | when (extraStatus) {
18 | PackageInstaller.STATUS_PENDING_USER_ACTION -> {
19 | startActivity(
20 | intent.getParcelableExtra(Intent.EXTRA_INTENT).apply {
21 | this?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
22 | }
23 | )
24 | }
25 | else -> {
26 | sendBroadcast(Intent().apply {
27 | action = APP_INSTALL_ACTION
28 | putExtra(EXTRA_INSTALL_STATUS, extraStatus)
29 | putExtra(EXTRA_INSTALL_STATUS_MESSAGE, extraStatusMessage)
30 | })
31 | }
32 | }
33 | stopSelf()
34 | return START_NOT_STICKY
35 | }
36 |
37 | override fun onBind(intent: Intent?): IBinder? = null
38 |
39 | companion object {
40 | const val APP_INSTALL_ACTION = "APP_INSTALL_ACTION"
41 |
42 | const val EXTRA_INSTALL_STATUS = "EXTRA_INSTALL_STATUS"
43 | const val EXTRA_INSTALL_STATUS_MESSAGE = "EXTRA_INSTALL_STATUS_MESSAGE"
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/service/AppUninstallService.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.service
2 |
3 | import android.app.Service
4 | import android.content.Intent
5 | import android.content.pm.PackageInstaller
6 | import android.os.IBinder
7 |
8 | class AppUninstallService : Service() {
9 |
10 | override fun onStartCommand(
11 | intent: Intent,
12 | flags: Int,
13 | startId: Int
14 | ): Int {
15 | when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
16 | PackageInstaller.STATUS_PENDING_USER_ACTION -> {
17 | startActivity(
18 | intent.getParcelableExtra(Intent.EXTRA_INTENT).apply {
19 | this?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
20 | }
21 | )
22 | }
23 | else -> {
24 | sendBroadcast(Intent().apply {
25 | action = APP_UNINSTALL_ACTION
26 | })
27 | }
28 | }
29 | stopSelf()
30 | return START_NOT_STICKY
31 | }
32 |
33 | override fun onBind(intent: Intent?): IBinder? = null
34 |
35 | companion object {
36 | const val APP_UNINSTALL_ACTION = "APP_UNINSTALL_ACTION"
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/util/PM.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.util
2 |
3 | import android.app.PendingIntent
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.pm.PackageInstaller
7 | import android.content.pm.PackageManager
8 | import android.os.Build
9 | import com.vanced.manager.installer.service.AppInstallService
10 | import com.vanced.manager.installer.service.AppUninstallService
11 | import java.io.File
12 |
13 | private const val byteArraySize = 1024 * 1024 // Because 1,048,576 is not readable
14 |
15 | object PM {
16 |
17 | fun installApp(apk: File, context: Context) {
18 | val packageInstaller = context.packageManager.packageInstaller
19 | val session =
20 | packageInstaller.openSession(packageInstaller.createSession(sessionParams))
21 | session.writeApk(apk)
22 | session.commit(context.installIntentSender)
23 | session.close()
24 | }
25 |
26 | fun installSplitApp(apks: Array, context: Context) {
27 | val packageInstaller = context.packageManager.packageInstaller
28 | val session =
29 | packageInstaller.openSession(packageInstaller.createSession(sessionParams))
30 | for (apk in apks) {
31 | session.writeApk(apk)
32 | }
33 | session.commit(context.installIntentSender)
34 | session.close()
35 | }
36 |
37 | fun uninstallPackage(pkg: String, context: Context) {
38 | val packageInstaller = context.packageManager.packageInstaller
39 | packageInstaller.uninstall(pkg, context.uninstallIntentSender)
40 | }
41 | }
42 |
43 | private fun PackageInstaller.Session.writeApk(apk: File) {
44 | apk.inputStream().use { inputStream ->
45 | openWrite(apk.name, 0, apk.length()).use { outputStream ->
46 | inputStream.copyTo(outputStream, byteArraySize)
47 | fsync(outputStream)
48 | }
49 | }
50 | }
51 |
52 | private val intentFlags
53 | get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
54 | PendingIntent.FLAG_MUTABLE
55 | else
56 | 0
57 |
58 | private val sessionParams
59 | get() = PackageInstaller.SessionParams(
60 | PackageInstaller.SessionParams.MODE_FULL_INSTALL
61 | ).apply {
62 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
63 | setInstallReason(PackageManager.INSTALL_REASON_USER)
64 | }
65 | }
66 |
67 | private val Context.installIntentSender
68 | get() = PendingIntent.getService(
69 | this,
70 | 0,
71 | Intent(this, AppInstallService::class.java),
72 | intentFlags
73 | ).intentSender
74 |
75 | private val Context.uninstallIntentSender
76 | get() = PendingIntent.getService(
77 | this,
78 | 0,
79 | Intent(this, AppUninstallService::class.java),
80 | intentFlags
81 | ).intentSender
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/util/PMRootResult.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.util
2 |
3 | enum class PMRootStatus {
4 | ACTION_FAILED_SET_INSTALLER,
5 | ACTION_FAILED_GET_PACKAGE_DIR,
6 | ACTION_FAILED_GET_PACKAGE_VERSION_NAME,
7 | ACTION_FAILED_GET_PACKAGE_VERSION_CODE,
8 | ACTION_FAILED_FORCE_STOP_APP,
9 |
10 | INSTALL_SUCCESSFUL,
11 | INSTALL_FAILED_ABORTED,
12 | INSTALL_FAILED_ALREADY_EXISTS,
13 | INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
14 | INSTALL_FAILED_INSUFFICIENT_STORAGE,
15 | INSTALL_FAILED_INVALID_APK,
16 | INSTALL_FAILED_VERSION_DOWNGRADE,
17 | INSTALL_FAILED_PARSE_NO_CERTIFICATES,
18 | INSTALL_FAILED_UNKNOWN,
19 |
20 | LINK_FAILED_UNMOUNT,
21 | LINK_FAILED_MOUNT,
22 |
23 | PATCH_FAILED_COPY,
24 | PATCH_FAILED_CHMOD,
25 | PATCH_FAILED_CHOWN,
26 | PATCH_FAILED_CHCON,
27 | PATCH_FAILED_DESTROY,
28 |
29 | SESSION_FAILED_CREATE,
30 | SESSION_FAILED_WRITE,
31 | SESSION_FAILED_COPY,
32 | SESSION_INVALID_ID,
33 |
34 | SCRIPT_FAILED_SETUP_POST_FS,
35 | SCRIPT_FAILED_SETUP_SERVICE_D,
36 | SCRIPT_FAILED_DESTROY_POST_FS,
37 | SCRIPT_FAILED_DESTROY_SERVICE_D,
38 |
39 | UNINSTALL_SUCCESSFUL,
40 | UNINSTALL_FAILED,
41 | }
42 |
43 | sealed class PMRootResult {
44 | data class Success(val value: V? = null) : PMRootResult()
45 | data class Error(val error: PMRootStatus, val message: String) : PMRootResult()
46 |
47 | val isError
48 | get() = this is Error
49 |
50 | val isSuccess
51 | get() = this is Success
52 | }
53 |
54 | inline fun PMRootResult.getOrElse(onError: (PMRootResult.Error) -> R): R? {
55 | return when (this) {
56 | is PMRootResult.Error -> onError(this)
57 | is PMRootResult.Success -> return this.value
58 | }
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/installer/util/RootPatchHelper.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.installer.util
2 |
3 | import com.vanced.manager.repository.manager.PackageManagerResult
4 | import com.vanced.manager.repository.manager.getOrElse
5 |
6 | object RootPatchHelper {
7 |
8 | fun cleanPatches(app: String): PackageManagerResult {
9 | val cleanOldPatches = Patcher.destroyOldPatch(app)
10 | if (cleanOldPatches.isError)
11 | return cleanOldPatches
12 |
13 | val cleanPatches = Patcher.destroyPatch(app)
14 | if (cleanOldPatches.isError)
15 | return cleanPatches
16 |
17 | return PackageManagerResult.Success(null)
18 | }
19 |
20 | inline fun prepareStock(
21 | stockPackage: String,
22 | stockVersion: String,
23 | install: () -> PackageManagerResult
24 | ): PackageManagerResult {
25 | val stockYoutubeVersion = PMRoot.getPackageVersionName(stockPackage)
26 | .getOrElse { null }
27 | if (stockYoutubeVersion != stockVersion) {
28 | val uninstallStock = PMRoot.uninstallApp(stockPackage)
29 | if (uninstallStock.isError)
30 | return uninstallStock
31 |
32 | val installStock = install()
33 | if (installStock.isError)
34 | return installStock
35 | }
36 |
37 | return PackageManagerResult.Success(null)
38 | }
39 |
40 | fun patchStock(
41 | patchPath: String,
42 | stockPackage: String,
43 | app: String
44 | ): PackageManagerResult {
45 | val movePatch = Patcher.movePatchToDataAdb(patchPath, app)
46 | if (movePatch.isError)
47 | return movePatch
48 |
49 | val chconPatch = Patcher.chconPatch(app)
50 | if (chconPatch.isError)
51 | return chconPatch
52 |
53 | val stockPackageDir = PMRoot.getPackageDir(stockPackage)
54 | .getOrElse { error -> return error }!!
55 |
56 | val setupScript = Patcher.setupScript(app, stockPackage, stockPackageDir)
57 | if (setupScript is PackageManagerResult.Error)
58 | return setupScript
59 |
60 | val linkPatch = Patcher.linkPatch(app, stockPackage, stockPackageDir)
61 | if (linkPatch is PackageManagerResult.Error)
62 | return linkPatch
63 |
64 | return PackageManagerResult.Success(null)
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/io/ManagerSuFile.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.io
2 |
3 | import com.topjohnwu.superuser.Shell
4 | import com.topjohnwu.superuser.io.SuFile
5 | import com.vanced.manager.util.errString
6 | import com.vanced.manager.util.outString
7 | import java.io.File
8 |
9 | class ManagerSuFile : SuFile {
10 |
11 | sealed class SuFileResult {
12 | data class Success(val output: String) : SuFileResult()
13 | data class Error(val error: String) : SuFileResult()
14 | }
15 |
16 | constructor(pathName: String) : super(pathName)
17 | constructor(parent: String, child: String) : super(parent, child)
18 | constructor(parent: File, child: String) : super(parent, child)
19 |
20 | private fun cmd(input: String): SuFileResult {
21 | val cmd = Shell.su(input.replace("@@", escapedPath)).exec()
22 | if (!cmd.isSuccess)
23 | return SuFileResult.Error(cmd.errString)
24 |
25 | return SuFileResult.Success(cmd.outString)
26 | }
27 |
28 | override fun delete(): Boolean {
29 | val result = cmd("rm -f @@ || rmdir -f @@")
30 | if (result is SuFileResult.Error)
31 | throw SUIOException(result.error)
32 |
33 | return true
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/io/SUIOException.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.io
2 |
3 | import java.io.IOException
4 |
5 | class SUIOException : IOException {
6 | constructor(message: String) : super(message)
7 | constructor(message: String, cause: Throwable) : super(message, cause)
8 | constructor(cause: Throwable) : super(cause)
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/network/GithubService.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.network
2 |
3 | import com.vanced.manager.network.dto.GithubReleaseDto
4 | import retrofit2.http.GET
5 |
6 | private const val REPOS_VANCED = "repos/YTVanced"
7 |
8 | interface GithubService {
9 |
10 | @GET("$REPOS_VANCED/Vanced/releases/latest")
11 | suspend fun getVancedYoutubeRelease(): GithubReleaseDto
12 |
13 | @GET("$REPOS_VANCED/VancedMusic/releases/latest")
14 | suspend fun getVancedYoutubeMusicRelease(): GithubReleaseDto
15 |
16 | @GET("$REPOS_VANCED/VancedMicrog/releases/latest")
17 | suspend fun getVancedMicrogRelease(): GithubReleaseDto
18 |
19 | @GET("$REPOS_VANCED/VancedManager/releases/latest")
20 | suspend fun getVancedManagerRelease(): GithubReleaseDto
21 |
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/network/dto/GithubDto.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.network.dto
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class GithubReleaseDto(
8 | @SerialName("tag_name")
9 | val tagName: String,
10 |
11 | @SerialName("body")
12 | val body: String,
13 |
14 | @SerialName("assets")
15 | val assets: List
16 | )
17 |
18 | @Serializable
19 | data class GithubReleaseAssetDto(
20 | @SerialName("name")
21 | val name: String,
22 |
23 | @SerialName("browser_download_url")
24 | val browserDownloadUrl: String
25 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/network/util/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.network.util
2 |
3 | const val BASE = "https://api.vancedapp.com/api/v1/"
4 |
5 | const val GITHUB_API_BASE = "https://api.github.com/"
6 |
7 | const val VANCED_NAME = "YouTube Vanced"
8 | const val MUSIC_NAME = "YouTube Vanced Music"
9 | const val MICROG_NAME = "Vanced microG"
10 |
11 | const val URL_SPONSOR_BRAVE = "https://vancedapp.com/brave"
12 | const val URL_SPONSOR_ADGUARD = "https://adguard.com/?aid=31141&source=manager"
13 |
14 | const val URL_MEDIA_INSTAGRAM = "https://instagram.com/vanced.youtube"
15 | const val URL_MEDIA_YOUTUBE = "https://youtube.com/c/YouTubeVanced"
16 | const val URL_MEDIA_GITHUB = "https://github.com/YTVanced/VancedManager"
17 | const val URL_MEDIA_WEBSITE = "https://vancedapp.com"
18 | const val URL_MEDIA_TELEGRAM = "https://t.me/joinchat/AAAAAEHf-pi4jH1SDlAL4w"
19 | const val URL_MEDIA_TWITTER = "https://twitter.com/YTVanced"
20 | const val URL_MEDIA_DISCORD = "https://discord.gg/WCGNdRruzb"
21 | const val URL_MEDIA_REDDIT = "https://www.reddit.com/r/Vanced/"
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/preferences/ManagerPreference.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.preferences
2 |
3 | import android.content.SharedPreferences
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.mutableStateOf
6 | import androidx.compose.runtime.setValue
7 | import androidx.core.content.edit
8 | import org.koin.core.component.KoinComponent
9 | import org.koin.core.component.inject
10 | import kotlin.reflect.KProperty
11 |
12 | fun managerStringPreference(
13 | key: String,
14 | defaultValue: String = ""
15 | ) = ManagerPreference(
16 | key = key,
17 | defaultValue = defaultValue,
18 | getter = SharedPreferences::getString,
19 | setter = SharedPreferences.Editor::putString
20 | )
21 |
22 | fun managerStringSetPreference(
23 | key: String,
24 | defaultValue: Set = setOf()
25 | ) = ManagerPreference(
26 | key = key,
27 | defaultValue = defaultValue,
28 | getter = SharedPreferences::getStringSet,
29 | setter = SharedPreferences.Editor::putStringSet
30 | )
31 |
32 | fun managerBooleanPreference(
33 | key: String,
34 | defaultValue: Boolean = false
35 | ) = ManagerPreference(
36 | key = key,
37 | defaultValue = defaultValue,
38 | getter = SharedPreferences::getBoolean,
39 | setter = SharedPreferences.Editor::putBoolean
40 | )
41 |
42 | fun managerIntPreference(
43 | key: String,
44 | defaultValue: Int = 0
45 | ) = ManagerPreference(
46 | key = key,
47 | defaultValue = defaultValue,
48 | getter = SharedPreferences::getInt,
49 | setter = SharedPreferences.Editor::putInt
50 | )
51 |
52 | fun managerLongPreference(
53 | key: String,
54 | defaultValue: Long = 0
55 | ) = ManagerPreference(
56 | key = key,
57 | defaultValue = defaultValue,
58 | getter = SharedPreferences::getLong,
59 | setter = SharedPreferences.Editor::putLong
60 | )
61 |
62 | class ManagerPreference(
63 | private val key: String,
64 | private val defaultValue: T,
65 | private val getter: SharedPreferences.(key: String, defaultValue: T) -> T?,
66 | private val setter: SharedPreferences.Editor.(key: String, newValue: T) -> Unit
67 | ) : KoinComponent {
68 |
69 | private val sharedPreferences: SharedPreferences by inject()
70 |
71 | var value by mutableStateOf(sharedPreferences.getter(key, defaultValue) ?: defaultValue)
72 | private set
73 |
74 | operator fun getValue(
75 | thisRef: Any?,
76 | property: KProperty<*>
77 | ) = value
78 |
79 | operator fun setValue(
80 | thisRef: Any?,
81 | property: KProperty<*>,
82 | newValue: T
83 | ) {
84 | value = newValue
85 | sharedPreferences.edit {
86 | setter(key, newValue)
87 | }
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/preferences/holder/PreferenceDefaultValueHolder.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.preferences.holder
2 |
3 | const val MANAGER_VARIANT_DEFAULT_VALUE = "nonroot"
4 |
5 | const val MANAGER_THEME_DEFAULT_VALUE = "System Default"
6 |
7 | const val VANCED_THEME_DEFAULT_VALUE = "dark"
8 | val VANCED_LANGUAGE_DEFAULT_VALUE = setOf("en")
9 |
10 | const val APP_VERSION_DEFAULT_VALUE = "latest"
11 |
12 | const val APP_ENABLED_DEFAULT_VALUE = true
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/preferences/holder/PreferenceHolder.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.preferences.holder
2 |
3 | import com.vanced.manager.preferences.managerStringPreference
4 | import com.vanced.manager.preferences.managerStringSetPreference
5 |
6 | var managerVariantPref by managerStringPreference(
7 | MANAGER_VARIANT_KEY,
8 | MANAGER_VARIANT_DEFAULT_VALUE
9 | )
10 |
11 | var vancedThemePref by managerStringPreference(APP_VANCED_THEME_KEY, VANCED_THEME_DEFAULT_VALUE)
12 | var vancedVersionPref by managerStringPreference(APP_VANCED_VERSION_KEY, APP_VERSION_DEFAULT_VALUE)
13 | var vancedLanguagesPref by managerStringSetPreference(
14 | APP_VANCED_LANGUAGE_KEY,
15 | VANCED_LANGUAGE_DEFAULT_VALUE
16 | )
17 |
18 | var musicVersionPref by managerStringPreference(APP_MUSIC_VERSION_KEY, APP_VERSION_DEFAULT_VALUE)
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/preferences/holder/PreferenceKeyHolder.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.preferences.holder
2 |
3 | const val USE_CUSTOM_TABS_KEY = "use_custom_tabs"
4 | const val MANAGER_VARIANT_KEY = "manager_variant"
5 |
6 | const val MANAGER_THEME_KEY = "manager_theme"
7 | const val MANAGER_ACCENT_COLOR_KEY = "manager_accent_color"
8 |
9 | const val APP_VANCED_THEME_KEY = "app_vanced_theme"
10 | const val APP_VANCED_VERSION_KEY = "app_vanced_version"
11 | const val APP_VANCED_LANGUAGE_KEY = "app_vanced_language"
12 |
13 | const val APP_MUSIC_VERSION_KEY = "app_music_version"
14 |
15 | const val VANCED_ENABLED_KEY = "manager_vanced_enabled"
16 | const val MUSIC_ENABLED_KEY = "manager_music_enabled"
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/repository/PreferenceRepository.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.repository
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.runtime.Composable
5 | import com.vanced.manager.repository.source.PreferenceData
6 | import com.vanced.manager.repository.source.PreferenceDatasource
7 |
8 | interface PreferenceRepository {
9 |
10 | var managerUseCustomTabs: Boolean
11 | var managerMode: ManagerMode
12 | var managerTheme: ManagerTheme
13 |
14 | }
15 |
16 | class PreferenceRepositoryImpl(
17 | private val preferenceDatasource: PreferenceDatasource
18 | ) : PreferenceRepository {
19 |
20 | override var managerUseCustomTabs: Boolean
21 | get() = preferenceDatasource.managerUseCustomTabs
22 | set(value) {
23 | preferenceDatasource.managerUseCustomTabs = value
24 | }
25 |
26 | override var managerMode: ManagerMode
27 | get() = ManagerMode.fromValue(preferenceDatasource.managerMode)
28 | set(value) {
29 | preferenceDatasource.managerMode = value.value
30 | }
31 |
32 | override var managerTheme: ManagerTheme
33 | get() = ManagerTheme.fromValue(preferenceDatasource.managerTheme)
34 | set(value) {
35 | preferenceDatasource.managerTheme = value.value
36 | }
37 |
38 | }
39 |
40 |
41 | enum class ManagerTheme(val value: String) {
42 | LIGHT(PreferenceData.MANAGER_THEME_VALUE_LIGHT),
43 | DARK(PreferenceData.MANAGER_THEME_VALUE_DARK),
44 | SYSTEM_DEFAULT(PreferenceData.MANAGER_THEME_VALUE_SYSTEM_DEFAULT);
45 |
46 | @Composable
47 | fun isDark() = when (this) {
48 | LIGHT -> false
49 | DARK -> true
50 | SYSTEM_DEFAULT -> isSystemInDarkTheme()
51 | }
52 |
53 | companion object {
54 | fun fromValue(value: String?): ManagerTheme {
55 | return values().find {
56 | it.value == value
57 | } ?: SYSTEM_DEFAULT
58 | }
59 | }
60 | }
61 |
62 | enum class ManagerMode(val value: String) {
63 | ROOT(PreferenceData.MANAGER_MODE_VALUE_ROOT),
64 | NONROOT(PreferenceData.MANAGER_MODE_VALUE_NONROOT);
65 |
66 | val isRoot get() = this == ROOT
67 | val isNonroot get() = this == NONROOT
68 |
69 | companion object {
70 | fun fromValue(value: String?): ManagerMode {
71 | return when (value) {
72 | "root" -> ROOT
73 | else -> NONROOT
74 | }
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/repository/source/PreferenceDatasource.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.repository.source
2 |
3 | import android.content.SharedPreferences
4 |
5 | interface PreferenceDatasource {
6 |
7 | var managerUseCustomTabs: Boolean
8 | var managerMode: String
9 | var managerTheme: String
10 |
11 | }
12 |
13 | class PreferenceDatasourceImpl(
14 | private val sharedPreferences: SharedPreferences
15 | ) : PreferenceDatasource {
16 |
17 | override var managerUseCustomTabs: Boolean
18 | get() = getBoolean(
19 | PreferenceData.MANAGER_USE_CUSTOM_TABS_KEY,
20 | PreferenceData.MANAGER_USE_CUSTOM_TABS_DEFAULT_VALUE
21 | )
22 | set(value) {
23 | putBoolean(PreferenceData.MANAGER_USE_CUSTOM_TABS_KEY, value)
24 | }
25 |
26 | override var managerMode: String
27 | get() = getString(
28 | PreferenceData.MANAGER_MODE_KEY,
29 | PreferenceData.MANAGER_MODE_DEFAULT_VALUE
30 | )
31 | set(value) {
32 | putString(PreferenceData.MANAGER_MODE_KEY, value)
33 | }
34 |
35 | override var managerTheme: String
36 | get() = getString(
37 | PreferenceData.MANAGER_THEME_KEY,
38 | PreferenceData.MANAGER_THEME_DEFAULT_VALUE
39 | )
40 | set(value) {
41 | putString(PreferenceData.MANAGER_THEME_KEY, value)
42 | }
43 |
44 | private fun getString(key: String, defaultValue: String): String {
45 | return sharedPreferences.getString(key, defaultValue) ?: defaultValue
46 | }
47 |
48 | private fun getBoolean(key: String, defaultValue: Boolean): Boolean {
49 | return sharedPreferences.getBoolean(key, defaultValue)
50 | }
51 |
52 | private fun putString(key: String, value: String) {
53 | sharedPreferences.edit().putString(key, value).apply()
54 | }
55 |
56 | private fun putBoolean(key: String, value: Boolean) {
57 | sharedPreferences.edit().putBoolean(key, value).apply()
58 | }
59 | }
60 |
61 | object PreferenceData {
62 |
63 | const val MANAGER_USE_CUSTOM_TABS_KEY = "manager_behaviour_use_custom_tabs"
64 | const val MANAGER_USE_CUSTOM_TABS_DEFAULT_VALUE = true
65 |
66 | const val MANAGER_MODE_KEY = "manager_behaviour_mode"
67 | const val MANAGER_MODE_VALUE_ROOT = "root"
68 | const val MANAGER_MODE_VALUE_NONROOT = "nonroot"
69 | const val MANAGER_MODE_DEFAULT_VALUE = MANAGER_MODE_VALUE_NONROOT
70 |
71 | const val MANAGER_THEME_KEY = "manager_appearance_theme"
72 | const val MANAGER_THEME_VALUE_LIGHT = "light"
73 | const val MANAGER_THEME_VALUE_DARK = "dark"
74 | const val MANAGER_THEME_VALUE_SYSTEM_DEFAULT = "system_default"
75 | const val MANAGER_THEME_DEFAULT_VALUE = MANAGER_THEME_VALUE_SYSTEM_DEFAULT
76 |
77 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/SplashScreenActivity.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.activity.ComponentActivity
6 | import com.topjohnwu.superuser.BusyBoxInstaller
7 | import com.topjohnwu.superuser.Shell
8 | import com.vanced.manager.BuildConfig
9 |
10 | class SplashScreenActivity : ComponentActivity() {
11 |
12 | init {
13 | Shell.enableVerboseLogging = BuildConfig.DEBUG
14 | Shell.setDefaultBuilder(
15 | Shell.Builder
16 | .create()
17 | .setInitializers(BusyBoxInstaller::class.java)
18 | )
19 | }
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 |
24 | Shell.getShell {
25 | startActivity(
26 | Intent(this, MainActivity::class.java)
27 | )
28 | finish()
29 | }
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerButton.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.foundation.BorderStroke
4 | import androidx.compose.foundation.interaction.MutableInteractionSource
5 | import androidx.compose.foundation.layout.PaddingValues
6 | import androidx.compose.foundation.layout.RowScope
7 | import androidx.compose.foundation.shape.RoundedCornerShape
8 | import androidx.compose.material3.*
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.remember
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Shape
13 | import androidx.compose.ui.unit.dp
14 |
15 | @Composable
16 | fun ManagerButton(
17 | onClick: () -> Unit,
18 | modifier: Modifier = Modifier,
19 | enabled: Boolean = true,
20 | interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
21 | elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
22 | shape: Shape = RoundedCornerShape(20.0.dp),
23 | border: BorderStroke? = null,
24 | colors: ButtonColors = ButtonDefaults.buttonColors(),
25 | contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
26 | content: @Composable RowScope.() -> Unit
27 | ) {
28 | Button(
29 | onClick,
30 | modifier,
31 | enabled,
32 | interactionSource,
33 | elevation,
34 | shape,
35 | border,
36 | colors,
37 | contentPadding,
38 | content
39 | )
40 | }
41 |
42 | @Composable
43 | fun ManagerElevatedButton(
44 | onClick: () -> Unit,
45 | modifier: Modifier = Modifier,
46 | enabled: Boolean = true,
47 | interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
48 | elevation: ButtonElevation? = ButtonDefaults.elevatedButtonElevation(),
49 | shape: Shape = RoundedCornerShape(20.0.dp),
50 | border: BorderStroke? = null,
51 | colors: ButtonColors = ButtonDefaults.elevatedButtonColors(),
52 | contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
53 | content: @Composable RowScope.() -> Unit
54 | ) {
55 | ElevatedButton(
56 | onClick,
57 | modifier,
58 | enabled,
59 | interactionSource,
60 | elevation,
61 | shape,
62 | border,
63 | colors,
64 | contentPadding,
65 | content
66 | )
67 | }
68 |
69 | @Composable
70 | fun ManagerFilledTonalButton(
71 | onClick: () -> Unit,
72 | modifier: Modifier = Modifier,
73 | enabled: Boolean = true,
74 | interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
75 | elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
76 | shape: Shape = RoundedCornerShape(20.0.dp),
77 | border: BorderStroke? = null,
78 | colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
79 | contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
80 | content: @Composable RowScope.() -> Unit
81 | ) {
82 | FilledTonalButton(
83 | onClick,
84 | modifier,
85 | enabled,
86 | interactionSource,
87 | elevation,
88 | shape,
89 | border,
90 | colors,
91 | contentPadding,
92 | content
93 | )
94 | }
95 |
96 | @Composable
97 | fun ManagerOutlinedButton(
98 | onClick: () -> Unit,
99 | modifier: Modifier = Modifier,
100 | enabled: Boolean = true,
101 | interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
102 | elevation: ButtonElevation? = null,
103 | shape: Shape = RoundedCornerShape(20.0.dp),
104 | border: BorderStroke? = ButtonDefaults.outlinedButtonBorder,
105 | colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
106 | contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
107 | content: @Composable RowScope.() -> Unit
108 | ) {
109 | Button(
110 | onClick,
111 | modifier,
112 | enabled,
113 | interactionSource,
114 | elevation,
115 | shape,
116 | border,
117 | colors,
118 | contentPadding,
119 | content
120 | )
121 | }
122 |
123 | @Composable
124 | fun ManagerTextButton(
125 | onClick: () -> Unit,
126 | modifier: Modifier = Modifier,
127 | enabled: Boolean = true,
128 | interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
129 | elevation: ButtonElevation? = null,
130 | shape: Shape = RoundedCornerShape(20.0.dp),
131 | border: BorderStroke? = null,
132 | colors: ButtonColors = ButtonDefaults.textButtonColors(),
133 | contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
134 | content: @Composable RowScope.() -> Unit
135 | ) {
136 | TextButton(
137 | onClick,
138 | modifier,
139 | enabled,
140 | interactionSource,
141 | elevation,
142 | shape,
143 | border,
144 | colors,
145 | contentPadding,
146 | content
147 | )
148 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerCard.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.interaction.MutableInteractionSource
5 | import androidx.compose.foundation.layout.ColumnScope
6 | import androidx.compose.material3.*
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.runtime.remember
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.graphics.Shape
12 | import com.vanced.manager.ui.theme.MediumShape
13 | import com.vanced.manager.ui.util.animated
14 |
15 | @Composable
16 | fun ManagerCard(
17 | modifier: Modifier = Modifier,
18 | onClick: (() -> Unit)? = null,
19 | shape: Shape = MediumShape,
20 | containerColor: Color = MaterialTheme.colorScheme.surface,
21 | contentColor: Color = contentColorFor(containerColor),
22 | elevation: CardElevation = CardDefaults.cardElevation(),
23 | content: @Composable ColumnScope.() -> Unit,
24 | ) {
25 | if (onClick != null) {
26 | val interactionSource = remember { MutableInteractionSource() }
27 | Card(
28 | modifier = modifier
29 | .clickable(
30 | interactionSource = interactionSource,
31 | indication = null,
32 | onClick = onClick
33 | ),
34 | interactionSource = interactionSource,
35 | shape = shape,
36 | containerColor = containerColor.animated,
37 | contentColor = contentColor.animated,
38 | elevation = elevation,
39 | content = content
40 | )
41 | } else {
42 | Card(
43 | modifier = modifier,
44 | shape = shape,
45 | containerColor = containerColor.animated,
46 | contentColor = contentColor.animated,
47 | elevation = elevation,
48 | content = content
49 | )
50 | }
51 | }
52 |
53 | @Composable
54 | fun ManagerElevatedCard(
55 | modifier: Modifier = Modifier,
56 | onClick: (() -> Unit)? = null,
57 | shape: Shape = MediumShape,
58 | containerColor: Color = MaterialTheme.colorScheme.surface,
59 | contentColor: Color = contentColorFor(containerColor),
60 | elevation: CardElevation = CardDefaults.elevatedCardElevation(),
61 | content: @Composable ColumnScope.() -> Unit,
62 | ) {
63 | if (onClick != null) {
64 | val interactionSource = remember { MutableInteractionSource() }
65 | ElevatedCard(
66 | modifier = modifier
67 | .clickable(
68 | interactionSource = interactionSource,
69 | indication = null,
70 | onClick = onClick
71 | ),
72 | interactionSource = interactionSource,
73 | shape = shape,
74 | containerColor = containerColor.animated,
75 | contentColor = contentColor.animated,
76 | elevation = elevation,
77 | content = content
78 | )
79 | } else {
80 | ElevatedCard(
81 | modifier = modifier,
82 | shape = shape,
83 | containerColor = containerColor.animated,
84 | contentColor = contentColor.animated,
85 | elevation = elevation,
86 | content = content
87 | )
88 | }
89 | }
90 |
91 | @Composable
92 | fun ManagerOutlinedCard(
93 | modifier: Modifier = Modifier,
94 | onClick: (() -> Unit)? = null,
95 | shape: Shape = MediumShape,
96 | containerColor: Color = MaterialTheme.colorScheme.surface,
97 | contentColor: Color = contentColorFor(containerColor),
98 | elevation: CardElevation = CardDefaults.outlinedCardElevation(),
99 | content: @Composable ColumnScope.() -> Unit,
100 | ) {
101 | if (onClick != null) {
102 | val interactionSource = remember { MutableInteractionSource() }
103 | OutlinedCard(
104 | modifier = modifier
105 | .clickable(
106 | interactionSource = interactionSource,
107 | indication = null,
108 | onClick = onClick
109 | ),
110 | interactionSource = interactionSource,
111 | shape = shape,
112 | containerColor = containerColor.animated,
113 | contentColor = contentColor.animated,
114 | elevation = elevation,
115 | content = content
116 | )
117 | } else {
118 | OutlinedCard(
119 | modifier = modifier,
120 | shape = shape,
121 | containerColor = containerColor.animated,
122 | contentColor = contentColor.animated,
123 | elevation = elevation,
124 | content = content
125 | )
126 | }
127 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerDialog.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.material3.AlertDialog
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.text.style.TextAlign
7 | import androidx.compose.ui.unit.dp
8 | import com.vanced.manager.ui.theme.LargeShape
9 |
10 | @Composable
11 | fun ManagerDialog(
12 | title: String,
13 | onDismissRequest: () -> Unit,
14 | confirmButton: @Composable () -> Unit,
15 | modifier: Modifier = Modifier,
16 | dismissButton: @Composable (() -> Unit)? = null,
17 | icon: @Composable (() -> Unit)? = null,
18 | content: @Composable () -> Unit,
19 | ) {
20 | AlertDialog(
21 | modifier = modifier,
22 | title = {
23 | ManagerText(
24 | text = title,
25 | textAlign = TextAlign.Center
26 | )
27 | },
28 | text = content,
29 | onDismissRequest = onDismissRequest,
30 | confirmButton = confirmButton,
31 | dismissButton = dismissButton,
32 | icon = icon,
33 | shape = LargeShape,
34 | tonalElevation = 2.dp,
35 | )
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerDropdownMenu.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.animation.ExperimentalAnimationApi
4 | import androidx.compose.animation.core.MutableTransitionState
5 | import androidx.compose.animation.core.animateFloat
6 | import androidx.compose.animation.core.tween
7 | import androidx.compose.animation.core.updateTransition
8 | import androidx.compose.foundation.layout.Column
9 | import androidx.compose.foundation.layout.ColumnScope
10 | import androidx.compose.foundation.layout.IntrinsicSize
11 | import androidx.compose.foundation.layout.width
12 | import androidx.compose.material3.CardDefaults
13 | import androidx.compose.material3.DropdownMenuItem
14 | import androidx.compose.material3.OutlinedCard
15 | import androidx.compose.runtime.Composable
16 | import androidx.compose.runtime.getValue
17 | import androidx.compose.runtime.remember
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.draw.alpha
20 | import androidx.compose.ui.draw.clip
21 | import androidx.compose.ui.draw.scale
22 | import androidx.compose.ui.platform.LocalDensity
23 | import androidx.compose.ui.unit.*
24 | import androidx.compose.ui.window.Popup
25 | import androidx.compose.ui.window.PopupPositionProvider
26 | import androidx.compose.ui.window.PopupProperties
27 | import com.vanced.manager.ui.theme.SmallShape
28 |
29 | private const val TransitionDuration = 200
30 |
31 | @ExperimentalAnimationApi
32 | @Composable
33 | fun ManagerDropdownMenu(
34 | expanded: Boolean,
35 | onDismissRequest: () -> Unit,
36 | content: @Composable ColumnScope.() -> Unit,
37 | ) {
38 | val expandedStates = remember { MutableTransitionState(false) }
39 | expandedStates.targetState = expanded
40 |
41 | val transition = updateTransition(expandedStates, "ManagerDropDownMenu")
42 |
43 | val alphaAndScale by transition.animateFloat(
44 | transitionSpec = { tween(durationMillis = TransitionDuration) },
45 | label = "AlphaAndScale"
46 | ) {
47 | if (it) 1f else 0f
48 | }
49 |
50 | if (expandedStates.currentState || expandedStates.targetState) {
51 | val density = LocalDensity.current
52 | val popupPositionProvider = ManagerDropdownMenuPopupPositionProvider(density)
53 |
54 | Popup(
55 | popupPositionProvider = popupPositionProvider,
56 | onDismissRequest = onDismissRequest,
57 | properties = PopupProperties(focusable = true)
58 | ) {
59 | OutlinedCard(
60 | modifier = Modifier
61 | .width(IntrinsicSize.Max)
62 | .alpha(alphaAndScale)
63 | .scale(alphaAndScale),
64 | elevation = CardDefaults.elevatedCardElevation()
65 | ) {
66 | Column(content = content)
67 | }
68 | }
69 | }
70 | }
71 |
72 | @Composable
73 | fun ManagerDropdownMenuItem(
74 | title: String,
75 | onClick: () -> Unit
76 | ) {
77 | DropdownMenuItem(
78 | onClick = onClick,
79 | modifier = Modifier.clip(SmallShape),
80 | text = {
81 | ManagerText(text = title)
82 | }
83 | )
84 | }
85 |
86 | //Kanged from Menu.kt
87 | private data class ManagerDropdownMenuPopupPositionProvider(
88 | val density: Density
89 | ) : PopupPositionProvider {
90 |
91 | override fun calculatePosition(
92 | anchorBounds: IntRect,
93 | windowSize: IntSize,
94 | layoutDirection: LayoutDirection,
95 | popupContentSize: IntSize
96 | ): IntOffset {
97 | val verticalMargin = with(density) { 48.dp.roundToPx() }
98 |
99 | //Compute horizontal position.
100 | val toRight = anchorBounds.left
101 | val toLeft = anchorBounds.right - popupContentSize.width
102 | val toDisplayRight = windowSize.width - popupContentSize.width
103 | val toDisplayLeft = 0
104 | val x = if (layoutDirection == LayoutDirection.Ltr) {
105 | sequenceOf(toRight, toLeft, toDisplayRight)
106 | } else {
107 | sequenceOf(toLeft, toRight, toDisplayLeft)
108 | }.firstOrNull {
109 | it >= 0 && it + popupContentSize.width <= windowSize.width
110 | } ?: toLeft
111 |
112 | // Compute vertical position.
113 | val toBottom = maxOf(anchorBounds.bottom, verticalMargin)
114 | val toTop = anchorBounds.top - popupContentSize.height
115 | val toCenter = anchorBounds.top - popupContentSize.height / 2
116 | val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin
117 | val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
118 | it >= verticalMargin && it + popupContentSize.height <= windowSize.height - verticalMargin
119 | } ?: toTop
120 |
121 | return IntOffset(x, y)
122 | }
123 |
124 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerLazyDsl.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.foundation.gestures.FlingBehavior
4 | import androidx.compose.foundation.gestures.ScrollableDefaults
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.PaddingValues
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.foundation.lazy.*
9 | import androidx.compose.material3.MaterialTheme
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Alignment
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.unit.dp
14 | import com.vanced.manager.ui.util.EdgeToEdgeContentPadding
15 |
16 | @Composable
17 | fun ManagerLazyColumn(
18 | modifier: Modifier = Modifier,
19 | state: LazyListState = rememberLazyListState(),
20 | contentPadding: PaddingValues = PaddingValues(
21 | start = EdgeToEdgeContentPadding,
22 | end = EdgeToEdgeContentPadding,
23 | bottom = 8.dp
24 | ),
25 | reverseLayout: Boolean = false,
26 | verticalArrangement: Arrangement.Vertical =
27 | Arrangement.spacedBy(8.dp, if (!reverseLayout) Alignment.Top else Alignment.Bottom),
28 | horizontalAlignment: Alignment.Horizontal = Alignment.Start,
29 | flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
30 | content: LazyListScope.() -> Unit
31 | ) {
32 | LazyColumn(
33 | modifier = modifier,
34 | state = state,
35 | contentPadding = contentPadding,
36 | reverseLayout = reverseLayout,
37 | verticalArrangement = verticalArrangement,
38 | horizontalAlignment = horizontalAlignment,
39 | flingBehavior = flingBehavior,
40 | content = content
41 | )
42 | }
43 |
44 | @Composable
45 | fun ManagerLazyRow(
46 | modifier: Modifier = Modifier,
47 | state: LazyListState = rememberLazyListState(),
48 | contentPadding: PaddingValues = PaddingValues(
49 | start = EdgeToEdgeContentPadding,
50 | end = EdgeToEdgeContentPadding,
51 | ),
52 | reverseLayout: Boolean = false,
53 | horizontalArrangement: Arrangement.Horizontal =
54 | Arrangement.spacedBy(8.dp, if (!reverseLayout) Alignment.Start else Alignment.End),
55 | verticalAlignment: Alignment.Vertical = Alignment.Top,
56 | flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
57 | content: LazyListScope.() -> Unit
58 | ) {
59 | LazyRow(
60 | modifier = modifier,
61 | state = state,
62 | contentPadding = contentPadding,
63 | reverseLayout = reverseLayout,
64 | horizontalArrangement = horizontalArrangement,
65 | verticalAlignment = verticalAlignment,
66 | flingBehavior = flingBehavior,
67 | content = content
68 | )
69 | }
70 |
71 | inline fun LazyListScope.managerCategory(
72 | crossinline categoryName: @Composable () -> String,
73 | content: LazyListScope.() -> Unit
74 | ) {
75 | item {
76 | ManagerText(
77 | modifier = Modifier
78 | .padding(
79 | start = 8.dp,
80 | top = 4.dp
81 | ),
82 | text = categoryName(),
83 | textStyle = MaterialTheme.typography.headlineSmall,
84 | )
85 | }
86 | content()
87 | }
88 |
89 | inline fun LazyListScope.items(
90 | items: Map,
91 | noinline key: ((key: K, value: V) -> Any)? = null,
92 | crossinline itemContent: @Composable LazyItemScope.(key: K, value: V) -> Unit
93 | ) = items(
94 | count = items.size,
95 | key = if (key != null) { index ->
96 | key(items.keys.elementAt(index), items.values.elementAt(index))
97 | } else null
98 | ) { index ->
99 | itemContent(items.keys.elementAt(index), items.values.elementAt(index))
100 | }
101 |
102 | inline fun LazyListScope.itemsIndexed(
103 | items: Map,
104 | noinline key: ((index: Int, key: K, value: V) -> Any)? = null,
105 | crossinline itemContent: @Composable LazyItemScope.(index: Int, key: K, value: V) -> Unit
106 | ) = items(
107 | count = items.size,
108 | key = if (key != null) { index ->
109 | key(index, items.keys.elementAt(index), items.values.elementAt(index))
110 | } else null
111 | ) { index ->
112 | itemContent(index, items.keys.elementAt(index), items.values.elementAt(index))
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerListItem.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.material.ContentAlpha
5 | import androidx.compose.material.LocalContentAlpha
6 | import androidx.compose.material3.LocalTextStyle
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.CompositionLocalProvider
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.unit.dp
13 | import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
14 | import com.vanced.manager.ui.util.DefaultContentPaddingVertical
15 |
16 | @Composable
17 | fun ManagerListItem(
18 | modifier: Modifier = Modifier,
19 | title: @Composable () -> Unit,
20 | description: @Composable (() -> Unit)? = null,
21 | icon: @Composable (() -> Unit)? = null,
22 | trailing: @Composable (() -> Unit)? = null
23 | ) {
24 | Row(
25 | modifier = modifier,
26 | horizontalArrangement = Arrangement.spacedBy(DefaultContentPaddingHorizontal)
27 | ) {
28 | if (icon != null) {
29 | Box(
30 | modifier = Modifier.align(Alignment.CenterVertically)
31 | ) {
32 | icon()
33 | }
34 | }
35 | Column(
36 | modifier = Modifier
37 | .weight(1f)
38 | .padding(
39 | vertical =
40 | if (description != null) DefaultContentPaddingVertical - 4.dp else DefaultContentPaddingVertical,
41 | )
42 | .align(Alignment.CenterVertically)
43 | ) {
44 | CompositionLocalProvider(
45 | LocalTextStyle provides MaterialTheme.typography.titleSmall
46 | ) {
47 | title()
48 | }
49 | if (description != null) {
50 | CompositionLocalProvider(
51 | LocalContentAlpha provides ContentAlpha.medium,
52 | LocalTextStyle provides MaterialTheme.typography.bodySmall
53 | ) {
54 | description()
55 | }
56 | }
57 | }
58 | if (trailing != null) {
59 | Box(
60 | modifier = Modifier.align(Alignment.CenterVertically),
61 | contentAlignment = Alignment.Center,
62 | ) {
63 | trailing()
64 | }
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerNavigator.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.animation.AnimatedContent
4 | import androidx.compose.animation.AnimatedContentScope
5 | import androidx.compose.animation.with
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.runtime.mutableStateListOf
8 | import androidx.compose.runtime.remember
9 | import androidx.compose.runtime.saveable.rememberSaveableStateHolder
10 | import androidx.compose.runtime.snapshots.SnapshotStateList
11 | import com.vanced.manager.ui.util.Screen
12 |
13 | @Composable
14 | fun rememberManagerNavigationController(
15 | initialScreen: T
16 | ) = remember {
17 | ManagerNavigationControllerImpl(initialScreen)
18 | }
19 |
20 | interface ManagerNavigationController {
21 |
22 | val screens: SnapshotStateList
23 |
24 | fun push(item: T)
25 |
26 | fun pop(): Boolean
27 |
28 | }
29 |
30 | class ManagerNavigationControllerImpl(
31 | initialScreen: T
32 | ) : ManagerNavigationController {
33 |
34 | override val screens: SnapshotStateList =
35 | mutableStateListOf(initialScreen)
36 |
37 | override fun push(item: T) {
38 | screens.add(item)
39 | }
40 |
41 | override fun pop(): Boolean {
42 | if (screens.size > 1) {
43 | screens.removeLast()
44 | return true
45 | }
46 | return false
47 | }
48 | }
49 |
50 | @Composable
51 | fun ManagerNavigator(
52 | navigationController: ManagerNavigationController,
53 | content: @Composable (targetContent: T) -> Unit
54 | ) {
55 | val saveableStateHolder = rememberSaveableStateHolder()
56 | val screens = remember { navigationController.screens }
57 |
58 | //TODO Animation is not working for some weird reasons
59 | AnimatedContent(
60 | transitionSpec = {
61 | if (targetState.size > initialState.size) {
62 | slideIntoContainer(AnimatedContentScope.SlideDirection.Start) with
63 | slideOutOfContainer(AnimatedContentScope.SlideDirection.End)
64 | } else {
65 | slideIntoContainer(AnimatedContentScope.SlideDirection.End) with
66 | slideOutOfContainer(AnimatedContentScope.SlideDirection.Start)
67 | }
68 | },
69 | targetState = screens
70 | ) { targetContents ->
71 | val targetContent = targetContents.last()
72 | saveableStateHolder.SaveableStateProvider(key = targetContent.route) {
73 | content(targetContent)
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerProgressIndicator.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.animation.core.animateFloatAsState
4 | import androidx.compose.material.ProgressIndicatorDefaults
5 | import androidx.compose.material3.LinearProgressIndicator
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.runtime.getValue
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import com.vanced.manager.ui.util.animated
12 |
13 | @Composable
14 | fun ManagerProgressIndicator(
15 | modifier: Modifier = Modifier,
16 | color: Color = MaterialTheme.colorScheme.primary,
17 | trackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
18 | ) {
19 | LinearProgressIndicator(
20 | modifier = modifier,
21 | color = color.animated,
22 | trackColor = trackColor.animated
23 | )
24 | }
25 |
26 | @Composable
27 | fun ManagerProgressIndicator(
28 | progress: Float,
29 | modifier: Modifier = Modifier,
30 | color: Color = MaterialTheme.colorScheme.primary,
31 | trackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
32 | ) {
33 | val animatedProgress by animateFloatAsState(
34 | targetValue = progress,
35 | animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
36 | )
37 | LinearProgressIndicator(
38 | progress = animatedProgress,
39 | modifier = modifier,
40 | color = color.animated,
41 | trackColor = trackColor.animated
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerScaffold.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.material3.FabPosition
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.Scaffold
7 | import androidx.compose.material3.contentColorFor
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import com.vanced.manager.ui.util.animated
12 |
13 | @Composable
14 | fun ManagerScaffold(
15 | modifier: Modifier = Modifier,
16 | topBar: @Composable () -> Unit = {},
17 | bottomBar: @Composable () -> Unit = {},
18 | snackbarHost: @Composable () -> Unit = {},
19 | floatingActionButton: @Composable () -> Unit = {},
20 | floatingActionButtonPosition: FabPosition = FabPosition.End,
21 | containerColor: Color = MaterialTheme.colorScheme.background,
22 | contentColor: Color = contentColorFor(containerColor),
23 | content: @Composable (PaddingValues) -> Unit
24 | ) {
25 | // //M3 Scaffold doesn't support tonal elevation for Surface
26 | // val absoluteTonalElevation = LocalAbsoluteTonalElevation.current + 1.dp
27 | // CompositionLocalProvider(
28 | // LocalAbsoluteTonalElevation provides absoluteTonalElevation
29 | // ) {
30 | Scaffold(
31 | modifier = modifier,
32 | topBar = topBar,
33 | bottomBar = bottomBar,
34 | snackbarHost = snackbarHost,
35 | floatingActionButton = floatingActionButton,
36 | floatingActionButtonPosition = floatingActionButtonPosition,
37 | containerColor = containerColor.animated,
38 | contentColor = contentColor.animated,
39 | content = content
40 | )
41 | // }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerSwipeRefresh.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Alignment
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.unit.Dp
9 | import androidx.compose.ui.unit.dp
10 | import com.google.accompanist.swiperefresh.SwipeRefresh
11 | import com.google.accompanist.swiperefresh.SwipeRefreshIndicator
12 | import com.google.accompanist.swiperefresh.SwipeRefreshState
13 |
14 | @Composable
15 | fun ManagerSwipeRefresh(
16 | swipeRefreshState: SwipeRefreshState,
17 | onRefresh: () -> Unit,
18 | modifier: Modifier = Modifier,
19 | swipeEnabled: Boolean = true,
20 | refreshTriggerDistance: Dp = 80.dp,
21 | indicatorAlignment: Alignment = Alignment.TopCenter,
22 | indicatorPadding: PaddingValues = PaddingValues(0.dp),
23 | clipIndicatorToPadding: Boolean = true,
24 | content: @Composable () -> Unit,
25 | ) {
26 | SwipeRefresh(
27 | modifier = modifier,
28 | state = swipeRefreshState,
29 | onRefresh = onRefresh,
30 | indicator = { state, trigger ->
31 | SwipeRefreshIndicator(
32 | state = state,
33 | refreshTriggerDistance = trigger,
34 | scale = true,
35 | contentColor = MaterialTheme.colorScheme.primary,
36 | backgroundColor = MaterialTheme.colorScheme.surface
37 | )
38 | },
39 | swipeEnabled = swipeEnabled,
40 | refreshTriggerDistance = refreshTriggerDistance,
41 | indicatorAlignment = indicatorAlignment,
42 | indicatorPadding = indicatorPadding,
43 | clipIndicatorToPadding = clipIndicatorToPadding,
44 | content = content
45 | )
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerText.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.material3.LocalTextStyle
4 | import androidx.compose.material3.Text
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.text.AnnotatedString
9 | import androidx.compose.ui.text.TextStyle
10 | import androidx.compose.ui.text.style.TextAlign
11 |
12 | @Composable
13 | fun ManagerText(
14 | text: String,
15 | modifier: Modifier = Modifier,
16 | color: Color = Color.Unspecified,
17 | textStyle: TextStyle = LocalTextStyle.current,
18 | textAlign: TextAlign? = null,
19 | ) {
20 | Text(
21 | modifier = modifier,
22 | text = text,
23 | color = color,
24 | style = textStyle,
25 | textAlign = textAlign
26 | )
27 | }
28 |
29 | @Composable
30 | fun ManagerText(
31 | text: AnnotatedString,
32 | modifier: Modifier = Modifier,
33 | color: Color = Color.Unspecified,
34 | textStyle: TextStyle = LocalTextStyle.current,
35 | textAlign: TextAlign? = null,
36 | ) {
37 | Text(
38 | modifier = modifier,
39 | text = text,
40 | color = color,
41 | style = textStyle,
42 | textAlign = textAlign
43 | )
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/component/ManagerTopAppBar.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.component
2 |
3 | import androidx.compose.foundation.layout.RowScope
4 | import androidx.compose.material3.*
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 |
8 | @Composable
9 | fun ManagerSmallTopAppBar(
10 | title: @Composable () -> Unit,
11 | modifier: Modifier = Modifier,
12 | navigationIcon: @Composable () -> Unit = {},
13 | actions: @Composable RowScope.() -> Unit = {},
14 | colors: TopAppBarColors = TopAppBarDefaults.smallTopAppBarColors(),
15 | scrollBehavior: TopAppBarScrollBehavior? = null
16 | ) {
17 | SmallTopAppBar(
18 | modifier = modifier,
19 | title = title,
20 | actions = actions,
21 | navigationIcon = navigationIcon,
22 | colors = colors,
23 | scrollBehavior = scrollBehavior
24 | )
25 | }
26 |
27 | @Composable
28 | fun ManagerCenterAlignedTopAppBar(
29 | title: @Composable () -> Unit,
30 | modifier: Modifier = Modifier,
31 | navigationIcon: @Composable () -> Unit = {},
32 | actions: @Composable RowScope.() -> Unit = {},
33 | colors: TopAppBarColors = TopAppBarDefaults.smallTopAppBarColors(),
34 | scrollBehavior: TopAppBarScrollBehavior? = null
35 | ) {
36 | CenterAlignedTopAppBar(
37 | modifier = modifier,
38 | title = title,
39 | actions = actions,
40 | navigationIcon = navigationIcon,
41 | colors = colors,
42 | scrollBehavior = scrollBehavior
43 | )
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/resource/ManagerString.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.resource
2 |
3 | import androidx.annotation.StringRes
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.res.stringResource
6 | import com.vanced.manager.R
7 |
8 | @Composable
9 | fun managerString(
10 | @StringRes stringId: Int?
11 | ) = stringResource(id = stringId ?: R.string.dummy_placeholder_text)
12 |
13 | @Composable
14 | fun managerString(
15 | @StringRes stringId: Int?,
16 | vararg formatArgs: Any
17 | ) = stringResource(id = stringId ?: R.string.dummy_placeholder_text, *formatArgs)
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val primaryColor = Color(defAccentColor)
6 | val primaryColorVariant = primaryColor.copy(alpha = 0.25f)
7 |
8 | val darkSurface = Color.Black
9 | val darkOnSurface = Color(0xFFD5D5D5)
10 |
11 | val lightSurface = Color.White
12 | val lightOnSurface = Color.Black
13 |
14 | val md_theme_light_primary = Color(0xFF0054d9)
15 | val md_theme_light_onPrimary = Color(0xFFffffff)
16 | val md_theme_light_primaryContainer = Color(0xFFdae2ff)
17 | val md_theme_light_onPrimaryContainer = Color(0xFF00174a)
18 | val md_theme_light_secondary = Color(0xFFc00020)
19 | val md_theme_light_onSecondary = Color(0xFFffffff)
20 | val md_theme_light_secondaryContainer = Color(0xFFffdad6)
21 | val md_theme_light_onSecondaryContainer = Color(0xFF410005)
22 | val md_theme_light_tertiary = Color(0xFF943896)
23 | val md_theme_light_onTertiary = Color(0xFFffffff)
24 | val md_theme_light_tertiaryContainer = Color(0xFFffd6fa)
25 | val md_theme_light_onTertiaryContainer = Color(0xFF37003c)
26 | val md_theme_light_error = Color(0xFFba1b1b)
27 | val md_theme_light_errorContainer = Color(0xFFffdad4)
28 | val md_theme_light_onError = Color(0xFFffffff)
29 | val md_theme_light_onErrorContainer = Color(0xFF410001)
30 | val md_theme_light_background = Color(0xFFfefbff)
31 | val md_theme_light_onBackground = Color(0xFF1b1b1e)
32 | val md_theme_light_surface = Color(0xFFfefbff)
33 | val md_theme_light_onSurface = Color(0xFF1b1b1e)
34 | val md_theme_light_surfaceVariant = Color(0xFFe2e2ec)
35 | val md_theme_light_onSurfaceVariant = Color(0xFF44464e)
36 | val md_theme_light_outline = Color(0xFF75767f)
37 | val md_theme_light_inverseOnSurface = Color(0xFFf2f0f5)
38 | val md_theme_light_inverseSurface = Color(0xFF303033)
39 |
40 | val md_theme_dark_primary = Color(0xFFb1c5ff)
41 | val md_theme_dark_onPrimary = Color(0xFF002a77)
42 | val md_theme_dark_primaryContainer = Color(0xFF003ea6)
43 | val md_theme_dark_onPrimaryContainer = Color(0xFFdae2ff)
44 | val md_theme_dark_secondary = Color(0xFFffb3af)
45 | val md_theme_dark_onSecondary = Color(0xFF69000c)
46 | val md_theme_dark_secondaryContainer = Color(0xFF920016)
47 | val md_theme_dark_onSecondaryContainer = Color(0xFFffdad6)
48 | val md_theme_dark_tertiary = Color(0xFFffa9fc)
49 | val md_theme_dark_onTertiary = Color(0xFF5a0061)
50 | val md_theme_dark_tertiaryContainer = Color(0xFF781c7c)
51 | val md_theme_dark_onTertiaryContainer = Color(0xFFffd6fa)
52 | val md_theme_dark_error = Color(0xFFffb4a9)
53 | val md_theme_dark_errorContainer = Color(0xFF930006)
54 | val md_theme_dark_onError = Color(0xFF680003)
55 | val md_theme_dark_onErrorContainer = Color(0xFFffdad4)
56 | val md_theme_dark_background = Color(0xFF1b1b1e)
57 | val md_theme_dark_onBackground = Color(0xFFe3e1e6)
58 | val md_theme_dark_surface = Color(0xFF1b1b1e)
59 | val md_theme_dark_onSurface = Color(0xFFe3e1e6)
60 | val md_theme_dark_surfaceVariant = Color(0xFF44464e)
61 | val md_theme_dark_onSurfaceVariant = Color(0xFFc6c6d0)
62 | val md_theme_dark_outline = Color(0xFF8f909a)
63 | val md_theme_dark_inverseOnSurface = Color(0xFF1b1b1e)
64 | val md_theme_dark_inverseSurface = Color(0xFFe3e1e6)
65 |
66 | val seed = Color(0xFF2e73ff)
67 | val error = Color(0xFFba1b1b)
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.ui.unit.dp
5 |
6 | //TODO M3 doesn't support Shapes yet
7 | val SmallShape = RoundedCornerShape(8.dp)
8 | val MediumShape = RoundedCornerShape(12.dp)
9 | val LargeShape = RoundedCornerShape(16.dp)
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.theme
2 |
3 | import android.os.Build
4 | import androidx.compose.foundation.gestures.LocalOverScrollConfiguration
5 | import androidx.compose.foundation.gestures.OverScrollConfiguration
6 | import androidx.compose.foundation.isSystemInDarkTheme
7 | import androidx.compose.material3.*
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.CompositionLocalProvider
10 | import androidx.compose.ui.platform.LocalContext
11 |
12 | const val defAccentColor = 0xFF0477E1
13 |
14 | private val LightThemeColors = lightColorScheme(
15 | primary = md_theme_light_primary,
16 | onPrimary = md_theme_light_onPrimary,
17 | primaryContainer = md_theme_light_primaryContainer,
18 | onPrimaryContainer = md_theme_light_onPrimaryContainer,
19 | secondary = md_theme_light_secondary,
20 | onSecondary = md_theme_light_onSecondary,
21 | secondaryContainer = md_theme_light_secondaryContainer,
22 | onSecondaryContainer = md_theme_light_onSecondaryContainer,
23 | tertiary = md_theme_light_tertiary,
24 | onTertiary = md_theme_light_onTertiary,
25 | tertiaryContainer = md_theme_light_tertiaryContainer,
26 | onTertiaryContainer = md_theme_light_onTertiaryContainer,
27 | error = md_theme_light_error,
28 | errorContainer = md_theme_light_errorContainer,
29 | onError = md_theme_light_onError,
30 | onErrorContainer = md_theme_light_onErrorContainer,
31 | background = md_theme_light_background,
32 | onBackground = md_theme_light_onBackground,
33 | surface = md_theme_light_surface,
34 | onSurface = md_theme_light_onSurface,
35 | surfaceVariant = md_theme_light_surfaceVariant,
36 | onSurfaceVariant = md_theme_light_onSurfaceVariant,
37 | outline = md_theme_light_outline,
38 | inverseOnSurface = md_theme_light_inverseOnSurface,
39 | inverseSurface = md_theme_light_inverseSurface,
40 | )
41 | private val DarkThemeColors = darkColorScheme(
42 | primary = md_theme_dark_primary,
43 | onPrimary = md_theme_dark_onPrimary,
44 | primaryContainer = md_theme_dark_primaryContainer,
45 | onPrimaryContainer = md_theme_dark_onPrimaryContainer,
46 | secondary = md_theme_dark_secondary,
47 | onSecondary = md_theme_dark_onSecondary,
48 | secondaryContainer = md_theme_dark_secondaryContainer,
49 | onSecondaryContainer = md_theme_dark_onSecondaryContainer,
50 | tertiary = md_theme_dark_tertiary,
51 | onTertiary = md_theme_dark_onTertiary,
52 | tertiaryContainer = md_theme_dark_tertiaryContainer,
53 | onTertiaryContainer = md_theme_dark_onTertiaryContainer,
54 | error = md_theme_dark_error,
55 | errorContainer = md_theme_dark_errorContainer,
56 | onError = md_theme_dark_onError,
57 | onErrorContainer = md_theme_dark_onErrorContainer,
58 | background = md_theme_dark_background,
59 | onBackground = md_theme_dark_onBackground,
60 | surface = md_theme_dark_surface,
61 | onSurface = md_theme_dark_onSurface,
62 | surfaceVariant = md_theme_dark_surfaceVariant,
63 | onSurfaceVariant = md_theme_dark_onSurfaceVariant,
64 | outline = md_theme_dark_outline,
65 | inverseOnSurface = md_theme_dark_inverseOnSurface,
66 | inverseSurface = md_theme_dark_inverseSurface,
67 | )
68 |
69 | @Composable
70 | inline fun apiDependantColorScheme(
71 | dynamic: () -> ColorScheme,
72 | static: () -> ColorScheme
73 | ): ColorScheme {
74 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
75 | dynamic()
76 | } else {
77 | static()
78 | }
79 | }
80 |
81 | @Composable
82 | fun ManagerTheme(
83 | darkMode: Boolean = isSystemInDarkTheme(),
84 | content: @Composable () -> Unit
85 | ) {
86 | val context = LocalContext.current
87 | val colorScheme =
88 | if (darkMode) {
89 | apiDependantColorScheme(
90 | dynamic = { dynamicDarkColorScheme(context) },
91 | static = { DarkThemeColors }
92 | )
93 | } else {
94 | apiDependantColorScheme(
95 | dynamic = { dynamicLightColorScheme(context) },
96 | static = { LightThemeColors }
97 | )
98 | }
99 | MaterialTheme(
100 | colorScheme = colorScheme,
101 | typography = ManagerTypography,
102 | ) {
103 | CompositionLocalProvider(
104 | LocalOverScrollConfiguration provides OverScrollConfiguration(
105 | forceShowAlways = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
106 | )
107 | ) {
108 | content()
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.Font
6 | import androidx.compose.ui.text.font.FontFamily
7 | import androidx.compose.ui.text.font.FontWeight
8 | import androidx.compose.ui.unit.sp
9 | import com.vanced.manager.R
10 |
11 | private val light = Font(R.font.inter_light, FontWeight.Light)
12 | private val regular = Font(R.font.inter_regular, FontWeight.Normal)
13 | private val medium = Font(R.font.inter_medium, FontWeight.Medium)
14 | private val semibold = Font(R.font.inter_semibold, FontWeight.SemiBold)
15 | private val bold = Font(R.font.inter_bold, FontWeight.Bold)
16 |
17 | private val InterFontFamily = FontFamily(light, regular, medium, semibold, bold)
18 |
19 | val ManagerTypography = Typography(
20 | displayLarge = TextStyle(
21 | fontFamily = InterFontFamily,
22 | fontWeight = FontWeight.Normal,
23 | fontSize = 57.sp,
24 | lineHeight = 64.sp,
25 | letterSpacing = (-0.25).sp,
26 | ),
27 | displayMedium = TextStyle(
28 | fontFamily = InterFontFamily,
29 | fontWeight = FontWeight.Normal,
30 | fontSize = 45.sp,
31 | lineHeight = 52.sp,
32 | letterSpacing = 0.sp,
33 | ),
34 | displaySmall = TextStyle(
35 | fontFamily = InterFontFamily,
36 | fontWeight = FontWeight.Normal,
37 | fontSize = 36.sp,
38 | lineHeight = 44.sp,
39 | letterSpacing = 0.sp,
40 | ),
41 | headlineLarge = TextStyle(
42 | fontFamily = InterFontFamily,
43 | fontWeight = FontWeight.Bold,
44 | fontSize = 32.sp,
45 | lineHeight = 40.sp,
46 | letterSpacing = 0.sp,
47 | ),
48 | headlineMedium = TextStyle(
49 | fontFamily = InterFontFamily,
50 | fontWeight = FontWeight.Bold,
51 | fontSize = 28.sp,
52 | lineHeight = 36.sp,
53 | letterSpacing = 0.sp,
54 | ),
55 | headlineSmall = TextStyle(
56 | fontFamily = InterFontFamily,
57 | fontWeight = FontWeight.SemiBold,
58 | fontSize = 24.sp,
59 | lineHeight = 32.sp,
60 | letterSpacing = 0.sp,
61 | ),
62 | titleLarge = TextStyle(
63 | fontFamily = InterFontFamily,
64 | fontWeight = FontWeight.SemiBold,
65 | fontSize = 22.sp,
66 | lineHeight = 28.sp,
67 | letterSpacing = 0.sp,
68 | ),
69 | titleMedium = TextStyle(
70 | fontFamily = InterFontFamily,
71 | fontWeight = FontWeight.SemiBold,
72 | fontSize = 18.sp,
73 | lineHeight = 24.sp,
74 | letterSpacing = 0.1.sp,
75 | ),
76 | titleSmall = TextStyle(
77 | fontFamily = InterFontFamily,
78 | fontWeight = FontWeight.SemiBold,
79 | fontSize = 16.sp,
80 | lineHeight = 20.sp,
81 | letterSpacing = 0.1.sp,
82 | ),
83 | bodyLarge = TextStyle(
84 | fontFamily = InterFontFamily,
85 | fontWeight = FontWeight.Normal,
86 | fontSize = 16.sp,
87 | lineHeight = 24.sp,
88 | letterSpacing = 0.5.sp,
89 | ),
90 | bodyMedium = TextStyle(
91 | fontFamily = InterFontFamily,
92 | fontWeight = FontWeight.Medium,
93 | fontSize = 14.sp,
94 | lineHeight = 20.sp,
95 | letterSpacing = 0.25.sp,
96 | ),
97 | bodySmall = TextStyle(
98 | fontFamily = InterFontFamily,
99 | fontWeight = FontWeight.Normal,
100 | fontSize = 12.sp,
101 | lineHeight = 14.sp,
102 | letterSpacing = 0.sp,
103 | ),
104 | labelLarge = TextStyle(
105 | fontFamily = InterFontFamily,
106 | fontWeight = FontWeight.SemiBold,
107 | fontSize = 14.sp,
108 | lineHeight = 20.sp,
109 | letterSpacing = 0.1.sp,
110 | ),
111 | labelMedium = TextStyle(
112 | fontFamily = InterFontFamily,
113 | fontWeight = FontWeight.Medium,
114 | fontSize = 12.sp,
115 | lineHeight = 16.sp,
116 | letterSpacing = 0.5.sp,
117 | ),
118 | labelSmall = TextStyle(
119 | fontFamily = InterFontFamily,
120 | fontWeight = FontWeight.Medium,
121 | fontSize = 11.sp,
122 | lineHeight = 16.sp,
123 | letterSpacing = 0.5.sp,
124 | ),
125 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/util/Color.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.util
2 |
3 | import androidx.compose.animation.animateColorAsState
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.graphics.Color
7 |
8 | val Color.animated
9 | @Composable
10 | get() = animateColorAsState(this, animationSpec = tween(400)).value
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/util/Const.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.util
2 |
3 | import androidx.compose.ui.unit.dp
4 |
5 | val DefaultContentPaddingHorizontal = 16.dp
6 | val DefaultContentPaddingVertical = 12.dp
7 |
8 | val EdgeToEdgeContentPadding = 8.dp
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/util/Screen.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.util
2 |
3 | import androidx.annotation.StringRes
4 | import com.vanced.manager.R
5 | import com.vanced.manager.domain.model.InstallationOption
6 |
7 | sealed class Screen(
8 | val route: String,
9 | @StringRes val displayName: Int,
10 | ) {
11 | object Home : Screen(
12 | route = "home",
13 | displayName = R.string.app_name
14 | )
15 |
16 | object Settings : Screen(
17 | route = "settings",
18 | displayName = R.string.toolbar_settings,
19 | )
20 |
21 | object About : Screen(
22 | route = "about",
23 | displayName = R.string.toolbar_about,
24 | )
25 |
26 | object Logs : Screen(
27 | route = "logs",
28 | displayName = R.string.toolbar_logs,
29 | )
30 |
31 | data class Configuration(
32 | val appName: String,
33 | val appVersions: List?,
34 | val appInstallationOptions: List
35 | ) : Screen(
36 | route = "installpreferences",
37 | displayName = R.string.toolbar_installation_preferences
38 | )
39 |
40 | data class Install(
41 | val appName: String,
42 | val appVersions: List?
43 | ) : Screen(
44 | route = "install",
45 | displayName = R.string.toolbar_install
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/viewmodel/ConfigurationViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.viewmodel
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import androidx.lifecycle.ViewModel
7 |
8 | class ConfigurationViewModel : ViewModel() {
9 |
10 | var currentIndex by mutableStateOf(0)
11 | private set
12 |
13 | fun next() {
14 | currentIndex++
15 | }
16 |
17 | fun back() {
18 | currentIndex--
19 | }
20 |
21 | fun reset() {
22 | currentIndex = 0
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.viewmodel
2 |
3 | import android.app.Application
4 | import android.content.ComponentName
5 | import android.content.Intent
6 | import androidx.compose.runtime.getValue
7 | import androidx.compose.runtime.mutableStateOf
8 | import androidx.compose.runtime.setValue
9 | import androidx.lifecycle.AndroidViewModel
10 | import androidx.lifecycle.viewModelScope
11 | import com.vanced.manager.domain.model.App
12 | import com.vanced.manager.installer.util.PM
13 | import com.vanced.manager.repository.AppRepository
14 | import com.vanced.manager.repository.ManagerMode
15 | import com.vanced.manager.repository.PreferenceRepository
16 | import kotlinx.coroutines.async
17 | import kotlinx.coroutines.awaitAll
18 | import kotlinx.coroutines.launch
19 | import kotlinx.coroutines.supervisorScope
20 | import retrofit2.HttpException
21 |
22 | class MainViewModel(
23 | private val appRepository: AppRepository,
24 | private val preferenceRepository: PreferenceRepository,
25 | private val app: Application,
26 | ) : AndroidViewModel(app) {
27 |
28 | var appMode by mutableStateOf(preferenceRepository.managerMode)
29 | var appTheme by mutableStateOf(preferenceRepository.managerTheme)
30 |
31 | private val appCount
32 | get() = when (appMode) {
33 | ManagerMode.ROOT -> 2
34 | ManagerMode.NONROOT -> 3
35 | }
36 |
37 | var appState by mutableStateOf(ManagerState.Fetching(appCount))
38 | private set
39 |
40 | fun fetch() {
41 | viewModelScope.launch {
42 | try {
43 | supervisorScope {
44 | appState = ManagerState.Fetching(appCount)
45 |
46 | when (appMode) {
47 | ManagerMode.ROOT -> {
48 | appState = ManagerState.Success(
49 | apps = listOf(
50 | async { appRepository.getVancedYoutubeRoot() },
51 | async { appRepository.getVancedYoutubeMusicRoot() }
52 | ).awaitAll()
53 | )
54 | }
55 | ManagerMode.NONROOT -> {
56 | appState = ManagerState.Success(
57 | apps = listOf(
58 | async { appRepository.getVancedYoutubeNonroot() },
59 | async { appRepository.getVancedYoutubeMusicNonroot() },
60 | async { appRepository.getVancedMicrog() }
61 | ).awaitAll()
62 | )
63 | }
64 | }
65 | }
66 | } catch (e: HttpException) {
67 | appState = ManagerState.Error(e.message())
68 | } catch (e: Exception) {
69 | appState = ManagerState.Error(e.toString())
70 | }
71 | }
72 | }
73 |
74 | fun launchApp(
75 | packageName: String,
76 | launchActivity: String
77 | ) {
78 | val component = ComponentName(packageName, launchActivity)
79 | val intent = Intent().apply {
80 | setComponent(component)
81 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
82 | }
83 | app.startActivity(intent)
84 | }
85 |
86 | //TODO implement root uninstallation
87 | fun uninstallApp(
88 | appPackage: String,
89 | ) {
90 | PM.uninstallPackage(appPackage, app)
91 | }
92 |
93 | }
94 |
95 | sealed class ManagerState {
96 | data class Fetching(val placeholderAppsCount: Int) : ManagerState()
97 | data class Success(val apps: List) : ManagerState()
98 | data class Error(val error: String) : ManagerState()
99 |
100 | val isFetching get() = this is Fetching
101 | val isSuccess get() = this is Success
102 | val isError get() = this is Error
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/viewmodel/SettingsViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.viewmodel
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import androidx.lifecycle.ViewModel
7 | import com.vanced.manager.R
8 | import com.vanced.manager.repository.ManagerMode
9 | import com.vanced.manager.repository.ManagerTheme
10 | import com.vanced.manager.repository.PreferenceRepository
11 |
12 | class SettingsViewModel(
13 | private val preferenceRepository: PreferenceRepository
14 | ) : ViewModel() {
15 |
16 | var managerUseCustomTabs by mutableStateOf(preferenceRepository.managerUseCustomTabs)
17 | private set
18 | var managerMode by mutableStateOf(preferenceRepository.managerMode)
19 | private set
20 | var managerTheme by mutableStateOf(preferenceRepository.managerTheme)
21 | private set
22 |
23 | fun saveManagerUseCustomTabs(value: Boolean) {
24 | managerUseCustomTabs = value
25 | preferenceRepository.managerUseCustomTabs = value
26 | }
27 |
28 | fun saveManagerMode(value: ManagerMode) {
29 | managerMode = value
30 | preferenceRepository.managerMode = value
31 | }
32 |
33 | fun saveManagerTheme(value: ManagerTheme) {
34 | managerTheme = value
35 | preferenceRepository.managerTheme = value
36 | }
37 |
38 | fun getThemeStringId(value: ManagerTheme): Int {
39 | return when (value) {
40 | ManagerTheme.DARK -> R.string.settings_preference_theme_dark
41 | ManagerTheme.LIGHT -> R.string.settings_preference_theme_light
42 | else -> R.string.settings_option_system_default
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/ui/widget/LinkCard.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.ui.widget
2 |
3 | import android.net.Uri
4 | import androidx.browser.customtabs.CustomTabsIntent
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.material3.Icon
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.remember
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.painter.Painter
13 | import androidx.compose.ui.platform.LocalContext
14 | import androidx.compose.ui.unit.dp
15 | import com.vanced.manager.ui.component.ManagerElevatedCard
16 | import com.vanced.manager.ui.component.ManagerText
17 | import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
18 | import com.vanced.manager.ui.util.DefaultContentPaddingVertical
19 | import org.koin.androidx.compose.inject
20 |
21 | //TODO this composable should not handle opening links
22 | @Composable
23 | fun LinkCard(
24 | text: String,
25 | icon: Painter,
26 | url: String,
27 | modifier: Modifier = Modifier
28 | ) {
29 | val context = LocalContext.current
30 | val customTabs: CustomTabsIntent by inject()
31 | val uri = remember { Uri.parse(url) }
32 | ManagerElevatedCard(
33 | modifier = modifier
34 | .height(100.dp)
35 | .widthIn(min = 100.dp),
36 | onClick = {
37 | customTabs.launchUrl(context, uri)
38 | }
39 | ) {
40 | Box(
41 | modifier = Modifier
42 | .fillMaxSize()
43 | .padding(
44 | horizontal = DefaultContentPaddingHorizontal,
45 | vertical = DefaultContentPaddingVertical
46 | ),
47 | ) {
48 | Icon(
49 | modifier = Modifier
50 | .size(32.dp)
51 | .align(Alignment.TopStart),
52 | painter = icon,
53 | contentDescription = null,
54 | )
55 | ManagerText(
56 | modifier = Modifier.align(Alignment.BottomStart),
57 | text = text,
58 | textStyle = MaterialTheme.typography.labelLarge
59 | )
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/util/AppHelper.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.util
2 |
3 | fun getLatestOrProvidedAppVersion(
4 | version: String,
5 | appVersions: List?
6 | ): String {
7 | if (appVersions == null)
8 | return version
9 |
10 | if (appVersions.contains(version))
11 | return version
12 |
13 | return appVersions.last()
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/util/Arch.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.util
2 |
3 | import android.os.Build
4 |
5 | val arch
6 | get() = when {
7 | Build.SUPPORTED_ABIS.contains("x86") -> "x86"
8 | Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
9 | else -> "armeabi_v7a"
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/util/Coroutines.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.util
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.launch
5 | import java.util.concurrent.Executor
6 | import kotlin.coroutines.CoroutineContext
7 |
8 | fun CoroutineContext.asExecutor(): Executor = object : Executor {
9 | private val scope = CoroutineScope(this@asExecutor)
10 |
11 | override fun execute(command: Runnable) {
12 | scope.launch { command.run() }
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/util/IO.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.util
2 |
3 | import okhttp3.ResponseBody
4 | import java.io.FileOutputStream
5 | import java.io.InputStream
6 | import java.io.OutputStream
7 |
8 | inline fun ResponseBody.writeFile(
9 | filePath: String,
10 | onProgress: (Float) -> Unit
11 | ) {
12 | byteStream().use { inputStream ->
13 | FileOutputStream(filePath).use { outputStream ->
14 | val totalBytes = contentLength()
15 | inputStream.copyTo(outputStream, 8192) { bytes ->
16 | onProgress((bytes * 100 / totalBytes).toFloat())
17 | }
18 | }
19 | }
20 | }
21 |
22 | inline fun InputStream.copyTo(
23 | outputStream: OutputStream,
24 | bufferSize: Int,
25 | onProgress: (Long) -> Unit
26 | ) {
27 | val buffer = ByteArray(bufferSize)
28 | var bytesCopied: Long = 0
29 | var bytes = read(buffer)
30 | while (bytes >= 0) {
31 | outputStream.write(buffer, 0, bytes)
32 | bytesCopied += bytes
33 | bytes = read(buffer)
34 | onProgress(bytesCopied)
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/util/Safety.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.util
2 |
3 | //Dear reader, welcome to HELL.
4 | //We don't kink-shame here.
5 |
6 | inline fun doubleUnionTryCatch(
7 | onCatch: (Exception) -> R,
8 | onTry: () -> R
9 | ): R {
10 | return try {
11 | onTry()
12 | } catch (e: Exception) {
13 | when (e) {
14 | is E1, is E2 -> onCatch(e)
15 | else -> throw e
16 | }
17 | }
18 | }
19 |
20 | inline fun tripleUnionTryCatch(
21 | onCatch: (Exception) -> R,
22 | onTry: () -> R
23 | ): R {
24 | return try {
25 | onTry()
26 | } catch (e: Exception) {
27 | when (e) {
28 | is E1, is E2, is E3 -> onCatch(e)
29 | else -> throw e
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/vanced/manager/util/SuShell.kt:
--------------------------------------------------------------------------------
1 | package com.vanced.manager.util
2 |
3 | import com.topjohnwu.superuser.Shell
4 | import kotlin.coroutines.resume
5 | import kotlin.coroutines.resumeWithException
6 | import kotlin.coroutines.suspendCoroutine
7 |
8 | val Shell.Result.outString
9 | get() = out.joinToString("\n")
10 |
11 | val Shell.Result.errString
12 | get() = err.joinToString("\n")
13 |
14 | val isMagiskInstalled
15 | get() = Shell.rootAccess() && Shell.su("magisk", "-c").exec().isSuccess
16 |
17 | suspend fun Shell.Job.await(): Shell.Result {
18 | return suspendCoroutine { continuation ->
19 | submit {
20 | continuation.resume(it)
21 | }
22 | }
23 | }
24 |
25 | class SuException(val stderrOut: String) : Exception(stderrOut)
26 |
27 | @Throws(SuException::class)
28 | suspend fun Shell.Job.awaitOutputOrThrow(): String {
29 | return suspendCoroutine { continuation ->
30 | submit {
31 | if (it.isSuccess) {
32 | continuation.resume(it.outString)
33 | } else {
34 | continuation.resumeWithException(SuException(it.errString))
35 | }
36 | }
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/fragment_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/fragment_enter_pop.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/fragment_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/fragment_exit_pop.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-anydpi-v24/ic_stat_name.xml:
--------------------------------------------------------------------------------
1 |
8 |
12 |
15 |
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
38 |
39 |
40 |
41 |
42 |
43 |
46 |
49 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
66 |
68 |
69 |
75 |
76 |
77 |
78 |
79 |
80 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-hdpi/ic_splash_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-hdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/ic_splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-ldpi/ic_splash_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-mdpi/ic_splash_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-mdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
14 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
45 |
48 |
50 |
51 |
57 |
58 |
59 |
60 |
61 |
62 |
65 |
67 |
68 |
74 |
75 |
76 |
77 |
78 |
79 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-xhdpi/ic_splash_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-xhdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-xxhdpi/ic_splash_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-xxhdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/drawable-xxxhdpi/ic_splash_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_adguard.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_android_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_app_icon_placeholder.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_brave.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_brave_light.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_discord.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_github.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_instagram.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_magisk.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_manager.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
44 |
47 |
49 |
50 |
56 |
57 |
58 |
59 |
60 |
61 |
64 |
66 |
67 |
73 |
74 |
75 |
76 |
77 |
78 |
81 |
82 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_microg.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_music.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_reddit.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_assignment_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_close_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_done_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_telegram.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_twitter.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_vanced.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_website.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_youtube.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_black.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_extralight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_extralight.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_extranold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_extranold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_light.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_medium.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_regular.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_semibold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/inter_thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/font/inter_thin.ttf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #131317
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #673AB7
5 | #FB542B
6 | #7289DA
7 | #1DA1F2
8 | #0088cc
9 | #FF4500
10 | #17191A
11 |
12 |
13 | #f7f7f7
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 128dp
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #171719
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/resources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Vanced Manager
5 |
6 | YouTube Vanced
7 | YouTube Music Vanced
8 | Vanced microG
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/file_provider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 |
7 | val kotlinVersion = "1.6.10"
8 | dependencies {
9 | classpath("com.android.tools.build:gradle:7.1.2")
10 | classpath(kotlin("gradle-plugin", version = kotlinVersion))
11 | classpath(kotlin("serialization", version = kotlinVersion))
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | mavenCentral()
19 | maven(url = "https://jitpack.io")
20 | }
21 | }
22 |
23 | task("clean") {
24 | delete(rootProject.buildDir)
25 | }
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | files:
2 | - source: /app/src/main/res/values/strings.xml
3 | translation: /app/src/main/res/values-%android_code%/strings.xml
4 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## For more details on how to configure your build environment visit
2 | # http://www.gradle.org/docs/current/userguide/build_environment.html
3 | #
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
7 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8 | #
9 | # When configured, Gradle will run in incubating parallel mode.
10 | # This option should only be used with decoupled projects. More details, visit
11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
12 | # org.gradle.parallel=true
13 | #Sun Dec 13 13:42:32 GET 2020
14 | android.enableJetifier=true
15 | android.useAndroidX=true
16 | kotlin.code.style=official
17 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamVanced/VancedManager/1c219ea3e0d09899c9cced8e4ae7dbed35f87c7d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "Vanced Manager"
2 |
3 | include(":app")
4 |
--------------------------------------------------------------------------------