Improved mirror fallback and added a check for broken microg

This commit is contained in:
X1nto 2021-01-27 20:58:18 +04:00
parent 196794a79b
commit 1c62fb45c1
14 changed files with 101 additions and 61 deletions

View File

@ -39,7 +39,6 @@ class AppListAdapter(
val dataModel = if (isRoot) rootDataModels[position] else dataModels[position] val dataModel = if (isRoot) rootDataModels[position] else dataModels[position]
with(binding) { with(binding) {
appName.text = dataModel?.appName appName.text = dataModel?.appName
appInstallButton.text = dataModel?.buttonTxt?.value
dataModel?.buttonTxt?.observe(lifecycleOwner) { dataModel?.buttonTxt?.observe(lifecycleOwner) {
appInstallButton.text = it appInstallButton.text = it
} }
@ -60,11 +59,9 @@ class AppListAdapter(
appUninstall.isVisible = it appUninstall.isVisible = it
appLaunch.isVisible = it appLaunch.isVisible = it
} }
appRemoteVersion.text = dataModel?.versionName?.value
dataModel?.versionName?.observe(lifecycleOwner) { dataModel?.versionName?.observe(lifecycleOwner) {
appRemoteVersion.text = it appRemoteVersion.text = it
} }
appInstalledVersion.text = dataModel?.installedVersionName?.value
dataModel?.installedVersionName?.observe(lifecycleOwner) { dataModel?.installedVersionName?.observe(lifecycleOwner) {
appInstalledVersion.text = it appInstalledVersion.text = it
} }

View File

@ -38,7 +38,7 @@ object VancedDownloader {
private var downloadPath: String? = null private var downloadPath: String? = null
private var folderName: String? = null private var folderName: String? = null
fun downloadVanced(context: Context) { fun downloadVanced(context: Context, version: String?) {
defPrefs = context.defPrefs defPrefs = context.defPrefs
prefs = context.installPrefs prefs = context.installPrefs
variant = defPrefs.managerVariant variant = defPrefs.managerVariant
@ -49,7 +49,7 @@ object VancedDownloader {
lang = it.split(", ").toMutableList() lang = it.split(", ").toMutableList()
} }
theme = prefs.theme theme = prefs.theme
vancedVersion = defPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf("")) vancedVersion = version ?: defPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf(""))
themePath = "$baseInstallUrl/apks/v$vancedVersion/$variant/Theme" themePath = "$baseInstallUrl/apks/v$vancedVersion/$variant/Theme"
hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json" hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json"
arch = getArch() arch = getArch()

View File

@ -3,19 +3,17 @@ package com.vanced.manager.model
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.beust.klaxon.JsonObject import com.beust.klaxon.JsonObject
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.utils.PackageHelper.isPackageInstalled import com.vanced.manager.utils.PackageHelper.isPackageInstalled
import com.vanced.manager.utils.lifecycleOwner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
open class DataModel( open class DataModel(
private val jsonObject: LiveData<JsonObject?>, private val jsonObject: LiveData<JsonObject?>,
private val context: Context, private val context: Context,
lifecycleOwner: LifecycleOwner,
val appPkg: String, val appPkg: String,
val appName: String, val appName: String,
val appIcon: Drawable?, val appIcon: Drawable?,
@ -30,33 +28,27 @@ open class DataModel(
val buttonTxt = MutableLiveData<String>() val buttonTxt = MutableLiveData<String>()
val changelog = MutableLiveData<String>() val changelog = MutableLiveData<String>()
private fun fetch() = CoroutineScope(Dispatchers.IO).launch { private fun fetch() {
val jobj = jsonObject.value val jobj = jsonObject.value
isAppInstalled.postValue(isAppInstalled(appPkg)) isAppInstalled.value = isAppInstalled(appPkg)
versionCode.postValue(jobj?.int("versionCode") ?: 0) versionCode.value = jobj?.int("versionCode") ?: 0
versionName.postValue(jobj?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)) versionName.value = jobj?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)
changelog.postValue(jobj?.string("changelog") ?: context.getString(R.string.unavailable)) changelog.value = jobj?.string("changelog") ?: context.getString(R.string.unavailable)
} }
init { init {
fetch() fetch()
with(context.lifecycleOwner()) { with(lifecycleOwner) {
this?.let { jsonObject.observe(this) {
jsonObject.observe(it) { fetch()
fetch()
}
} }
this?.let { isAppInstalled.observe(this) {
isAppInstalled.observe(it) { installedVersionCode.value = getPkgVersionCode(appPkg)
installedVersionCode.value = getPkgVersionCode(appPkg) installedVersionName.value = getPkgVersionName(appPkg)
installedVersionName.value = getPkgVersionName(appPkg)
}
} }
this?.let { versionCode.observe(this) { versionCode ->
versionCode.observe(it) { versionCode -> installedVersionCode.observe(this) { installedVersionCode ->
installedVersionCode.observe(it) { installedVersionCode -> buttonTxt.value = compareInt(installedVersionCode, versionCode)
buttonTxt.value = compareInt(installedVersionCode, versionCode)
}
} }
} }
} }

View File

@ -2,6 +2,7 @@ package com.vanced.manager.model
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.beust.klaxon.JsonObject import com.beust.klaxon.JsonObject
import com.vanced.manager.utils.PackageHelper import com.vanced.manager.utils.PackageHelper
@ -9,6 +10,7 @@ import com.vanced.manager.utils.PackageHelper
class RootDataModel( class RootDataModel(
jsonObject: LiveData<JsonObject?>, jsonObject: LiveData<JsonObject?>,
context: Context, context: Context,
lifecycleOwner: LifecycleOwner,
appPkg: String, appPkg: String,
appName: String, appName: String,
appIcon: Drawable?, appIcon: Drawable?,
@ -19,7 +21,7 @@ class RootDataModel(
//Ironic, isn't it? //Ironic, isn't it?
private val scriptName: String? private val scriptName: String?
): DataModel( ): DataModel(
jsonObject, context, appPkg, appName, appIcon jsonObject, context, lifecycleOwner, appPkg, appName, appIcon
) { ) {
override fun isAppInstalled(pkg: String): Boolean { override fun isAppInstalled(pkg: String): Boolean {

View File

@ -26,14 +26,17 @@ class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
const val CLOSE_DIALOG = "close_dialog" const val CLOSE_DIALOG = "close_dialog"
private const val TAG_APP = "TAG_APP" private const val TAG_APP = "TAG_APP"
private const val TAG_VERSION = "TAG_VERSION"
private const val TAG_INSTALLING = "TAG_INSTALLING" private const val TAG_INSTALLING = "TAG_INSTALLING"
fun newInstance( fun newInstance(
app: String, app: String,
version: String? = null,
installing: Boolean = false installing: Boolean = false
): AppDownloadDialog = AppDownloadDialog().apply { ): AppDownloadDialog = AppDownloadDialog().apply {
arguments = Bundle().apply { arguments = Bundle().apply {
putString(TAG_APP, app) putString(TAG_APP, app)
putString(TAG_VERSION, version)
putBoolean(TAG_INSTALLING, installing) putBoolean(TAG_INSTALLING, installing)
} }
} }
@ -70,7 +73,7 @@ class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
appDownloadHeader.text = app appDownloadHeader.text = app
if (arguments?.getBoolean(TAG_INSTALLING) == false) { if (arguments?.getBoolean(TAG_INSTALLING) == false) {
when (app) { when (app) {
getString(R.string.vanced) -> downloadVanced(requireContext()) getString(R.string.vanced) -> downloadVanced(requireContext(), arguments?.getString(TAG_VERSION))
getString(R.string.music) -> downloadMusic(requireContext()) getString(R.string.music) -> downloadMusic(requireContext())
getString(R.string.microg) -> downloadMicrog(requireContext()) getString(R.string.microg) -> downloadMicrog(requireContext())
} }

View File

@ -39,13 +39,12 @@ class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVaria
variantSave.setOnClickListener { variantSave.setOnClickListener {
val newPref = variantRadiogroup.getCheckedButtonTag() val newPref = variantRadiogroup.getCheckedButtonTag()
if (variant != newPref) { if (variant != newPref) {
with (prefs.managerVariant) { prefs.managerVariant =
if (newPref == "root" && Shell.rootAccess()) { if (newPref == "root" && Shell.rootAccess()) {
this == "root" "root"
} else { } else {
this == "nonroot" "nonroot"
} }
}
dismiss() dismiss()
requireActivity().recreate() requireActivity().recreate()
} else { } else {

View File

@ -34,7 +34,7 @@ class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPrefe
private fun bindData() { private fun bindData() {
with(binding) { with(binding) {
val musicVersionsConv = musicVersions.value?.value?.reversed()?.convertToAppVersions() val musicVersionsConv = musicVersions.value?.value?.convertToAppVersions()
musicInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.music)) musicInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.music))
musicVersion.text = getString(R.string.chosen_version, prefs.getString("music_version", "latest")) musicVersion.text = getString(R.string.chosen_version, prefs.getString("music_version", "latest"))
openVersionSelector.setOnClickListener { openVersionSelector.setOnClickListener {

View File

@ -9,9 +9,7 @@ import android.widget.TextView
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.vanced.manager.core.ui.base.BindingDialogFragment import com.vanced.manager.core.ui.base.BindingDialogFragment
import com.vanced.manager.databinding.DialogCustomUrlBinding import com.vanced.manager.databinding.DialogCustomUrlBinding
import com.vanced.manager.utils.baseUrl import com.vanced.manager.utils.*
import com.vanced.manager.utils.defPrefs
import com.vanced.manager.utils.loadJson
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class URLChangeDialog : BindingDialogFragment<DialogCustomUrlBinding>() { class URLChangeDialog : BindingDialogFragment<DialogCustomUrlBinding>() {
@ -61,6 +59,7 @@ class URLChangeDialog : BindingDialogFragment<DialogCustomUrlBinding>() {
private fun saveUrl(url: String) { private fun saveUrl(url: String) {
lifecycleScope.launch { lifecycleScope.launch {
prefs.installUrl = url prefs.installUrl = url
baseInstallUrl = url
loadJson(requireActivity()) loadJson(requireActivity())
dismiss() dismiss()
} }

View File

@ -3,11 +3,14 @@ package com.vanced.manager.ui.dialogs
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
import com.vanced.manager.core.ui.ext.showDialog import com.vanced.manager.core.ui.ext.showDialog
import com.vanced.manager.databinding.DialogVancedPreferencesBinding import com.vanced.manager.databinding.DialogVancedPreferencesBinding
import com.vanced.manager.utils.* import com.vanced.manager.utils.*
import com.vanced.manager.utils.AppUtils.vancedPkg
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
import java.util.* import java.util.*
class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPreferencesBinding>() { class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPreferencesBinding>() {
@ -39,7 +42,7 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
val loc = Locale(lang) val loc = Locale(lang)
showLang.add(loc.getDisplayLanguage(loc).capitalize(Locale.ROOT)) showLang.add(loc.getDisplayLanguage(loc).capitalize(Locale.ROOT))
} }
val vancedVersionsConv = vancedVersions.value?.value?.reversed()?.convertToAppVersions() val vancedVersionsConv = vancedVersions.value?.value?.convertToAppVersions()
vancedInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.vanced)) vancedInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.vanced))
vancedTheme.text = getString(R.string.chosen_theme, installPrefs.getString("theme", "dark")?.convertToAppTheme(requireActivity())) vancedTheme.text = getString(R.string.chosen_theme, installPrefs.getString("theme", "dark")?.convertToAppTheme(requireActivity()))
vancedVersion.text = getString(R.string.chosen_version, defPrefs.getString("vanced_version", "latest")) vancedVersion.text = getString(R.string.chosen_version, defPrefs.getString("vanced_version", "latest"))
@ -62,16 +65,36 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
showDialog(VancedLanguageSelectionDialog()) showDialog(VancedLanguageSelectionDialog())
} }
vancedInstall.setOnClickListener { vancedInstall.setOnClickListener {
if (showLang.isEmpty()) { if (showLang.isEmpty()) {
installPrefs.lang = "en" installPrefs.lang = "en"
} }
dismiss()
showDialog( fun downloadVanced(version: String? = null) {
AppDownloadDialog.newInstance( dismiss()
app = getString(R.string.vanced) showDialog(
AppDownloadDialog.newInstance(
app = getString(R.string.vanced),
version = version
)
) )
) }
if (defPrefs.managerVariant == "nonroot" && isMicrogBroken && installPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf(""))?.take(2)?.toIntOrNull() == 16 && !isPackageInstalled(vancedPkg, requireActivity().packageManager)) {
MaterialAlertDialogBuilder(requireActivity()).apply {
setTitle(R.string.microg_bug)
setMessage(R.string.microg_bug_summary)
setPositiveButton(R.string.auth_dialog_ok) { _, _ ->
downloadVanced("15.43.32")
}
setNeutralButton(R.string.cancel) { _, _ ->
dismiss()
}
create()
}.applyAccent()
return@setOnClickListener
}
downloadVanced()
} }
} }
} }

View File

@ -155,14 +155,16 @@ open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
} }
init { init {
if (variant == "root") { with (activity) {
vancedRootModel.value = RootDataModel(vanced, activity, vancedRootPkg, activity.getString(R.string.vanced), AppCompatResources.getDrawable(activity, R.drawable.ic_vanced), "vanced") if (variant == "root") {
musicRootModel.value = RootDataModel(music, activity, musicRootPkg, activity.getString(R.string.music), AppCompatResources.getDrawable(activity, R.drawable.ic_music), "music") vancedRootModel.value = RootDataModel(vanced, this, this, vancedRootPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced), "vanced")
} else { musicRootModel.value = RootDataModel(music, this, this, musicRootPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music), "music")
vancedModel.value = DataModel(vanced, activity, vancedPkg, activity.getString(R.string.vanced), AppCompatResources.getDrawable(activity, R.drawable.ic_vanced)) } else {
musicModel.value = DataModel(music, activity, musicPkg, activity.getString(R.string.music), AppCompatResources.getDrawable(activity, R.drawable.ic_music)) vancedModel.value = DataModel(vanced, this, this, vancedPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced))
microgModel.value = DataModel(microg, activity, microgPkg, activity.getString(R.string.microg), AppCompatResources.getDrawable(activity, R.drawable.ic_microg)) musicModel.value = DataModel(music, this, this, musicPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music))
microgModel.value = DataModel(microg, this, this, microgPkg, this.getString(R.string.microg), AppCompatResources.getDrawable(this, R.drawable.ic_microg))
}
managerModel.value = DataModel(manager, this, this, managerPkg, this.getString(R.string.app_name), AppCompatResources.getDrawable(this, R.mipmap.ic_launcher))
} }
managerModel.value = DataModel(manager, activity, managerPkg, activity.getString(R.string.app_name), AppCompatResources.getDrawable(activity, R.mipmap.ic_launcher))
} }
} }

View File

@ -30,7 +30,7 @@ fun DialogFragment.show(activity: FragmentActivity) {
} }
fun List<String>.convertToAppVersions(): List<String> = arrayListOf("latest") + reversed() fun List<String>.convertToAppVersions(): List<String> = listOf("latest") + reversed()
fun String.convertToAppTheme(context: Context): String { fun String.convertToAppTheme(context: Context): String {
return context.getString(R.string.light_plus_other, this.capitalize(Locale.ROOT)) return context.getString(R.string.light_plus_other, this.capitalize(Locale.ROOT))

View File

@ -13,12 +13,16 @@ import androidx.lifecycle.MutableLiveData
import androidx.preference.PreferenceManager.getDefaultSharedPreferences import androidx.preference.PreferenceManager.getDefaultSharedPreferences
import com.beust.klaxon.JsonArray import com.beust.klaxon.JsonArray
import com.beust.klaxon.JsonObject import com.beust.klaxon.JsonObject
import com.github.kittinunf.fuel.httpGet
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.utils.AppUtils.generateChecksum import com.vanced.manager.utils.AppUtils.generateChecksum
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.IOException
import java.net.ConnectException
import java.net.HttpURLConnection
import java.net.SocketTimeoutException
import java.net.URL
import java.util.* import java.util.*
private const val TAG = "VMNetTools" private const val TAG = "VMNetTools"
@ -57,6 +61,8 @@ fun openUrl(url: String, color: Int, context: Context) {
fun getFileNameFromUrl(url: String) = url.substring(url.lastIndexOf('/') + 1, url.length) fun getFileNameFromUrl(url: String) = url.substring(url.lastIndexOf('/') + 1, url.length)
//TODO: Use a better connection method that doesn't cause inappropriate blocks
@Suppress("BlockingMethodInNonBlockingContext")
suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) { suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) {
isFetching.postValue(true) isFetching.postValue(true)
val installUrl = context.defPrefs.installUrl val installUrl = context.defPrefs.installUrl
@ -64,18 +70,33 @@ suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) {
baseInstallUrl = installUrl baseInstallUrl = installUrl
} }
baseInstallUrl.httpGet().response { _, response, _ -> try {
if (response.statusCode / 100 != 2) { val latestbaseUrl = "$baseInstallUrl/latest.json"
baseInstallUrl = "https://mirror.codebucket.de/vanced" val connection = URL(latestbaseUrl).openConnection() as HttpURLConnection
connection.apply {
connectTimeout = 5000
readTimeout = 5000
connect()
} }
if (connection.responseCode != 200) {
Log.d(TAG, latestbaseUrl + ": " + connection.responseCode.toString())
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
}
} catch (e: IOException) {
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
} catch (e: SocketTimeoutException) {
Log.d(TAG, "connection timed out")
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
} }
Log.d(TAG, "Fetching using URL: $baseInstallUrl")
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
val hour = calendar.get(Calendar.HOUR_OF_DAY) val hour = calendar.get(Calendar.HOUR_OF_DAY)
val minute = calendar.get(Calendar.MINUTE) val minute = calendar.get(Calendar.MINUTE)
val second = calendar.get(Calendar.SECOND) val second = calendar.get(Calendar.SECOND)
val fetchTime = "fetchTime=$hour$minute$second" val fetchTime = "fetchTime=$hour$minute$second"
val latest = getJson("$baseInstallUrl/latest.json?$fetchTime") val latest = getJson("$baseInstallUrl/latest.json?$fetchTime")
val versions = getJson("$baseInstallUrl/versions.json?$fetchTime") val versions = getJson("$baseInstallUrl/versions.json?$fetchTime")
isMicrogBroken = latest?.boolean("is_microg_broken") ?: false isMicrogBroken = latest?.boolean("is_microg_broken") ?: false

View File

@ -32,7 +32,7 @@ object PackageHelper {
const val apkInstallPath = "/data/adb" const val apkInstallPath = "/data/adb"
private const val INSTALLER_TAG = "VMInstall" private const val INSTALLER_TAG = "VMInstall"
private val vancedThemes = vanced.value?.array<String>("themes")?.value ?: arrayOf("black", "dark", "pink", "blue") private val vancedThemes = vanced.value?.array<String>("themes")?.value ?: listOf("black", "dark", "pink", "blue")
init { init {
Shell.enableVerboseLogging = BuildConfig.DEBUG Shell.enableVerboseLogging = BuildConfig.DEBUG

View File

@ -90,6 +90,8 @@
<string name="vanced_installed">Vanced has successfully been installed! Open now?</string> <string name="vanced_installed">Vanced has successfully been installed! Open now?</string>
<string name="version">Version</string> <string name="version">Version</string>
<string name="music_installed">Vanced Music has successfully been installed! Open now?</string> <string name="music_installed">Vanced Music has successfully been installed! Open now?</string>
<string name="microg_bug">Bug in microG</string>
<string name="microg_bug_summary">Due to a bug in microG, installing Vanced 16+ first requires you to install v15.43.32, open, login and then manually select and install version 16. Do you want to proceed installing v15.43.32?</string>
<string name="please_be_patient">Please be patient…</string> <string name="please_be_patient">Please be patient…</string>
<string name="launch">Open</string> <string name="launch">Open</string>
<string name="welcome">Welcome</string> <string name="welcome">Welcome</string>