├── .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 | [![Github All Releases](https://img.shields.io/github/downloads/YTVanced/VancedManager/total.svg?style=for-the-badge)](https://github.com/YTVanced/VancedManager/releases/latest) [![Github All Releases](https://img.shields.io/github/release/YTVanced/VancedManager.svg?style=for-the-badge)](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 | [![Build](https://github.com/YTVanced/VancedManager/actions/workflows/debug.yml/badge.svg?branch=dev)](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 | ![Signed APK Builder](https://github.com/X1nto/VancedInstaller/workflows/Signed%20APK%20Builder/badge.svg?branch=master) 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 | ![screenshot](https://i.imgur.com/r2jiq7J.png) 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 | ![xfileFIN](https://i.imgur.com/hLdzTVq.png) 45 | - KevinX8 46 | ![KevinX8](https://i.imgur.com/cS9C7P8.png) 47 | - Zanezam 48 | ![Zanezam](https://i.imgur.com/QVcXA6q.png) 49 | - Laura Almeida 50 | ![Laura Almeida](https://i.imgur.com/ovVD939.png) 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 | --------------------------------------------------------------------------------