diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml index a643436a44..88480ced12 100644 --- a/.github/workflows/debug.yml +++ b/.github/workflows/debug.yml @@ -18,11 +18,6 @@ jobs: with: java-version: 1.8 - - name: Write firebase config to file - run: echo $FIREBASE_CONFIG > app/google-services.json - env: - FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }} - - name: Grant rights run: chmod +x ./gradlew @@ -36,4 +31,4 @@ jobs: uses: actions/upload-artifact@v2 with: name: 'Manager' - path: app/build/outputs/apk/debug/app-debug.apk \ No newline at end of file + path: app/build/outputs/apk/debug/app-debug.apk diff --git a/.gitignore b/.gitignore index ec679baaec..6b24209962 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,8 @@ build/ out/ app/src/main/java/com/vanced/manager/core/base/DummyJava.java -app/google-services.json +app/build/ +app/release local.properties /.github/ *.iml diff --git a/README.md b/README.md index 3cc0a26428..5a4c52e062 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,11 @@ For anyone who wants to provide translations please submit them to https://trans [![Github All Releases](https://img.shields.io/github/downloads/YTVanced/VancedManager/total.svg)]() [![Github All Releases](https://img.shields.io/github/release/YTVanced/VancedManager.svg)]() # Vanced Manager 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. -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 5mb?" and that's how Vanced Manager was born. +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. -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, support for background download and installation* -##### *Due to changes in Android Oreo and up, and because of aggressive battery optimisations in some ROMs, feature may not be available for all devices. +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 + +##### Background download/installation feature is no longer supported due to problems with some ROMs, please do NOT report problems regarding background activity. ## Vanced Developers - xfileFIN @@ -16,12 +17,16 @@ After 3 months of development, we are finally ready to introduce Vanced Manager - Zanezam - Laura Almeida -## Vanced Manager Developer +## Vanced Manager Developers - Xinto (X1nto) +- Koopah (ostajic) + +## Contributors +- AioiLight ## Credits - topjohnwu for his wonderful [LibSU](https://github.com/topjohnwu/libsu) - Mindorks for their amazing [PRDownloader](https://github.com/MindorksOpenSource/PRDownloader) - aefyr for [SAI](https://github.com/aefyr/SAI), which was an inspiration for our Manager -- kittinunf for the [Fuel](https://github.com/kittinunf/Fuel) library -- cbeust for the [klaxon](https://github.com/cbeust/klaxon) library +- kittinunf for [Fuel](https://github.com/kittinunf/Fuel) HTTP client +- cbeust for [klaxon](https://github.com/cbeust/klaxon) JSON parser \ No newline at end of file diff --git a/READMEME.md b/READMEME.md index b6afbd3a2c..29fe0e5232 100644 --- a/READMEME.md +++ b/READMEME.md @@ -9,7 +9,7 @@ No one really thought there would be problems with this format, because installa ## Problems with .apks format 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 # Vanced Manager -Ladies and gentlemen, I'm very proud to introduce the new **Vancad Manger 1.0.0™** (typo is intentional) +Ladies and gentlemen, I'm very proud to introduce the new **Vancad Manger 1.2.0™** (typo is intentional) 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). 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). @@ -21,16 +21,21 @@ Isn't this lovely and beautiful? - 1337Potato: shit - Response: Yes + +=================== - Noobbot: The app is not useful because I have YT Premium. Thank you bye -- Response: I hope you get sucked by a di- +- Response: I hope you get sucked by a di- + +=================== - Vortextriangle: The app is so useful that I uninstalled it after installing Vanced - Response: yo that's finna woke ## Credits ### Vanced Manager developers -- X1nto (UI, UX, Downloader, Installer, Signature Checker, PussiSlayer69, Collector of 400 BAT, Professional Liar) +- X1nto (UI, UX, Downloader, Installer, Signature Checker, PussiSlayer69, Collector of 400 BAT, A great liar) +- Koopah (Unix lord, Unmounter of /system, Code criticizer) ### The Vanced Team - xfileFIN ![xfileFIN](https://i.imgur.com/hLdzTVq.png) @@ -40,3 +45,6 @@ Isn't this lovely and beautiful? ![Zanezam](https://i.imgur.com/QVcXA6q.png) - Laura Almeida ![Laura Almeida](https://i.imgur.com/ovVD939.png) + +###### If someone is reading this pls help me, KevinX8 is bullying me and forces me to develop manager. please send bobs and veganas and call 911 + diff --git a/app/build.gradle b/app/build.gradle index 5ad0872499..a4a687d255 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,3 +1,6 @@ +import java.util.regex.Matcher +import java.util.regex.Pattern + apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' @@ -5,19 +8,25 @@ apply plugin: 'kotlin-kapt' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.firebase-perf' apply plugin: 'com.google.firebase.crashlytics' +apply plugin: 'androidx.navigation.safeargs.kotlin' + +ext.langs = getLanguages() android { compileSdkVersion 30 - buildToolsVersion "30.0.1" + buildToolsVersion "30.0.2" defaultConfig { applicationId "com.vanced.manager" minSdkVersion 21 targetSdkVersion 30 - versionCode 12 - versionName "1.2.0 (Niko)" + versionCode 13 + versionName "1.3.0b (Arcturus)" vectorDrawables.useSupportLibrary = true + + buildConfigField "String[]", "MANAGER_LANGUAGES", "{" + surroundWithQuotes(langs) + "}" + buildConfigField "String[]", "MANAGER_LANGUAGE_NAMES", "{" + surroundWithQuotes(getLanguageNames()) + "}" } lintOptions { @@ -56,6 +65,48 @@ android { } +def getLanguages() { + List langs = new ArrayList() + langs.add("en") + Pattern pattern = Pattern.compile("-(\\w+)-") + new File("${projectDir}/src/main/res").eachDir { dir -> + if (dir.name.startsWith("values-") && !dir.name.contains("v23")) { + Matcher matcher = pattern.matcher(dir.name) + if (matcher.find()) { + if (langs.contains(matcher.group(1))) + langs.add(matcher.group(1) + "_${dir.name.substring(dir.name.length() - 2)}") + else + langs.add(matcher.group(1)) + } + } + } + return langs.toArray() +} + +def getLanguageNames() { + List langnames = new ArrayList() + for (int i = 0; i < langs.size(); i++) { + if (langs[i].length() > 2) { + Locale loc = new Locale(langs[i].substring(0, langs[i].length() - 3), langs[i].substring(langs[i].length() - 2)) + project.logger.lifecycle(loc.getDisplayLanguage(loc).capitalize() + " (" + loc.getDisplayCountry(loc).capitalize() + ")") + langnames.add(loc.getDisplayLanguage(loc).capitalize() + " (" + loc.getDisplayCountry(loc).capitalize() + ")") + } else { + Locale loc = new Locale(langs[i]) + project.logger.lifecycle(loc.getDisplayLanguage(loc).capitalize()) + langnames.add(loc.getDisplayLanguage(loc).capitalize()) + } + } + return langnames.toArray() +} + +def surroundWithQuotes(Object[] arr) { + String[] dummyArr = arr + for (int i = 0; i < arr.length; i++) { + dummyArr[i] = "\"" + arr[i] + "\"" + } + return dummyArr.join(", ") +} + dependencies { // Kotlin @@ -63,10 +114,9 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" // AndroidX - implementation 'androidx.activity:activity:1.1.0' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.browser:browser:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.core:core-ktx:1.3.1' implementation 'androidx.fragment:fragment-ktx:1.2.5' implementation 'androidx.lifecycle:lifecycle-livedata-core-ktx:2.2.0' @@ -84,11 +134,13 @@ dependencies { implementation 'com.github.kittinunf.fuel:fuel:2.2.3' implementation 'com.github.kittinunf.fuel:fuel-coroutines:2.2.3' implementation 'com.github.kittinunf.fuel:fuel-json:2.2.3' + implementation 'com.squareup.okhttp3:logging-interceptor:4.8.1' implementation 'com.github.topjohnwu.libsu:core:3.0.1' + implementation 'com.github.topjohnwu.libsu:io:3.0.1' implementation 'com.google.firebase:firebase-messaging:20.2.4' implementation 'com.google.firebase:firebase-perf:19.0.8' implementation 'com.mindorks.android:prdownloader:0.6.0' - implementation 'com.google.firebase:firebase-analytics-ktx:17.4.4' - implementation 'com.google.firebase:firebase-crashlytics:17.1.1' + implementation 'com.google.firebase:firebase-analytics-ktx:17.5.0' + implementation 'com.google.firebase:firebase-crashlytics:17.2.1' } diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000000..1c61c878fb --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "840099702732", + "firebase_url": "https://vanced-manager-official.firebaseio.com", + "project_id": "vanced-manager-official", + "storage_bucket": "vanced-manager-official.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:840099702732:android:ca65567b49f622bc359f69", + "android_client_info": { + "package_name": "com.vanced.manager" + } + }, + "oauth_client": [ + { + "client_id": "840099702732-4fjjofq6on2bpd7jb6f96bk0mkrjkkf6.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAPI1RUaoCHmmWz9-TLvTKYPYs0ZVKkS2U" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "840099702732-4fjjofq6on2bpd7jb6f96bk0mkrjkkf6.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 25e189a878..7233f3e290 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -13,6 +13,7 @@ #} -keep class com.vanced.manager.core.App +-keepnames class androidx.navigation.fragment.NavHostFragment # Uncomment this to preserve the line number information for # debugging stack traces. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 52f5c22bbe..b9fbff4c5c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,10 +4,17 @@ xmlns:tools="http://schemas.android.com/tools" package="com.vanced.manager"> + + + + + + + + android:theme="@style/DarkTheme.Blue" + android:exported="true"/> - - - - - - \ No newline at end of file + diff --git a/app/src/main/java/com/vanced/manager/adapter/ChangelogAdapter.kt b/app/src/main/java/com/vanced/manager/adapter/ChangelogAdapter.kt new file mode 100644 index 0000000000..fe9ba7ab30 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/adapter/ChangelogAdapter.kt @@ -0,0 +1,38 @@ +package com.vanced.manager.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.vanced.manager.R +import com.vanced.manager.ui.viewmodels.HomeViewModel + +class ChangelogAdapter(private val variant: String, viewModel: HomeViewModel?): RecyclerView.Adapter() { + + private val nonrootChangelogs = arrayOf(viewModel?.vanced?.get()?.changelog?.get(), viewModel?.music?.get()?.changelog?.get(), viewModel?.microg?.get()?.changelog?.get(), viewModel?.manager?.get()?.changelog?.get()) + private val rootChangelogs = arrayOf(viewModel?.vanced?.get()?.changelog?.get(), viewModel?.manager?.get()?.changelog?.get()) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChangelogViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val view = layoutInflater.inflate(R.layout.view_changelog, parent, false) + + return ChangelogViewHolder(view) + } + + override fun getItemCount(): Int = if (variant == "root") 2 else 4 + + override fun onBindViewHolder(holder: ChangelogViewHolder, position: Int) { + holder.changelog.text = + if (variant == "root") + rootChangelogs[position] + else + nonrootChangelogs[position] + + } + + open class ChangelogViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val changelog: TextView = itemView.findViewById(R.id.changelog_text) + } + +} diff --git a/app/src/main/java/com/vanced/manager/adapter/SectionPageAdapter.kt b/app/src/main/java/com/vanced/manager/adapter/SectionPageAdapter.kt deleted file mode 100644 index 3d8d47246b..0000000000 --- a/app/src/main/java/com/vanced/manager/adapter/SectionPageAdapter.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.vanced.manager.adapter - -import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.vanced.manager.ui.fragments.ManagerChangelogFragment -import com.vanced.manager.ui.fragments.MicrogChangelogFragment -import com.vanced.manager.ui.fragments.VancedChangelogFragment - -class SectionPageAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { - - override fun getItemCount(): Int = 3 - - override fun createFragment(position: Int): Fragment { - return when (position) { - 0 -> VancedChangelogFragment() - 1 -> MicrogChangelogFragment() - 2 -> ManagerChangelogFragment() - else -> throw NotImplementedError() - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/adapter/SectionPageRootAdapter.kt b/app/src/main/java/com/vanced/manager/adapter/SectionPageRootAdapter.kt deleted file mode 100644 index d3fd89df21..0000000000 --- a/app/src/main/java/com/vanced/manager/adapter/SectionPageRootAdapter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.vanced.manager.adapter - -import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.vanced.manager.ui.fragments.ManagerChangelogFragment -import com.vanced.manager.ui.fragments.VancedChangelogFragment - -class SectionPageRootAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { - - override fun getItemCount(): Int = 2 - - override fun createFragment(position: Int): Fragment { - return when (position) { - 0 -> VancedChangelogFragment() - 1 -> ManagerChangelogFragment() - else -> throw NotImplementedError() - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/adapter/VariantAdapter.kt b/app/src/main/java/com/vanced/manager/adapter/VariantAdapter.kt new file mode 100644 index 0000000000..632faf34d8 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/adapter/VariantAdapter.kt @@ -0,0 +1,91 @@ +package com.vanced.manager.adapter + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateDecelerateInterpolator +import android.widget.LinearLayout +import android.widget.Toast +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.tabs.TabLayoutMediator +import com.vanced.manager.R +import com.vanced.manager.databinding.ViewHomeBinding +import com.vanced.manager.ui.viewmodels.HomeViewModel +import kotlinx.android.synthetic.main.include_changelogs.view.* + +class VariantAdapter(private val viewModel: HomeViewModel, private val context: Context) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VariantAdapterHolder { + val view = ViewHomeBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return VariantAdapterHolder(view, context) + } + + override fun onBindViewHolder(holder: VariantAdapterHolder, position: Int) { + val variants = arrayOf("nonroot", "root") + holder.bind(variants[position], viewModel) + } + + override fun getItemCount(): Int = 2 + + class VariantAdapterHolder(private val homeBinding: ViewHomeBinding, private val context: Context) : RecyclerView.ViewHolder(homeBinding.root) { + private var isExpanded: Boolean = false + + fun bind(variant: String, viewModel: HomeViewModel) { + with (homeBinding) { + this.variant = variant + this.viewModel = viewModel + + with(includeChangelogsLayout) { + changelogButton.setOnClickListener { + with (includeChangelogsLayout) { + viewpager.visibility = if (isExpanded) View.GONE else View.VISIBLE + tablayout.visibility = if (isExpanded) View.GONE else View.VISIBLE + changelogButton.animate().apply { + rotation(if (isExpanded) 0F else 180F) + interpolator = AccelerateDecelerateInterpolator() + } + isExpanded = !isExpanded + } + } + viewpager.adapter = ChangelogAdapter(variant, viewModel) + val nonrootTitles = arrayOf("Vanced", "Music", "microG", "Manager") + val rootTitles = arrayOf("Vanced", "Manager") + + TabLayoutMediator(tablayout, viewpager) { tab, position -> + tab.text = + if (variant == "root") { + rootTitles[position] + } else { + nonrootTitles[position] + } + }.attach() + } + + includeVancedLayout.vancedCard.setOnLongClickListener { + versionToast("Vanced", viewModel.vanced.get()?.installedVersionName?.get()!!) + true + } + + includeMusicLayout.musicCard.setOnLongClickListener { + versionToast("Music", viewModel.music.get()?.installedVersionName?.get()!!) + true + } + + includeMicrogLayout.microgCard.setOnLongClickListener { + versionToast("MicroG", viewModel.microg.get()?.installedVersionName?.get()!!) + true + } + } + } + private fun versionToast(name: String, app: String?) { + val clip = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clip.setPrimaryClip(ClipData.newPlainText(name, app)) + Toast.makeText(context, context.getString(R.string.version_toast, name), Toast.LENGTH_LONG).show() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/App.kt b/app/src/main/java/com/vanced/manager/core/App.kt index d69f5c1448..d4b8ab3b42 100644 --- a/app/src/main/java/com/vanced/manager/core/App.kt +++ b/app/src/main/java/com/vanced/manager/core/App.kt @@ -1,29 +1,49 @@ package com.vanced.manager.core import android.app.Application +import android.content.res.Configuration +import androidx.preference.PreferenceManager.getDefaultSharedPreferences +import com.beust.klaxon.JsonObject import com.crowdin.platform.Crowdin import com.crowdin.platform.CrowdinConfig import com.crowdin.platform.data.remote.NetworkType import com.downloader.PRDownloader +import com.vanced.manager.utils.InternetTools.baseUrl +import com.vanced.manager.utils.JsonHelper.getJson +import kotlinx.coroutines.* -class App: Application() { +open class App: Application() { + + var vanced: JsonObject? = null + var music: JsonObject? = null + var microg: JsonObject? = null + var manager: JsonObject? = null override fun onCreate() { + loadJsonAsync() super.onCreate() PRDownloader.initialize(this) Crowdin.init(this, - CrowdinConfig.Builder() - .withDistributionHash("36c51aed3180a4f43073d28j4s6") - .withNetworkType(NetworkType.WIFI) - .build()) + CrowdinConfig.Builder() + .withDistributionHash("36c51aed3180a4f43073d28j4s6") + .withNetworkType(NetworkType.WIFI) + .build() + ) + + } + + fun loadJsonAsync() { + val latest = runBlocking { getJson("${getDefaultSharedPreferences(this@App).getString("update_url", baseUrl)}/latest.json") } + vanced = latest.obj("vanced") + music = latest.obj("music") + microg = latest.obj("microg") + manager = latest.obj("manager") } - /* override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) Crowdin.onConfigurationChanged() } - */ -} \ No newline at end of file +} diff --git a/app/src/main/java/com/vanced/manager/core/downloader/MicrogDownloadService.kt b/app/src/main/java/com/vanced/manager/core/downloader/MicrogDownloadService.kt deleted file mode 100644 index a2e29a9e0e..0000000000 --- a/app/src/main/java/com/vanced/manager/core/downloader/MicrogDownloadService.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.vanced.manager.core.downloader - -import android.app.Service -import android.content.Intent -import android.os.IBinder -import android.widget.Toast -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.preference.PreferenceManager -import com.downloader.Error -import com.downloader.OnDownloadListener -import com.downloader.PRDownloader -import com.vanced.manager.R -import com.vanced.manager.core.installer.AppInstaller -import com.vanced.manager.ui.fragments.HomeFragment -import com.vanced.manager.utils.AppUtils.installing -import com.vanced.manager.utils.InternetTools.baseUrl -import com.vanced.manager.utils.InternetTools.getFileNameFromUrl -import com.vanced.manager.utils.InternetTools.getObjectFromJson -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -class MicrogDownloadService: Service() { - - //private var downloadId: Long = 0 - private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - //registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) - downloadMicrog() - stopSelf() - return START_NOT_STICKY - } - - private fun downloadMicrog() { - CoroutineScope(Dispatchers.IO).launch { - val url = getObjectFromJson( - "${PreferenceManager.getDefaultSharedPreferences(this@MicrogDownloadService) - .getString("install_url", baseUrl)}/microg.json", "url" - ) - - //downloadId = download(url, "apk", "microg.apk", this@MicrogDownloadService) - - PRDownloader.download(url, getExternalFilesDir("apk")?.path, "microg.apk") - .build() - .setOnStartOrResumeListener { installing = true } - .setOnProgressListener { progress -> - val mProgress = progress.currentBytes * 100 / progress.totalBytes - localBroadcastManager.sendBroadcast(Intent(HomeFragment.MICROG_DOWNLOADING).putExtra("progress", mProgress.toInt()).putExtra("file", getFileNameFromUrl(url))) - } - .start(object : OnDownloadListener { - override fun onDownloadComplete() { - val intent = Intent(this@MicrogDownloadService, AppInstaller::class.java) - intent.putExtra("path", "${getExternalFilesDir("apk")}/microg.apk") - intent.putExtra("pkg", "com.mgoogle.android.gms") - localBroadcastManager.sendBroadcast(Intent(HomeFragment.MICROG_INSTALLING)) - startService(intent) - } - - override fun onError(error: Error?) { - installing = false - Toast.makeText(this@MicrogDownloadService, getString(R.string.error_downloading, "microG"), Toast.LENGTH_SHORT).show() - } - }) - - } - - } - - /* - private val receiver = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) { - //prefs?.edit()?.putBoolean("isMicrogDownloading", false)?.apply() - //cancelNotif(channel, this@MicrogDownloadService) - val bIntent = Intent(this@MicrogDownloadService, AppInstaller::class.java) - bIntent.putExtra("path", "${getExternalFilesDir("apk")}/microg.apk") - bIntent.putExtra("pkg", "com.mgoogle.android.gms") - startService(bIntent) - } - } - } - */ - - - override fun onBind(intent: Intent?): IBinder? { - return null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/downloader/MicrogDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/MicrogDownloader.kt new file mode 100644 index 0000000000..b48b1c50be --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/downloader/MicrogDownloader.kt @@ -0,0 +1,73 @@ +package com.vanced.manager.core.downloader + +import android.content.Context +import android.widget.Toast +import com.downloader.Error +import com.downloader.OnDownloadListener +import com.downloader.PRDownloader +import com.vanced.manager.R +import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.microgProgress +import com.vanced.manager.utils.AppUtils.mutableInstall +import com.vanced.manager.utils.InternetTools.getFileNameFromUrl +import com.vanced.manager.utils.InternetTools.getJsonString +import com.vanced.manager.utils.PackageHelper.install +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object MicrogDownloader { + + //private var downloadId: Long = 0 + + fun downloadMicrog(context: Context) { + CoroutineScope(Dispatchers.IO).launch { + val url = getJsonString("microg.json", "url", context) + //downloadId = download(url, "apk", "microg.apk", this@MicrogDownloadService) + + microgProgress.get()?.currentDownload = PRDownloader.download(url, context.getExternalFilesDir("apk")?.path, "microg.apk") + .build() + .setOnStartOrResumeListener { + mutableInstall.value = true + microgProgress.get()?.downloadingFile?.set(context.getString(R.string.downloading_file, getFileNameFromUrl(url))) + microgProgress.get()?.showDownloadBar?.set(true) + } + .setOnCancelListener { + mutableInstall.value = false + microgProgress.get()?.showDownloadBar?.set(false) + } + .setOnProgressListener { progress -> + microgProgress.get()?.downloadProgress?.set((progress.currentBytes * 100 / progress.totalBytes).toInt()) + } + .start(object : OnDownloadListener { + override fun onDownloadComplete() { + install("microg", "${context.getExternalFilesDir("apk")}/microg.apk", context) + microgProgress.get()?.showDownloadBar?.set(false) + microgProgress.get()?.showInstallCircle?.set(true) + } + + override fun onError(error: Error?) { + mutableInstall.value = false + microgProgress.get()?.showDownloadBar?.set(false) + Toast.makeText(context, context.getString(R.string.error_downloading, "microG"), Toast.LENGTH_SHORT).show() + } + }) + + } + } + + /* + private val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) { + //prefs?.edit()?.putBoolean("isMicrogDownloading", false)?.apply() + //cancelNotif(channel, this@MicrogDownloadService) + val bIntent = Intent(this@MicrogDownloadService, AppInstaller::class.java) + bIntent.putExtra("path", "${getExternalFilesDir("apk")}/microg.apk") + bIntent.putExtra("pkg", "com.mgoogle.android.gms") + startService(bIntent) + } + } + } + */ + +} diff --git a/app/src/main/java/com/vanced/manager/core/downloader/MusicDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/MusicDownloader.kt new file mode 100644 index 0000000000..6917cafd32 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/downloader/MusicDownloader.kt @@ -0,0 +1,76 @@ +package com.vanced.manager.core.downloader + +import android.content.Context +import android.widget.Toast +import com.downloader.Error +import com.downloader.OnDownloadListener +import com.downloader.PRDownloader +import com.vanced.manager.R +import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.musicProgress +import com.vanced.manager.utils.AppUtils.mutableInstall +import com.vanced.manager.utils.InternetTools.getFileNameFromUrl +import com.vanced.manager.utils.InternetTools.getJsonString +import com.vanced.manager.utils.PackageHelper.install +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object MusicDownloader { + + //private var downloadId: Long = 0 + + fun downloadMusic(context: Context){ + CoroutineScope(Dispatchers.IO).launch { + val version = getJsonString("music.json", "version", context) + val url = "https://vanced.app/api/v1/music/v$version.apk" + + //downloadId = download(url, "apk", "music.apk", this@MusicDownloadService) + + musicProgress.get()?.currentDownload = PRDownloader.download(url, context.getExternalFilesDir("apk")?.path, "music.apk") + .build() + .setOnStartOrResumeListener { + mutableInstall.value = true + musicProgress.get()?.downloadingFile?.set(context.getString(R.string.downloading_file, getFileNameFromUrl(url))) + musicProgress.get()?.showDownloadBar?.set(true) + } + .setOnProgressListener { progress -> + musicProgress.get()?.downloadProgress?.set((progress.currentBytes * 100 / progress.totalBytes).toInt()) + } + .setOnCancelListener { + mutableInstall.value = false + musicProgress.get()?.showDownloadBar?.set(false) + } + .start(object : OnDownloadListener { + override fun onDownloadComplete() { + install("music", "${context.getExternalFilesDir("apk")}/music.apk", context) + musicProgress.get()?.showDownloadBar?.set(false) + musicProgress.get()?.showInstallCircle?.set(true) + } + + override fun onError(error: Error?) { + mutableInstall.value = false + musicProgress.get()?.showDownloadBar?.set(false) + Toast.makeText(context, context.getString(R.string.error_downloading, "Music"), Toast.LENGTH_SHORT).show() + } + }) + + } + + } + + /* + private val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) { + //prefs?.edit()?.putBoolean("isMusicDownloading", false)?.apply() + //cancelNotif(channel, this@MusicDownloadService) + val bIntent = Intent(this@MusicDownloadService, AppInstaller::class.java) + bIntent.putExtra("path", "${getExternalFilesDir("apk")}/music.apk") + bIntent.putExtra("pkg", "com.mgoogle.android.gms") + startService(bIntent) + } + } + } + */ + +} diff --git a/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloadService.kt b/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloadService.kt deleted file mode 100644 index 537e29da71..0000000000 --- a/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloadService.kt +++ /dev/null @@ -1,142 +0,0 @@ -package com.vanced.manager.core.downloader - -import android.app.Service -import android.content.Context -import android.content.Intent -import android.os.Build -import android.os.IBinder -import android.widget.Toast -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.preference.PreferenceManager -import com.downloader.Error -import com.downloader.OnDownloadListener -import com.downloader.PRDownloader -import com.vanced.manager.R -import com.vanced.manager.core.installer.RootSplitInstallerService -import com.vanced.manager.core.installer.SplitInstaller -import com.vanced.manager.ui.fragments.HomeFragment -import com.vanced.manager.utils.AppUtils.installing -import com.vanced.manager.utils.InternetTools.baseUrl -import com.vanced.manager.utils.InternetTools.getFileNameFromUrl -import com.vanced.manager.utils.InternetTools.getObjectFromJson -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import java.io.File - -class VancedDownloadService: Service() { - - //private var downloadId: Long = 0 - //private var apkType: String = "arch" - private var count: Int = 0 - private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - //registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) - downloadSplits() - stopSelf() - return START_NOT_STICKY - } - - private fun downloadSplits( - type: String = "arch" - ) { - CoroutineScope(Dispatchers.IO).launch { - File(getExternalFilesDir("apk")?.path as String).deleteRecursively() - val defPrefs = PreferenceManager.getDefaultSharedPreferences(this@VancedDownloadService) - val installUrl = defPrefs.getString("install_url", baseUrl) - val vancedVer = getObjectFromJson("$installUrl/vanced.json", "version") - - val prefs = getSharedPreferences("installPrefs", Context.MODE_PRIVATE) - val variant = defPrefs.getString("vanced_variant", "nonroot") - val lang = prefs?.getString("lang", "en")?.split(", ")?.toTypedArray() - val theme = prefs?.getString("theme", "dark") - val arch = - when { - Build.SUPPORTED_ABIS.contains("x86") -> "x86" - Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a" - else -> "armeabi_v7a" - } - val url = - when (type) { - "arch" -> "$installUrl/apks/v$vancedVer/$variant/Arch/split_config.$arch.apk" - "theme" -> "$installUrl/apks/v$vancedVer/$variant/Theme/$theme.apk" - "lang" -> "$installUrl/apks/v$vancedVer/$variant/Language/split_config.${lang?.get(count)}.apk" - else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?") - } - - //apkType = type - //downloadId = download(url, "apks", getFileNameFromUrl(url), this@VancedDownloadService) - - PRDownloader - .download(url, getExternalFilesDir("apks")?.path, getFileNameFromUrl(url)) - .build() - .setOnStartOrResumeListener{ installing = true } - .setOnProgressListener { progress -> - val mProgress = progress.currentBytes * 100 / progress.totalBytes - localBroadcastManager.sendBroadcast(Intent(HomeFragment.VANCED_DOWNLOADING).putExtra("progress", mProgress.toInt()).putExtra("file", getFileNameFromUrl(url))) - } - .start(object : OnDownloadListener { - override fun onDownloadComplete() { - when (type) { - "arch" -> downloadSplits("theme") - "theme" -> downloadSplits("lang") - "lang" -> { - count++ - if (count < lang?.count()!!) - downloadSplits("lang") - else - prepareInstall(variant!!) - } - } - } - - override fun onError(error: Error?) { - installing = false - Toast.makeText(this@VancedDownloadService, getString(R.string.error_downloading, "Vanced"), Toast.LENGTH_SHORT).show() - } - }) - } - } - - /* - private val receiver = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - val prefs = context?.getSharedPreferences("installPrefs", Context.MODE_PRIVATE) - val variant = PreferenceManager.getDefaultSharedPreferences(this@VancedDownloadService).getString("vanced_variant", "nonroot") - val lang = prefs?.getString("lang", "en") - if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) { - when (apkType) { - "arch" -> downloadSplits("theme") - "theme" -> downloadSplits("lang") - "lang" -> { - if (lang == "en") { - prepareInstall(variant!!) - //cancelNotif(channel, this@VancedDownloadService) - } else { - downloadSplits("enlang") - } - } - "enlang" -> { - prepareInstall(variant!!) - //cancelNotif(channel, this@VancedDownloadService) - } - } - } - } - } - */ - - private fun prepareInstall(variant: String) { - localBroadcastManager.sendBroadcast(Intent(HomeFragment.VANCED_INSTALLING)) - if (variant == "root") - startService(Intent(this, RootSplitInstallerService::class.java)) - else - startService(Intent(this, SplitInstaller::class.java)) - } - - override fun onBind(intent: Intent?): IBinder? { - return null - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloader.kt new file mode 100644 index 0000000000..2a1a8761d1 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloader.kt @@ -0,0 +1,255 @@ +package com.vanced.manager.core.downloader + +import android.content.Context +import android.content.SharedPreferences +import android.os.Build +import android.widget.Toast +import androidx.preference.PreferenceManager.getDefaultSharedPreferences +import com.downloader.Error +import com.downloader.OnDownloadListener +import com.downloader.PRDownloader +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.logEvent +import com.vanced.manager.R +import com.vanced.manager.core.App +import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.vancedProgress +import com.vanced.manager.utils.AppUtils.mutableInstall +import com.vanced.manager.utils.InternetTools +import com.vanced.manager.utils.InternetTools.baseUrl +import com.vanced.manager.utils.InternetTools.getFileNameFromUrl +import com.vanced.manager.utils.InternetTools.getObjectFromJson +import com.vanced.manager.utils.PackageHelper.getPkgVerCode +import com.vanced.manager.utils.PackageHelper.installVanced +import com.vanced.manager.utils.PackageHelper.installVancedRoot +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import java.io.File +import java.io.IOException +import java.security.MessageDigest + +object VancedDownloader { + + private var sha256Val: String? = null + + private lateinit var prefs: SharedPreferences + private lateinit var defPrefs: SharedPreferences + private lateinit var arch: String + private var installUrl: String? = null + private var variant: String? = null + private var theme: String? = null + private var lang: Array? = null + + private lateinit var themePath: String + + //private var downloadId: Long = 0 + //private var apkType: String = "arch" + private var count: Int = 0 + private var hashUrl = "" + + private const val yPkg = "com.google.android.youtube" + private var vancedVersionCode = 0 + private val vancedVersion by lazy { runBlocking { getObjectFromJson("$installUrl/vanced.json", "version") }} + + fun downloadVanced(context: Context){ + //registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) + File(context.getExternalFilesDir("apks")?.path as String).deleteRecursively() + defPrefs = getDefaultSharedPreferences(context) + installUrl = defPrefs.getString("install_url", baseUrl) + prefs = context.getSharedPreferences("installPrefs", Context.MODE_PRIVATE) + variant = defPrefs.getString("vanced_variant", "nonroot") + lang = prefs.getString("lang", "en")?.split(", ")?.toTypedArray() + theme = prefs.getString("theme", "dark") + themePath = "$installUrl/apks/v$vancedVersion/$variant/Theme" + hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json" + //newInstaller = defPrefs.getBoolean("new_installer", false) + arch = + when { + Build.SUPPORTED_ABIS.contains("x86") -> "x86" + Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a" + else -> "armeabi_v7a" + } + val app = context.applicationContext as App + vancedVersionCode = app.vanced?.int("versionCode") ?: 0 + downloadSplits(context) + } + + private fun downloadSplits( + context: Context, + type: String = "theme" + ) { + CoroutineScope(Dispatchers.IO).launch { + val url = + when (type) { + "theme" -> "$themePath/$theme.apk" + "arch" -> "$installUrl/apks/v$vancedVersion/$variant/Arch/split_config.$arch.apk" + "stock" -> "$themePath/stock.apk" + "dpi" -> "$themePath/dpi.apk" + "lang" -> "$installUrl/apks/v$vancedVersion/$variant/Language/split_config.${lang?.get(count)}.apk" + else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?") + } + + //apkType = type + //downloadId = download(url, "apks", getFileNameFromUrl(url), this@VancedDownloadService) + + vancedProgress.get()?.currentDownload = PRDownloader + .download(url, context.getExternalFilesDir("apks")?.path, getFileNameFromUrl(url)) + .build() + .setOnStartOrResumeListener { + mutableInstall.value = true + vancedProgress.get()?.downloadingFile?.set(context.getString(R.string.downloading_file, getFileNameFromUrl(url))) + vancedProgress.get()?.showDownloadBar?.set(true) + } + .setOnProgressListener { progress -> + vancedProgress.get()?.downloadProgress?.set((progress.currentBytes * 100 / progress.totalBytes).toInt()) + } + .setOnCancelListener { + mutableInstall.value = false + vancedProgress.get()?.showDownloadBar?.set(false) + } + .start(object : OnDownloadListener { + override fun onDownloadComplete() { + when (type) { + "theme" -> + if (variant == "root") { + if (validateTheme(context)) { + if(downloadStockCheck(context)) + downloadSplits(context, "arch") + else + prepareInstall(variant!!, context) + } else + downloadSplits(context, "theme") + } else + downloadSplits(context, "arch") + "arch" -> if (variant == "root") downloadSplits(context, "stock") else downloadSplits(context, "lang") + "stock" -> downloadSplits(context, "dpi") + "dpi" -> downloadSplits(context, "lang") + "lang" -> { + count++ + if (count < lang?.count()!!) + downloadSplits(context, "lang") + else + prepareInstall(variant!!, context) + } + + } + } + override fun onError(error: Error?) { + if (type == "lang") { + count++ + if (count < lang?.count()!!) + downloadSplits(context, "lang") + else + prepareInstall(variant!!, context) + } else { + mutableInstall.value = false + vancedProgress.get()?.showDownloadBar?.set(false) + Toast.makeText(context, context.getString(R.string.error_downloading, getFileNameFromUrl(url)), Toast.LENGTH_SHORT).show() + } + } + }) + } + } + + private fun downloadStockCheck(context: Context) :Boolean { + return try { + getPkgVerCode(yPkg,context.packageManager) != vancedVersionCode + } catch (e: Exception) { + true + } + } + + private suspend fun getSha256(obj: String, context: Context) { + sha256Val = InternetTools.getJsonString(hashUrl, obj, context) + } + + private fun validateTheme(context: Context): Boolean { + val themeS = context.getExternalFilesDir("apks")?.path + "/${theme}.apk" + val themeF = File(themeS) + runBlocking { getSha256(theme!!, context) } + return checkSHA256(sha256Val!!,themeF) + } + + /* + private val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + val prefs = context?.getSharedPreferences("installPrefs", Context.MODE_PRIVATE) + val variant = PreferenceManager.getDefaultSharedPreferences(this@VancedDownloadService).getString("vanced_variant", "nonroot") + val lang = prefs?.getString("lang", "en") + if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) { + when (apkType) { + "arch" -> downloadSplits(context, "theme") + "theme" -> downloadSplits(context, "lang") + "lang" -> { + if (lang == "en") { + prepareInstall(variant!!) + //cancelNotif(channel, this@VancedDownloadService) + } else { + downloadSplits(context, "enlang") + } + } + "enlang" -> { + prepareInstall(variant!!) + //cancelNotif(channel, this@VancedDownloadService) + } + } + } + } + } + */ + + private fun prepareInstall(variant: String, context: Context) { + vancedProgress.get()?.showDownloadBar?.set(false) + vancedProgress.get()?.showInstallCircle?.set(true) + FirebaseAnalytics.getInstance(context).logEvent(FirebaseAnalytics.Event.SELECT_ITEM) { + param("Vanced Variant", variant) + theme?.let { param("Vanced Theme", it) } + } + if (variant == "root") + installVancedRoot(context) + else + installVanced(context) + } + + private fun checkSHA256(sha256: String, updateFile: File?): Boolean { + return try { + val dataBuffer = updateFile!!.readBytes() + // Generate the checksum + val sum = generateChecksum(dataBuffer) + + sum == sha256 + } catch (e: Exception) { + e.printStackTrace() + false + } + } + + @Throws(IOException::class) + private fun generateChecksum(data: ByteArray): String { + try { + val digest: MessageDigest = MessageDigest.getInstance("SHA-256") + val hash: ByteArray = digest.digest(data) + return printableHexString(hash) + } catch (e: Exception) { + e.printStackTrace() + } + + return "" + } + + + private fun printableHexString(data: ByteArray): String { + // Create Hex String + val hexString: StringBuilder = StringBuilder() + for (aMessageDigest:Byte in data) { + var h: String = Integer.toHexString(0xFF and aMessageDigest.toInt()) + while (h.length < 2) + h = "0$h" + hexString.append(h) + } + return hexString.toString() + } + + +} diff --git a/app/src/main/java/com/vanced/manager/core/installer/AppInstaller.kt b/app/src/main/java/com/vanced/manager/core/installer/AppInstaller.kt deleted file mode 100644 index 9101f93fec..0000000000 --- a/app/src/main/java/com/vanced/manager/core/installer/AppInstaller.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.vanced.manager.core.installer - -import android.app.PendingIntent -import android.app.Service -import android.content.Intent -import android.content.pm.PackageInstaller -import android.os.IBinder -import java.io.FileInputStream -import java.io.InputStream - -class AppInstaller: Service() { - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - val callbackIntent = Intent(applicationContext, AppInstallerService::class.java) - val pendingIntent = PendingIntent.getService(applicationContext, 0, callbackIntent, 0) - val packageInstaller = packageManager.packageInstaller - val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) - params.setAppPackageName(intent?.getStringExtra("pkg")) - val sessionId = packageInstaller.createSession(params) - val session = packageInstaller.openSession(sessionId) - val inputStream: InputStream = FileInputStream(intent?.getStringExtra("path") as String) - val outputStream = session.openWrite("install", 0, -1) - val buffer = ByteArray(65536) - var c: Int - while (inputStream.read(buffer).also { c = it } != -1) { - outputStream.write(buffer, 0, c) - } - session.fsync(outputStream) - inputStream.close() - outputStream.close() - session.commit(pendingIntent.intentSender) - stopSelf() - return START_NOT_STICKY - } - - override fun onBind(intent: Intent?): IBinder? { - return null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/AppInstallerService.kt b/app/src/main/java/com/vanced/manager/core/installer/AppInstallerService.kt index 3d6e730e3c..1e89b45360 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/AppInstallerService.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/AppInstallerService.kt @@ -5,19 +5,19 @@ import android.content.Intent import android.content.pm.PackageInstaller import android.os.IBinder import android.util.Log -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import com.vanced.manager.ui.fragments.HomeFragment +import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.microgProgress +import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.musicProgress +import com.vanced.manager.utils.AppUtils.mutableInstall import com.vanced.manager.utils.AppUtils.sendFailure +import com.vanced.manager.utils.AppUtils.sendRefresh class AppInstallerService: Service() { - private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) } - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + val app = intent.getStringExtra("app") when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) { PackageInstaller.STATUS_PENDING_USER_ACTION -> { Log.d(TAG, "Requesting user confirmation for installation") - localBroadcastManager.sendBroadcast(Intent(HomeFragment.MICROG_INSTALLING)) val confirmationIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT) confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) try { @@ -28,12 +28,14 @@ class AppInstallerService: Service() { } PackageInstaller.STATUS_SUCCESS -> { Log.d(TAG, "Installation succeed") - with(localBroadcastManager) { - sendBroadcast(Intent(HomeFragment.REFRESH_HOME)) - sendBroadcast(Intent(HomeFragment.MICROG_INSTALLED)) - } + if (app == "microg") microgProgress.get()?.showInstallCircle?.set(false) else musicProgress.get()?.showInstallCircle?.set(false) + mutableInstall.postValue(false) + sendRefresh(this) + } + else -> { + sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), this) + if (app == "microg") microgProgress.get()?.showInstallCircle?.set(false) else musicProgress.get()?.showInstallCircle?.set(false) } - else -> sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), this) } stopSelf() return START_NOT_STICKY @@ -47,4 +49,4 @@ class AppInstallerService: Service() { const val TAG = "VMInstall" } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/vanced/manager/core/installer/AppUninstallerService.kt b/app/src/main/java/com/vanced/manager/core/installer/AppUninstallerService.kt index ab0e4e67af..22d5fa913c 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/AppUninstallerService.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/AppUninstallerService.kt @@ -5,14 +5,10 @@ import android.content.Intent import android.content.pm.PackageInstaller import android.os.IBinder import android.util.Log -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import com.vanced.manager.ui.fragments.HomeFragment -import kotlinx.coroutines.* +import com.vanced.manager.utils.AppUtils.sendRefresh class AppUninstallerService: Service() { - private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) } - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val pkgName = intent?.getStringExtra("pkg") when (intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) { @@ -25,20 +21,14 @@ class AppUninstallerService: Service() { } catch (e: Exception) { } } - //Delay broadcast until activity (and fragment) show up on screen + //Delay broadcast until activity (and fragment) show up on the screen PackageInstaller.STATUS_SUCCESS -> { - CoroutineScope(Dispatchers.IO).launch { - delay(500) - localBroadcastManager.sendBroadcast(Intent(HomeFragment.REFRESH_HOME)) - Log.d("VMpm", "Successfully uninstalled $pkgName") - } + sendRefresh(this) + Log.d("VMpm", "Successfully uninstalled $pkgName") } PackageInstaller.STATUS_FAILURE -> { - CoroutineScope(Dispatchers.IO).launch { - delay(500) - localBroadcastManager.sendBroadcast(Intent(HomeFragment.REFRESH_HOME)) - Log.d("VMpm", "Failed to uninstall $pkgName") - } + sendRefresh(this) + Log.d("VMpm", "Failed to uninstall $pkgName") } } stopSelf() @@ -49,4 +39,4 @@ class AppUninstallerService: Service() { return null } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/vanced/manager/core/installer/RootSplitInstallerService.kt b/app/src/main/java/com/vanced/manager/core/installer/RootSplitInstallerService.kt deleted file mode 100644 index 0f91b214f3..0000000000 --- a/app/src/main/java/com/vanced/manager/core/installer/RootSplitInstallerService.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.vanced.manager.core.installer - -import android.app.Service -import android.content.Intent -import android.os.Build -import android.os.IBinder -import android.util.Log -import androidx.annotation.Nullable -import androidx.annotation.WorkerThread -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import com.topjohnwu.superuser.Shell -import com.vanced.manager.ui.fragments.HomeFragment -import com.vanced.manager.utils.AppUtils.sendFailure -import com.vanced.manager.utils.FileInfo -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import java.io.File -import java.text.SimpleDateFormat -import java.util.* -import java.util.regex.Pattern -import kotlin.collections.ArrayList - -class RootSplitInstallerService: Service() { - - private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - Shell.getShell { - CoroutineScope(Dispatchers.IO).launch { - val apkFilesPath = getExternalFilesDir("apks")?.path - val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) } - if (fileInfoList != null) { - installSplitApkFiles(fileInfoList) - } - } - - } - stopSelf() - return START_NOT_STICKY - } - - @WorkerThread - private fun installSplitApkFiles(apkFiles: ArrayList) { - var sessionId: Int? - Log.d("AppLog", "installing split apk files:$apkFiles") - run { - val sessionIdResult = Shell.su("pm install-create -r -t").exec().out - val sessionIdPattern = Pattern.compile("(\\d+)") - val sessionIdMatcher = sessionIdPattern.matcher(sessionIdResult[0]) - sessionIdMatcher.find() - sessionId = Integer.parseInt(sessionIdMatcher.group(1)!!) - } - apkFiles.forEach { apkFile -> - Log.d("AppLog", "installing APK : ${apkFile.name} ${apkFile.fileSize} ") - val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name) - val process: Process = Runtime.getRuntime().exec(command) - val inputPipe = apkFile.getInputStream() - try { - process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) } - } catch (e: Exception) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - process.destroyForcibly() - else - process.destroy() - - throw RuntimeException(e) - } - process.waitFor() - } - Log.d("AppLog", "committing...") - val installResult = Shell.su("pm install-commit $sessionId").exec() - if (installResult.isSuccess) { - with(localBroadcastManager) { - sendBroadcast(Intent(HomeFragment.REFRESH_HOME)) - sendBroadcast(Intent(HomeFragment.VANCED_INSTALLED)) - } - } else - sendFailure(installResult.out, this) - } - - private fun SimpleDateFormat.tryParse(str: String) = try { - parse(str) != null - } catch (e: Exception) { - false - } - - @WorkerThread - private fun getFileInfoList(splitApkPath: String): ArrayList { - val parentFile = File(splitApkPath) - val result = ArrayList() - - if (parentFile.exists() && parentFile.canRead()) { - val listFiles = parentFile.listFiles() ?: return ArrayList() - for (file in listFiles) - result.add(FileInfo(file.name, file.length(), file)) - return result - } - val longLines = Shell.su("ls -l $splitApkPath").exec().out - val pattern = Pattern.compile(" +") - val formatter = SimpleDateFormat("HH:mm", Locale.getDefault()) - longLinesLoop@ for (line in longLines) { - val matcher = pattern.matcher(line) - for (i in 0 until 4) - if (!matcher.find()) - continue@longLinesLoop - val startSizeStr = matcher.end() - matcher.find() - val endSizeStr = matcher.start() - val fileSizeStr = line.substring(startSizeStr, endSizeStr) - while (true) { - val testTimeStr: String = - line.substring(matcher.end(), line.indexOf(' ', matcher.end())) - if (formatter.tryParse(testTimeStr)) { - //found time, so apk is next - val fileName = line.substring(line.indexOf(' ', matcher.end()) + 1) - if (fileName.endsWith("apk")) - result.add(FileInfo(fileName, fileSizeStr.toLong(), File(splitApkPath, fileName))) - break - } - matcher.find() - } - } - return result - } - - @Nullable - override fun onBind(intent: Intent?): IBinder? { - return null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/SplitInstaller.kt b/app/src/main/java/com/vanced/manager/core/installer/SplitInstaller.kt deleted file mode 100644 index c51e1afa75..0000000000 --- a/app/src/main/java/com/vanced/manager/core/installer/SplitInstaller.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.vanced.manager.core.installer - -import android.app.PendingIntent -import android.app.Service -import android.content.Context -import android.content.Intent -import android.content.pm.PackageInstaller -import android.os.IBinder -import android.util.Log -import java.io.* - -class SplitInstaller: Service() { - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - installSplitApk(this) - return START_NOT_STICKY - } - - override fun onBind(intent: Intent?): IBinder? { - return null - } - - private fun installSplitApk(context: Context): Int { - val apkFolderPath = context.getExternalFilesDir("apks")?.path + "/" - val nameSizeMap = HashMap() - var totalSize: Long = 0 - var sessionId = 0 - val folder = File(apkFolderPath) - val listOfFiles = folder.listFiles() - try { - for (listOfFile in listOfFiles!!) { - if (listOfFile.isFile) { - Log.d("AppLog", "installApk: " + listOfFile.name) - nameSizeMap[listOfFile.name] = listOfFile.length() - totalSize += listOfFile.length() - } - } - } catch (e: Exception) { - e.printStackTrace() - return -1 - } - val installParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) - installParams.setSize(totalSize) - try { - sessionId = context.packageManager.packageInstaller.createSession(installParams) - Log.d("AppLog","Success: created install session [$sessionId]") - for ((key, value) in nameSizeMap) { - doWriteSession(sessionId, apkFolderPath + key, value, key, context) - } - doCommitSession(sessionId, context) - Log.d("AppLog","Success") - } catch (e: IOException) { - e.printStackTrace() - } - return sessionId - } - - private fun doWriteSession(sessionId: Int, inPath: String?, sizeBytes: Long, splitName: String, context: Context): Int { - var inPathToUse = inPath - var sizeBytesToUse = sizeBytes - if ("-" == inPathToUse) { - inPathToUse = null - } else if (inPathToUse != null) { - val file = File(inPathToUse) - if (file.isFile) - sizeBytesToUse = file.length() - } - var session: PackageInstaller.Session? = null - var inputStream: InputStream? = null - var out: OutputStream? = null - try { - session = context.packageManager.packageInstaller.openSession(sessionId) - if (inPathToUse != null) { - inputStream = FileInputStream(inPathToUse) - } - out = session.openWrite(splitName, 0, sizeBytesToUse) - var total = 0 - val buffer = ByteArray(65536) - var c: Int - while (true) { - c = inputStream!!.read(buffer) - if (c == -1) - break - total += c - out.write(buffer, 0, c) - } - session.fsync(out) - Log.d("AppLog", "Success: streamed $total bytes") - return PackageInstaller.STATUS_SUCCESS - } catch (e: IOException) { - Log.e("AppLog", "Error: failed to write; " + e.message) - return PackageInstaller.STATUS_FAILURE - } finally { - try { - out?.close() - inputStream?.close() - session?.close() - } catch (e: IOException) { - e.printStackTrace() - } - } - } - - private fun doCommitSession(sessionId: Int, context: Context) { - var session: PackageInstaller.Session? = null - try { - try { - session = context.packageManager.packageInstaller.openSession(sessionId) - val callbackIntent = Intent(context.applicationContext, SplitInstallerService::class.java) - val pendingIntent = PendingIntent.getService(context.applicationContext, 0, callbackIntent, 0) - session.commit(pendingIntent.intentSender) - session.close() - Log.d("AppLog", "install request sent") - Log.d("AppLog", "doCommitSession: " + context.packageManager.packageInstaller.mySessions) - Log.d("AppLog", "doCommitSession: after session commit ") - } catch (e: IOException) { - e.printStackTrace() - } - - } finally { - session!!.close() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/SplitInstallerService.kt b/app/src/main/java/com/vanced/manager/core/installer/SplitInstallerService.kt index 362a8e0d7c..0e95fb3d37 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/SplitInstallerService.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/SplitInstallerService.kt @@ -5,18 +5,16 @@ import android.content.Intent import android.content.pm.PackageInstaller import android.os.IBinder import android.util.Log -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import com.vanced.manager.ui.fragments.HomeFragment +import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.vancedProgress +import com.vanced.manager.utils.AppUtils.mutableInstall import com.vanced.manager.utils.AppUtils.sendFailure +import com.vanced.manager.utils.AppUtils.sendRefresh class SplitInstallerService: Service() { - private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) } - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) { PackageInstaller.STATUS_PENDING_USER_ACTION -> { - localBroadcastManager.sendBroadcast(Intent(HomeFragment.VANCED_INSTALLING)) Log.d(TAG, "Requesting user confirmation for installation") val confirmationIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT) confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) @@ -27,13 +25,14 @@ class SplitInstallerService: Service() { } PackageInstaller.STATUS_SUCCESS -> { Log.d(TAG, "Installation succeed") - with(localBroadcastManager) { - sendBroadcast(Intent(HomeFragment.REFRESH_HOME)) - sendBroadcast(Intent(HomeFragment.VANCED_INSTALLED)) - } + vancedProgress.get()?.showInstallCircle?.set(false) + mutableInstall.postValue(false) + sendRefresh(this) + } + else -> { + sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), this) + vancedProgress.get()?.showInstallCircle?.set(false) } - else -> sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), this) - } stopSelf() return START_NOT_STICKY @@ -47,4 +46,4 @@ class SplitInstallerService: Service() { const val TAG = "VMInstall" } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/vanced/manager/model/DataModel.kt b/app/src/main/java/com/vanced/manager/model/DataModel.kt new file mode 100644 index 0000000000..79fc50c2a7 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/model/DataModel.kt @@ -0,0 +1,93 @@ +package com.vanced.manager.model + +import android.content.Context +import android.graphics.drawable.Drawable +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.databinding.ObservableBoolean +import androidx.databinding.ObservableField +import androidx.databinding.ObservableInt +import com.beust.klaxon.JsonObject +import com.vanced.manager.R +import com.vanced.manager.utils.PackageHelper.isPackageInstalled +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +open class DataModel( + private val jsonObject: JsonObject?, + app: String, + private val context: Context +) { + + private val appPkg = + when (app) { + "vanced" -> "com.vanced.android.youtube" + "vancedRoot" -> "com.google.android.youtube" + "microg" -> "com.mgoogle.android.gms" + else -> "com.vanced.android.apps.youtube.music" + } + + private val versionCode = ObservableInt() + private val installedVersionCode = ObservableInt() + + val isAppInstalled = ObservableBoolean() + val versionName = ObservableField() + val installedVersionName = ObservableField() + val buttonTxt = ObservableField() + val buttonIcon = ObservableField() + val changelog = ObservableField() + + fun fetch() = CoroutineScope(Dispatchers.IO).launch { + isAppInstalled.set(isPackageInstalled(appPkg, context.packageManager)) + versionName.set(jsonObject?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)) + installedVersionName.set(getPkgVersionName(isAppInstalled.get(), appPkg)) + versionCode.set(jsonObject?.int("versionCode") ?: 0) + installedVersionCode.set(getPkgVersionCode(isAppInstalled.get(), appPkg)) + buttonTxt.set(compareInt(installedVersionCode.get(), versionCode.get())) + buttonIcon.set(compareIntDrawable(installedVersionCode.get(), versionCode.get())) + changelog.set(jsonObject?.string("changelog") ?: context.getString(R.string.unavailable)) + } + + init { + fetch() + } + + private fun getPkgVersionName(toCheck: Boolean, pkg: String): String { + return if (toCheck) { + context.packageManager.getPackageInfo(pkg, 0).versionName.removeSuffix("-vanced") + } else { + context.getString(R.string.unavailable) + } + } + + @Suppress("DEPRECATION") + private fun getPkgVersionCode(toCheck: Boolean, pkg: String): Int { + return if (toCheck) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) + context.packageManager.getPackageInfo(pkg, 0).longVersionCode.and(0xFFFFFFFF).toInt() + else + context.packageManager.getPackageInfo(pkg, 0).versionCode + } else 0 + } + + private fun compareInt(int1: Int, int2: Int): String { + return when { + int1 == 0 -> context.getString(R.string.install) + int2 > int1 -> context.getString(R.string.update) + int2 == int1 || int1 > int2 -> context.getString(R.string.button_reinstall) + else -> context.getString(R.string.install) + } + + } + + private fun compareIntDrawable(int1: Int, int2: Int): Drawable? { + return when { + int1 == 0 -> ContextCompat.getDrawable(context, R.drawable.ic_download) + int2 > int1 -> ContextCompat.getDrawable(context, R.drawable.ic_update) + int2 == int1 -> ContextCompat.getDrawable(context, R.drawable.ic_done) + else -> ContextCompat.getDrawable(context, R.drawable.ic_download) + } + } + +} diff --git a/app/src/main/java/com/vanced/manager/model/ProgressModel.kt b/app/src/main/java/com/vanced/manager/model/ProgressModel.kt new file mode 100644 index 0000000000..ebbcfc7725 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/model/ProgressModel.kt @@ -0,0 +1,23 @@ +package com.vanced.manager.model + +import androidx.databinding.ObservableBoolean +import androidx.databinding.ObservableField +import androidx.databinding.ObservableInt + +open class ProgressModel { + + val downloadProgress = ObservableInt() + val downloadingFile = ObservableField() + val showInstallCircle = ObservableBoolean() + val showDownloadBar = ObservableBoolean() + + var currentDownload: Int = 0 + + init { + downloadProgress.set(0) + downloadingFile.set("") + showInstallCircle.set(false) + showDownloadBar.set(false) + } + +} diff --git a/app/src/main/java/com/vanced/manager/ui/MainActivity.kt b/app/src/main/java/com/vanced/manager/ui/MainActivity.kt index 506634080c..5381509abd 100644 --- a/app/src/main/java/com/vanced/manager/ui/MainActivity.kt +++ b/app/src/main/java/com/vanced/manager/ui/MainActivity.kt @@ -1,25 +1,30 @@ package com.vanced.manager.ui import android.content.Context +import android.content.Intent import android.os.Bundle import android.util.Log import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import androidx.navigation.NavDestination import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.setupWithNavController -import androidx.preference.PreferenceManager +import androidx.preference.PreferenceManager.getDefaultSharedPreferences import com.crowdin.platform.Crowdin import com.crowdin.platform.LoadingStateListener import com.google.firebase.messaging.FirebaseMessaging import com.vanced.manager.R import com.vanced.manager.databinding.ActivityMainBinding import com.vanced.manager.ui.dialogs.DialogContainer +import com.vanced.manager.ui.fragments.HomeFragmentDirections +import com.vanced.manager.ui.fragments.SettingsFragmentDirections import com.vanced.manager.ui.fragments.UpdateCheckFragment import com.vanced.manager.utils.AppUtils.installing import com.vanced.manager.utils.InternetTools +import com.vanced.manager.utils.LanguageContextWrapper import com.vanced.manager.utils.PackageHelper import com.vanced.manager.utils.ThemeHelper.setFinalTheme import kotlinx.coroutines.CoroutineScope @@ -28,8 +33,8 @@ import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { - private lateinit var binding: ActivityMainBinding - private val navHost by lazy { findNavController(R.id.bottom_nav_host) } + lateinit var binding: ActivityMainBinding + private val navHost by lazy { findNavController(R.id.nav_host) } private val loadingObserver = object : LoadingStateListener { val tag = "VMLocalisation" @@ -51,10 +56,8 @@ class MainActivity : AppCompatActivity() { with(binding) { lifecycleOwner = this@MainActivity setSupportActionBar(homeToolbar) - val appBarConfiguration = AppBarConfiguration(navHost.graph) - homeToolbar.setupWithNavController(navHost, appBarConfiguration) + homeToolbar.setupWithNavController(this@MainActivity.navHost, AppBarConfiguration(this@MainActivity.navHost.graph)) } - navHost.addOnDestinationChangedListener { _, currFrag: NavDestination, _ -> setDisplayHomeAsUpEnabled(currFrag.id != R.id.home_fragment) } @@ -63,6 +66,15 @@ class MainActivity : AppCompatActivity() { } + override fun onBackPressed() { + if (!navHost.popBackStack()) + finish() + } + + private fun setDisplayHomeAsUpEnabled(isNeeded: Boolean) { + binding.homeToolbar.navigationIcon = if (isNeeded) ContextCompat.getDrawable(this, R.drawable.ic_keyboard_backspace_black_24dp) else null + } + override fun onPause() { super.onPause() Crowdin.unregisterDataLoadingObserver(loadingObserver) @@ -75,24 +87,24 @@ class MainActivity : AppCompatActivity() { } override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (installing) { + if (installing.value!!) return false - } + when (item.itemId) { android.R.id.home -> { - onBackPressed() + onBackPressedDispatcher.onBackPressed() return true } R.id.toolbar_about -> { - navHost.navigate(R.id.toAboutFragment) + navHost.navigate(HomeFragmentDirections.toAboutFragment()) return true } R.id.toolbar_settings -> { - navHost.navigate(R.id.action_settingsFragment) + navHost.navigate(HomeFragmentDirections.toSettingsFragment()) return true } R.id.dev_settings -> { - navHost.navigate(R.id.toDevSettingsFragment) + navHost.navigate(SettingsFragmentDirections.toDevSettingsFragment()) return true } else -> super.onOptionsItemSelected(item) @@ -101,18 +113,14 @@ class MainActivity : AppCompatActivity() { return false } - private fun setDisplayHomeAsUpEnabled(isNeeded: Boolean) { - binding.homeToolbar.navigationIcon = if (isNeeded) getDrawable(R.drawable.ic_keyboard_backspace_black_24dp) else null - } - override fun attachBaseContext(newBase: Context) { - super.attachBaseContext(Crowdin.wrapContext(newBase)) + super.attachBaseContext(LanguageContextWrapper.wrap(newBase)) } private fun initDialogs() { - val prefs = PreferenceManager.getDefaultSharedPreferences(this) + val prefs = getDefaultSharedPreferences(this) val variant = prefs.getString("vanced_variant", "nonroot") - val showRootDialog = prefs.getBoolean("show_root_dialog", true) + prefs.getBoolean("show_root_dialog", true) when { prefs.getBoolean("firstStart", true) -> { @@ -124,9 +132,6 @@ class MainActivity : AppCompatActivity() { } !prefs.getBoolean("statement", true) -> DialogContainer.statementFalse(this) variant == "root" -> { - if (showRootDialog) - DialogContainer.showRootDialog(this) - if (PackageHelper.getPackageVersionName( "com.google.android.youtube", packageManager diff --git a/app/src/main/java/com/vanced/manager/ui/core/SlidingConstraintLayout.kt b/app/src/main/java/com/vanced/manager/ui/core/SlidingConstraintLayout.kt index 79f0c59f8e..2010b8dde7 100644 --- a/app/src/main/java/com/vanced/manager/ui/core/SlidingConstraintLayout.kt +++ b/app/src/main/java/com/vanced/manager/ui/core/SlidingConstraintLayout.kt @@ -6,8 +6,8 @@ import androidx.constraintlayout.widget.ConstraintLayout open class SlidingConstraintLayout : ConstraintLayout { - constructor(context: Context?) : super(context) - constructor(context: Context?, attrs: AttributeSet?) : super( + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super( context, attrs ) @@ -29,4 +29,4 @@ open class SlidingConstraintLayout : ConstraintLayout { (1).toFloat() x = newWidth } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/vanced/manager/ui/core/SlidingWidthLinearLayout.kt b/app/src/main/java/com/vanced/manager/ui/core/SlidingWidthLinearLayout.kt new file mode 100644 index 0000000000..58aea5b627 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/ui/core/SlidingWidthLinearLayout.kt @@ -0,0 +1,33 @@ +package com.vanced.manager.ui.core + +import android.content.Context +import android.util.AttributeSet +import android.widget.LinearLayout + +open class SlidingWidthLinearLayout: LinearLayout { + + constructor(context: Context?) : super(context) + constructor(context: Context?, attrs: AttributeSet?) : super( + context, + attrs + ) + + var xFraction: Float + get() { + val width = width + return if (width != 0) + x / getWidth() + else + x + } + set(xFraction) { + val width = width + val newWidth = + if (width > 0) + xFraction * width + else + (1).toFloat() + x = newWidth + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/ui/dialogs/DialogContainer.kt b/app/src/main/java/com/vanced/manager/ui/dialogs/DialogContainer.kt index 01b0d3708a..8496b48411 100644 --- a/app/src/main/java/com/vanced/manager/ui/dialogs/DialogContainer.kt +++ b/app/src/main/java/com/vanced/manager/ui/dialogs/DialogContainer.kt @@ -8,10 +8,10 @@ import androidx.core.content.ContextCompat.startActivity import androidx.preference.PreferenceManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.vanced.manager.R -import com.vanced.manager.core.downloader.VancedDownloadService -import com.vanced.manager.core.installer.SplitInstaller +import com.vanced.manager.core.downloader.VancedDownloader.downloadVanced import com.vanced.manager.utils.InternetTools.openUrl import com.vanced.manager.utils.MiuiHelper +import com.vanced.manager.utils.PackageHelper.installVanced object DialogContainer { @@ -63,38 +63,16 @@ object DialogContainer { setTitle("") setMessage("") setNegativeButton("") { dialog, _ -> - context.startService(Intent(context, VancedDownloadService::class.java)) + downloadVanced(context) dialog.dismiss() } setPositiveButton(context.getString(R.string.button_reinstall)) { dialog, _ -> - context.startService(Intent(context, SplitInstaller::class.java)) + installVanced(context) dialog.dismiss() } } } - fun showRootDialog(activity: Activity) { - MaterialAlertDialogBuilder(activity).apply { - setTitle(activity.getString(R.string.hold_on)) - setMessage(activity.getString(R.string.disable_signature)) - setNeutralButton(activity.getString(R.string.button_dismiss)) { dialog, _ -> - dialog.dismiss() - } - setPositiveButton(activity.getString(R.string.guide)) { _, _ -> - openUrl( - "https://lmgtfy.com/?q=andnixsh+apk+verification+disable", - R.color.Twitter, - activity - ) - } - setCancelable(false) - create() - show() - } - PreferenceManager.getDefaultSharedPreferences(activity).edit() - .putBoolean("show_root_dialog", false).apply() - } - //Easter Egg fun statementFalse(context: Context) { MaterialAlertDialogBuilder(context).apply { @@ -143,21 +121,34 @@ object DialogContainer { } } - fun launchVanced(activity: Activity) { + fun launchVanced(context: Context) { val intent = Intent() intent.component = - if (PreferenceManager.getDefaultSharedPreferences(activity).getString("vanced_variant", "nonroot") == "root") + if (PreferenceManager.getDefaultSharedPreferences(context).getString("vanced_variant", "nonroot") == "root") ComponentName("com.google.android.youtube", "com.google.android.youtube.HomeActivity") else ComponentName("com.vanced.android.youtube", "com.google.android.youtube.HomeActivity") + + MaterialAlertDialogBuilder(context).apply { + setTitle(context.getString(R.string.success)) + setMessage(context.getString(R.string.vanced_installed)) + setPositiveButton(context.getString(R.string.launch)) { _, _ -> + startActivity(context, intent, null) + } + setNegativeButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() } + create() + show() + } + } + + fun launchMusic(activity: Activity) { + val intent = Intent() + intent.component = ComponentName("com.vanced.android.youtube.music", "com.vanced.android.youtube.music.MusicActivity") MaterialAlertDialogBuilder(activity).apply { setTitle(activity.getString(R.string.success)) - setMessage(activity.getString(R.string.vanced_installed)) + setMessage(activity.getString(R.string.music_installed)) setPositiveButton(activity.getString(R.string.launch)) { _, _ -> - run { - startActivity(activity, intent, null) - activity.finish() - } + startActivity(activity, intent, null) } setNegativeButton(activity.getString(R.string.close)) { dialog, _ -> dialog.dismiss() } create() @@ -165,4 +156,4 @@ object DialogContainer { } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/vanced/manager/ui/events/Event.kt b/app/src/main/java/com/vanced/manager/ui/events/Event.kt new file mode 100644 index 0000000000..ccd9014053 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/ui/events/Event.kt @@ -0,0 +1,23 @@ +package com.vanced.manager.ui.events + +open class Event(private val content: T) { + + private var hasBeenHandled = false + + /** + * Returns the content and prevents its use again. + */ + fun getContentIfNotHandled(): T? { + return if (hasBeenHandled) { + null + } else { + hasBeenHandled = true + content + } + } + + /** + * Returns the content, even if it's already been handled. + */ + fun peekContent(): T = content +} diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/AboutFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/AboutFragment.kt index 9d1ea5ddfc..4780a55fff 100644 --- a/app/src/main/java/com/vanced/manager/ui/fragments/AboutFragment.kt +++ b/app/src/main/java/com/vanced/manager/ui/fragments/AboutFragment.kt @@ -18,6 +18,7 @@ import com.vanced.manager.ui.viewmodels.AboutViewModel class AboutFragment : Fragment() { private lateinit var binding: FragmentAboutBinding + private val viewModel: AboutViewModel by viewModels() private var count = 0 private var startMillSec: Long = 0 @@ -27,6 +28,7 @@ class AboutFragment : Fragment() { ): View? { requireActivity().title = getString(R.string.title_about) binding = DataBindingUtil.inflate(inflater, R.layout.fragment_about, container, false) + binding.viewModel = viewModel return binding.root } @@ -35,9 +37,6 @@ class AboutFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val viewModel: AboutViewModel by viewModels() - binding.viewModel = viewModel - view.setOnTouchListener { _, event: MotionEvent -> val eventAction = event.action diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/ChosenPreferenceDialogFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/ChosenPreferenceDialogFragment.kt index fa73d27021..7eeab6f3fc 100644 --- a/app/src/main/java/com/vanced/manager/ui/fragments/ChosenPreferenceDialogFragment.kt +++ b/app/src/main/java/com/vanced/manager/ui/fragments/ChosenPreferenceDialogFragment.kt @@ -19,8 +19,8 @@ class ChosenPreferenceDialogFragment : DialogFragment() { private lateinit var binding: FragmentChosenPreferencesBinding override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? ): View? { dialog?.window?.apply { setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) } binding = DataBindingUtil.inflate(inflater, R.layout.fragment_chosen_preferences, container, false) @@ -40,6 +40,9 @@ class ChosenPreferenceDialogFragment : DialogFragment() { newPrefs.add(loc.getDisplayLanguage(loc).capitalize(Locale.ROOT)) } } + + + binding.chosenTheme.text = requireActivity().getString(R.string.chosen_theme, prefs.getString("theme", "dark")?.capitalize(Locale.ROOT)) binding.chosenLang.text = requireActivity().getString(R.string.chosen_lang, newPrefs.joinToString()) diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/DevSettingsFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/DevSettingsFragment.kt index 2412848f5a..89e0db4431 100644 --- a/app/src/main/java/com/vanced/manager/ui/fragments/DevSettingsFragment.kt +++ b/app/src/main/java/com/vanced/manager/ui/fragments/DevSettingsFragment.kt @@ -45,14 +45,16 @@ class DevSettingsFragment: PreferenceFragmentCompat() { true } - val archPref: Preference? = findPreference("device_arch") val supportedAbis: Array = Build.SUPPORTED_ABIS - if (supportedAbis.contains("arm64-v8a") || supportedAbis.contains("x86_64")) { - archPref?.summary = "64bit" - } else { - archPref?.summary = "32bit" - } + findPreference("device_arch")?.summary = + if (supportedAbis.contains("arm64-v8a") || supportedAbis.contains("x86_64")) { + "64bit" + } else { + "32bit" + } + + findPreference("device_os")?.summary = "${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})" val forceUpdate: Preference? = findPreference("force_update") forceUpdate?.setOnPreferenceClickListener { diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/HomeFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/HomeFragment.kt index 70a4c08ad2..4d12156268 100644 --- a/app/src/main/java/com/vanced/manager/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/com/vanced/manager/ui/fragments/HomeFragment.kt @@ -1,10 +1,12 @@ package com.vanced.manager.ui.fragments -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.os.Bundle -import android.util.Log import android.view.* -import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.AnimationUtils import android.widget.Toast import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment @@ -12,28 +14,42 @@ import androidx.fragment.app.viewModels import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.navigation.findNavController import androidx.preference.PreferenceManager.getDefaultSharedPreferences -import com.google.android.material.snackbar.Snackbar +import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import com.topjohnwu.superuser.Shell import com.vanced.manager.R -import com.vanced.manager.adapter.* -import com.vanced.manager.core.downloader.MicrogDownloadService -import com.vanced.manager.core.downloader.VancedDownloadService +import com.vanced.manager.adapter.VariantAdapter import com.vanced.manager.databinding.FragmentHomeBinding -import com.vanced.manager.ui.MainActivity import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder -import com.vanced.manager.ui.dialogs.DialogContainer.launchVanced import com.vanced.manager.ui.viewmodels.HomeViewModel -import com.vanced.manager.utils.AppUtils.installing -import com.vanced.manager.utils.PackageHelper +import com.vanced.manager.utils.AppUtils -class HomeFragment : Fragment(), View.OnClickListener { +open class HomeFragment : Fragment() { private lateinit var binding: FragmentHomeBinding - private var isExpanded: Boolean = false private val viewModel: HomeViewModel by viewModels() private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) } + private val tabListener = object : TabLayout.OnTabSelectedListener { + + override fun onTabSelected(tab: TabLayout.Tab) { + if (tab.position == 1 && !Shell.rootAccess()) { + Toast.makeText(requireActivity(), getString(R.string.root_not_granted), Toast.LENGTH_SHORT).show() + } + val variant = if (tab.position == 1) "root" else "nonroot" + getDefaultSharedPreferences(requireActivity()).edit().putString("vanced_variant", variant).apply() + } + + override fun onTabUnselected(tab: TabLayout.Tab) { + return + } + + override fun onTabReselected(tab: TabLayout.Tab) { + return + } + + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -46,222 +62,75 @@ class HomeFragment : Fragment(), View.OnClickListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.viewModel = viewModel - - val variantPref = getDefaultSharedPreferences(requireActivity()).getString("vanced_variant", "nonroot") + viewModel.navigateDestination.observe(viewLifecycleOwner, { + val content = it.getContentIfNotHandled() + if(content != null){ + view.findNavController().navigate(content) + } + }) with(binding) { - rootSwitch.setOnClickListener(this@HomeFragment) - nonrootSwitch.setOnClickListener(this@HomeFragment) - - includeVancedLayout.vancedInstallbtn.setOnClickListener(this@HomeFragment) - includeVancedLayout.vancedUninstallbtn.setOnClickListener(this@HomeFragment) - includeMicrogLayout.microgInstallbtn.setOnClickListener(this@HomeFragment) - includeMicrogLayout.microgUninstallbtn.setOnClickListener(this@HomeFragment) - includeChangelogsLayout.changelogButton.setOnClickListener(this@HomeFragment) - } - - binding.includeVancedLayout.vancedCard.setOnLongClickListener { - versionToast("Vanced", viewModel.vancedInstalledVersion.get()) - true - } - - binding.includeMicrogLayout.microgCard.setOnLongClickListener { - versionToast("MicroG", viewModel.microgInstalledVersion.get()) - true - } - - with(binding.includeChangelogsLayout) { - viewpager.adapter = if (variantPref == "root") SectionPageRootAdapter(this@HomeFragment) else SectionPageAdapter(this@HomeFragment) - TabLayoutMediator(tablayout, viewpager) { tab, position -> - if (variantPref == "root") - when (position) { - 0 -> tab.text = "Vanced" - 1 -> tab.text = "Manager" - } - else - when (position) { - 0 -> tab.text = "Vanced" - 1 -> tab.text = "MicroG" - 2 -> tab.text = "Manager" - } - + mainViewpager.adapter = VariantAdapter(viewModel, requireActivity()) + TabLayoutMediator(mainTablayout, mainViewpager) { tab, position -> + val variants = arrayOf("nonroot", "root") + tab.text = variants[position] }.attach() + mainTablayout.getTabAt(if (getDefaultSharedPreferences(requireActivity()).getString("vanced_variant", "nonroot") == "root") 1 else 0)?.select() } - } - - override fun onClick(v: View?) { - val prefs = requireActivity().getSharedPreferences("installPrefs", Context.MODE_PRIVATE) - val variant = getDefaultSharedPreferences(requireActivity()).getString("vanced_variant", "nonroot") - val vancedPkgName = - if (variant == "root") { - "com.google.android.youtube" - } else { - "com.vanced.android.youtube" - } + AppUtils.installing.observe(viewLifecycleOwner, { value -> + if (value) hideTab() else showTab() + }) - when (v?.id) { - R.id.vanced_installbtn -> { - if (!installing) { - if (!viewModel.fetching.get()!!) { - if (variant == "nonroot" && !viewModel.microgInstalled.get()!!) { - Snackbar.make( - binding.homeRefresh, - R.string.no_microg, - Snackbar.LENGTH_LONG - ) - .setAction(R.string.install) { - requireActivity().startService( - Intent( - requireActivity(), - MicrogDownloadService::class.java - ) - ) - }.show() - } else { - if (prefs?.getBoolean("valuesModified", false)!!) { - requireActivity().startService( - Intent( - requireActivity(), - VancedDownloadService::class.java - ) - ) - } else { - view?.findNavController()?.navigate(R.id.toInstallThemeFragment) - } - } - } - } else - Toast.makeText(requireActivity(), R.string.installation_wait, Toast.LENGTH_SHORT).show() - } - R.id.microg_installbtn -> { - if (!installing) - requireActivity().startService(Intent(requireActivity(), MicrogDownloadService::class.java)) - else - Toast.makeText(requireActivity(), R.string.installation_wait, Toast.LENGTH_SHORT).show() - } - R.id.microg_uninstallbtn -> PackageHelper.uninstallApk("com.mgoogle.android.gms", requireActivity()) - R.id.vanced_uninstallbtn -> PackageHelper.uninstallApk(vancedPkgName, requireActivity()) - R.id.nonroot_switch -> writeToVariantPref("nonroot", R.anim.slide_in_left, R.anim.slide_out_right) - R.id.root_switch -> - if (Shell.rootAccess()) { - writeToVariantPref("root", R.anim.slide_in_right, R.anim.slide_out_left) - } else { - writeToVariantPref("nonroot", R.anim.slide_in_left, R.anim.slide_out_right) - Toast.makeText(requireActivity(), activity?.getString(R.string.root_not_granted), Toast.LENGTH_SHORT).show() - } - R.id.changelog_button -> cardExpandCollapse() - } - } - - private fun writeToVariantPref(variant: String, animIn: Int, animOut: Int) { - val prefs = getDefaultSharedPreferences(activity) - if (prefs.getString("vanced_variant", "nonroot") != variant) { - prefs.edit().putString("vanced_variant", variant).apply() - startActivity(Intent(activity, MainActivity::class.java)) - activity?.overridePendingTransition(animIn, animOut) - activity?.finish() - } else - Log.d("VMVariant", "$variant is already selected") - } - - private fun versionToast(name: String, app: String?) { - val clip = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clip.setPrimaryClip(ClipData.newPlainText(name, app)) - Toast.makeText(activity, getString(R.string.version_toast, name), Toast.LENGTH_LONG).show() - } - - private fun cardExpandCollapse() { - with(binding.includeChangelogsLayout) { - viewpager.visibility = if (isExpanded) View.GONE else View.VISIBLE - tablayout.visibility = if (isExpanded) View.GONE else View.VISIBLE - changelogButton.animate().apply { - rotation(if (isExpanded) 0F else 180F) - interpolator = AccelerateDecelerateInterpolator() - } - isExpanded = !isExpanded - } } - + override fun onPause() { - localBroadcastManager.unregisterReceiver(broadcastReceiver) super.onPause() + localBroadcastManager.unregisterReceiver(broadcastReceiver) + binding.mainTablayout.removeOnTabSelectedListener(tabListener) } override fun onResume() { super.onResume() registerReceivers() + binding.mainTablayout.addOnTabSelectedListener(tabListener) } private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { - VANCED_DOWNLOADING -> { - with(binding.includeVancedLayout) { - vancedDownloading.visibility = View.VISIBLE - vancedDownloading.progress = intent.getIntExtra("progress", 0) - vancedDownloadingTxt.visibility = View.VISIBLE - vancedDownloadingTxt.text = requireActivity().getString(R.string.downloading_file, intent.getStringExtra("file")) - } - } - MICROG_DOWNLOADING -> { - with(binding.includeMicrogLayout) { - microgDownloading.visibility = View.VISIBLE - microgDownloading.progress = intent.getIntExtra("progress", 0) - microgDownloadingTxt.visibility = View.VISIBLE - microgDownloadingTxt.text = requireActivity().getString(R.string.downloading_file, "microg.apk") - } - } - MICROG_INSTALLING -> { - with (binding.includeMicrogLayout) { - microgDownloading.visibility = View.GONE - microgDownloadingTxt.visibility = View.GONE - microgInstalling.visibility = View.VISIBLE - } - } - VANCED_INSTALLING -> { - with (binding.includeVancedLayout) { - vancedDownloading.visibility = View.GONE - vancedDownloadingTxt.visibility = View.GONE - vancedInstalling.visibility = View.VISIBLE - } - } - VANCED_INSTALLED -> { - binding.includeVancedLayout.vancedInstalling.visibility = View.GONE - launchVanced(requireActivity()) - installing = false - } - MICROG_INSTALLED -> { - binding.includeMicrogLayout.microgInstalling.visibility = View.GONE - installing = false - } - INSTALL_FAILED -> { - with(binding) { - includeMicrogLayout.microgInstalling.visibility = View.GONE - includeVancedLayout.vancedInstalling.visibility = View.GONE - } - installAlertBuilder(intent.getStringExtra("errorMsg") as String, requireActivity()) - installing = false - } - REFRESH_HOME -> { - Log.d("VMRefresh", "Refreshing home page") - viewModel.fetchData() - } + INSTALL_FAILED -> installAlertBuilder(intent.getStringExtra("errorMsg") as String, requireActivity()) + REFRESH_HOME -> viewModel.fetchData() } } } + private fun hideTab() { + val tabHide = AnimationUtils.loadAnimation(requireActivity(), R.anim.tablayout_exit) + with(binding) { + if (mainTablayout.visibility != View.GONE) { + mainTablayout.startAnimation(tabHide) + mainTablayout.visibility = View.GONE + } + mainViewpager.isUserInputEnabled = false + } + } + + private fun showTab() { + val tabShow = AnimationUtils.loadAnimation(requireActivity(), R.anim.tablayout_enter) + with(binding) { + if (mainTablayout.visibility != View.VISIBLE) { + mainTablayout.visibility = View.VISIBLE + mainTablayout.startAnimation(tabShow) + } + mainViewpager.isUserInputEnabled = true + } + } + private fun registerReceivers() { val intentFilter = IntentFilter() - intentFilter.addAction(VANCED_DOWNLOADING) - intentFilter.addAction(MICROG_DOWNLOADING) - intentFilter.addAction(VANCED_INSTALLING) - intentFilter.addAction(MICROG_INSTALLING) - intentFilter.addAction(VANCED_INSTALLED) - intentFilter.addAction(MICROG_INSTALLED) - intentFilter.addAction(REFRESH_HOME) intentFilter.addAction(INSTALL_FAILED) + intentFilter.addAction(REFRESH_HOME) localBroadcastManager.registerReceiver(broadcastReceiver, intentFilter) } @@ -270,13 +139,7 @@ class HomeFragment : Fragment(), View.OnClickListener { super.onCreateOptionsMenu(menu, inflater) } - companion object { - const val VANCED_DOWNLOADING = "vanced_downloading" - const val MICROG_DOWNLOADING = "microg_downloading" - const val VANCED_INSTALLING = "vanced_installing" - const val MICROG_INSTALLING = "microg_installing" - const val VANCED_INSTALLED = "vanced_installed" - const val MICROG_INSTALLED = "microg_installed" + companion object { const val INSTALL_FAILED = "install_failed" const val REFRESH_HOME = "refresh_home" } diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/ManagerChangelogFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/ManagerChangelogFragment.kt deleted file mode 100644 index 644c8d7c5f..0000000000 --- a/app/src/main/java/com/vanced/manager/ui/fragments/ManagerChangelogFragment.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.vanced.manager.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.fragment.app.Fragment -import com.vanced.manager.R -import com.vanced.manager.utils.InternetTools -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -class ManagerChangelogFragment : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_manager_changelog, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - CoroutineScope(Dispatchers.Main).launch { - val changelog = InternetTools.getObjectFromJson("https://x1nto.github.io/VancedFiles/manager.json", "changelog") - view.findViewById(R.id.manager_changelog).text = changelog - } - } -} diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/MicrogChangelogFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/MicrogChangelogFragment.kt deleted file mode 100644 index 13dfb9452c..0000000000 --- a/app/src/main/java/com/vanced/manager/ui/fragments/MicrogChangelogFragment.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.vanced.manager.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.fragment.app.Fragment -import androidx.preference.PreferenceManager -import com.vanced.manager.R -import com.vanced.manager.utils.InternetTools -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -class MicrogChangelogFragment : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_microg_changelog, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - CoroutineScope(Dispatchers.Main).launch { - val baseUrl = PreferenceManager.getDefaultSharedPreferences(activity).getString("install_url", InternetTools.baseUrl) - view.findViewById(R.id.microg_changelog).text = InternetTools.getObjectFromJson("$baseUrl/microg.json", "changelog") - } - } -} diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/SettingsFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/SettingsFragment.kt index 514acbe8d5..cf07ed0d52 100644 --- a/app/src/main/java/com/vanced/manager/ui/fragments/SettingsFragment.kt +++ b/app/src/main/java/com/vanced/manager/ui/fragments/SettingsFragment.kt @@ -5,8 +5,14 @@ import android.view.Menu import android.view.MenuInflater import android.widget.Toast import androidx.preference.* +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.messaging.FirebaseMessaging +import com.google.firebase.perf.FirebasePerformance +import com.vanced.manager.BuildConfig.MANAGER_LANGUAGES +import com.vanced.manager.BuildConfig.MANAGER_LANGUAGE_NAMES import com.vanced.manager.R +import com.vanced.manager.utils.LanguageHelper.getLanguageFormat import java.io.File class SettingsFragment : PreferenceFragmentCompat() { @@ -46,6 +52,13 @@ class SettingsFragment : PreferenceFragmentCompat() { } } + findPreference("firebase_analytics")?.setOnPreferenceChangeListener { _, newValue -> + FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(newValue as Boolean) + FirebasePerformance.getInstance().isPerformanceCollectionEnabled = newValue + FirebaseAnalytics.getInstance(requireActivity()).setAnalyticsCollectionEnabled(newValue) + true + } + val themePref = preferenceScreen.sharedPreferences.getString("theme_mode", "Follow System") findPreference("theme_mode")?.apply { summary = when (themePref) { @@ -82,6 +95,21 @@ class SettingsFragment : PreferenceFragmentCompat() { } } + val langPref = preferenceScreen.sharedPreferences.getString("manager_lang", "System Default") + preferenceScreen.findPreference("manager_lang")?.apply { + summary = langPref?.let { getLanguageFormat(requireActivity(), it) } + entries = arrayOf(getString(R.string.system_default)) + MANAGER_LANGUAGE_NAMES + entryValues = arrayOf("System Default") + MANAGER_LANGUAGES + + setOnPreferenceChangeListener { _, newValue -> + if (langPref != newValue) { + requireActivity().recreate() + return@setOnPreferenceChangeListener true + } + false + } + } + findPreference("vanced_chosen_modes")?.setOnPreferenceClickListener { ChosenPreferenceDialogFragment().show(childFragmentManager.beginTransaction(), "Chosen Preferences") true diff --git a/app/src/main/java/com/vanced/manager/ui/fragments/UpdateCheckFragment.kt b/app/src/main/java/com/vanced/manager/ui/fragments/UpdateCheckFragment.kt index 69888bb2a1..e899004db3 100644 --- a/app/src/main/java/com/vanced/manager/ui/fragments/UpdateCheckFragment.kt +++ b/app/src/main/java/com/vanced/manager/ui/fragments/UpdateCheckFragment.kt @@ -45,6 +45,8 @@ class UpdateCheckFragment : DialogFragment() { checkUpdates() view.findViewById