Remove viewmodel memory leak

This commit is contained in:
Tiger Oakes 2021-10-24 21:37:31 -07:00
parent 4af47e8de0
commit 368808de0c
8 changed files with 90 additions and 70 deletions

View File

@ -84,6 +84,7 @@ class ExpandableAppListAdapter(
appDownload.apply {
setOnClickListener {
viewModel.openInstallDialog(
activity.supportFragmentManager,
buttonTag,
apps[position]
)

View File

@ -64,7 +64,7 @@ class LinkAdapter(
fun bind(position: Int) {
binding.linkBg.setOnClickListener {
viewModel.openUrl(links[position].linkUrl)
viewModel.openUrl(context, links[position].linkUrl)
}
}
}

View File

@ -42,7 +42,7 @@ class SponsorAdapter(
with(binding) {
sponsorName.text = sponsors[position].name
cardSponsor.setOnClickListener {
viewModel.openUrl(sponsors[position].url)
viewModel.openUrl(context, sponsors[position].url)
}
}
}

View File

@ -0,0 +1,37 @@
package com.vanced.manager.core
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
/**
* CombinedLiveData is a helper class to combine results from two LiveData sources.
* @param combine Function reference that will be used to combine all LiveData data.
* @param R The type of data returned after combining all LiveData data.
* Usage:
* CombinedLiveData(
* getLiveData1(),
* getLiveData2()
* ) { data1, data2 ->
* // Use datas[0], datas[1], ..., datas[N] to return a value
* }
*/
class CombinedLiveData<R, A, B>(
liveDataA: LiveData<A>,
liveDataB: LiveData<B>,
private val combine: (a: A?, b: B?) -> R
) : MediatorLiveData<R>() {
private var a: A? = null
private var b: B? = null
init {
addSource(liveDataA) {
a = it
value = combine(a, b)
}
addSource(liveDataB) {
b = it
value = combine(a, b)
}
}
}

View File

@ -26,7 +26,6 @@ import com.vanced.manager.databinding.FragmentHomeBinding
import com.vanced.manager.ui.dialogs.AppInfoDialog
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
import com.vanced.manager.ui.viewmodels.HomeViewModel
import com.vanced.manager.ui.viewmodels.HomeViewModelFactory
import com.vanced.manager.utils.isFetching
import com.vanced.manager.utils.manager
@ -37,9 +36,7 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
const val REFRESH_HOME = "REFRESH_HOME"
}
private val viewModel: HomeViewModel by viewModels {
HomeViewModelFactory(requireActivity())
}
private val viewModel: HomeViewModel by viewModels()
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) }

View File

@ -1,13 +1,14 @@
package com.vanced.manager.ui.viewmodels
import android.annotation.SuppressLint
import android.app.Application
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
import com.vanced.manager.R
@ -38,12 +39,11 @@ import com.vanced.manager.utils.PackageHelper.uninstallRootApk
import com.vanced.manager.utils.PackageHelper.vancedInstallFilesExist
import kotlinx.coroutines.launch
//TODO fix leak
@SuppressLint("StaticFieldLeak")
class HomeViewModel(private val activity: FragmentActivity) : ViewModel() {
class HomeViewModel(application: Application) : AndroidViewModel(application) {
private val prefs = getDefaultSharedPreferences(activity)
private val prefs = getDefaultSharedPreferences(context)
private val variant get() = prefs.getString("vanced_variant", "nonroot")
private val context: Context get() = getApplication()
val vancedModel = MutableLiveData<DataModel>()
val vancedRootModel = MutableLiveData<RootDataModel>()
@ -54,13 +54,13 @@ class HomeViewModel(private val activity: FragmentActivity) : ViewModel() {
fun fetchData() {
viewModelScope.launch {
loadJson(activity)
loadJson(context)
}
}
private val microgToast = Toast.makeText(activity, R.string.no_microg, Toast.LENGTH_LONG)
private val microgToast = Toast.makeText(context, R.string.no_microg, Toast.LENGTH_LONG)
fun openUrl(url: String) {
fun openUrl(context: Context, url: String) {
val color: Int =
when (url) {
DISCORD -> R.color.Discord
@ -71,82 +71,82 @@ class HomeViewModel(private val activity: FragmentActivity) : ViewModel() {
else -> R.color.Vanced
}
openUrl(url, color, activity)
openUrl(url, color, context)
}
fun launchApp(app: String, isRoot: Boolean) {
val componentName = when (app) {
activity.getString(R.string.vanced) -> if (isRoot) ComponentName(
context.getString(R.string.vanced) -> if (isRoot) ComponentName(
vancedRootPkg,
"$vancedRootPkg.HomeActivity"
) else ComponentName(vancedPkg, "$vancedRootPkg.HomeActivity")
activity.getString(R.string.music) -> if (isRoot) ComponentName(
context.getString(R.string.music) -> if (isRoot) ComponentName(
musicRootPkg,
"$musicRootPkg.activities.MusicActivity"
) else ComponentName(musicPkg, "$musicRootPkg.activities.MusicActivity")
activity.getString(R.string.microg) -> ComponentName(
context.getString(R.string.microg) -> ComponentName(
microgPkg,
"org.microg.gms.ui.SettingsActivity"
)
else -> throw IllegalArgumentException("Can't open this app")
}
try {
activity.startActivity(Intent().setComponent(componentName))
context.startActivity(Intent().setComponent(componentName))
} catch (e: ActivityNotFoundException) {
log("VMHMV", e.toString())
}
}
fun openInstallDialog(buttonTag: ButtonTag?, app: String) {
if (variant == "nonroot" && app != activity.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
fun openInstallDialog(fragmentManager: FragmentManager, buttonTag: ButtonTag?, app: String) {
if (variant == "nonroot" && app != context.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
microgToast.show()
return
}
if (buttonTag == ButtonTag.UPDATE) {
when (app) {
activity.getString(R.string.vanced) -> VancedPreferencesDialog().show(activity)
activity.getString(R.string.music) -> MusicPreferencesDialog().show(activity)
else -> AppDownloadDialog.newInstance(app).show(activity)
context.getString(R.string.vanced) -> VancedPreferencesDialog().show(fragmentManager)
context.getString(R.string.music) -> MusicPreferencesDialog().show(fragmentManager)
else -> AppDownloadDialog.newInstance(app).show(fragmentManager)
}
return
}
when (app) {
activity.getString(R.string.vanced) -> {
context.getString(R.string.vanced) -> {
when (variant) {
"nonroot" -> {
if (vancedInstallFilesExist(activity)) {
InstallationFilesDetectedDialog.newInstance(app).show(activity)
if (vancedInstallFilesExist(context)) {
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
} else {
VancedPreferencesDialog().show(activity)
VancedPreferencesDialog().show(fragmentManager)
}
}
"root" -> {
VancedPreferencesDialog().show(activity)
VancedPreferencesDialog().show(fragmentManager)
}
}
}
activity.getString(R.string.music) -> {
context.getString(R.string.music) -> {
when (variant) {
"nonroot" -> {
if (musicApkExists(activity)) {
InstallationFilesDetectedDialog.newInstance(app).show(activity)
if (musicApkExists(context)) {
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
} else {
MusicPreferencesDialog().show(activity)
MusicPreferencesDialog().show(fragmentManager)
}
}
"root" -> {
MusicPreferencesDialog().show(activity)
MusicPreferencesDialog().show(fragmentManager)
}
}
}
activity.getString(R.string.microg) -> {
if (apkExist(activity, "microg.apk")) {
InstallationFilesDetectedDialog.newInstance(app).show(activity)
context.getString(R.string.microg) -> {
if (apkExist(context, "microg.apk")) {
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
} else {
AppDownloadDialog.newInstance(app).show(activity)
AppDownloadDialog.newInstance(app).show(fragmentManager)
}
}
}
@ -155,32 +155,30 @@ class HomeViewModel(private val activity: FragmentActivity) : ViewModel() {
fun uninstallPackage(pkg: String) {
if (variant == "root" && uninstallRootApk(pkg)) {
viewModelScope.launch { loadJson(activity) }
viewModelScope.launch { loadJson(context) }
} else {
uninstallApk(pkg, activity)
uninstallApk(pkg, context)
}
}
init {
with(activity) {
with(context) {
if (variant == "root") {
vancedRootModel.value = RootDataModel(
vanced,
this,
this,
vancedRootPkg,
this.getString(R.string.vanced),
activity.getString(R.string.description_vanced),
this.getString(R.string.description_vanced),
R.drawable.ic_vanced,
"vanced"
)
musicRootModel.value = RootDataModel(
music,
this,
this,
musicRootPkg,
this.getString(R.string.music),
activity.getString(R.string.description_vanced_music),
this.getString(R.string.description_vanced_music),
R.drawable.ic_music,
"music"
)
@ -188,35 +186,31 @@ class HomeViewModel(private val activity: FragmentActivity) : ViewModel() {
vancedModel.value = DataModel(
vanced,
this,
this,
vancedPkg,
this.getString(R.string.vanced),
activity.getString(R.string.description_vanced),
this.getString(R.string.description_vanced),
R.drawable.ic_vanced
)
musicModel.value = DataModel(
music,
this,
this,
musicPkg,
this.getString(R.string.music),
activity.getString(R.string.description_vanced_music),
this.getString(R.string.description_vanced_music),
R.drawable.ic_music
)
microgModel.value = DataModel(
microg,
this,
this,
microgPkg,
this.getString(R.string.microg),
activity.getString(R.string.description_microg),
this.getString(R.string.description_microg),
R.drawable.ic_microg
)
}
managerModel.value = DataModel(
manager,
this,
this,
managerPkg,
this.getString(R.string.app_name),
"Just manager meh",

View File

@ -1,14 +0,0 @@
package com.vanced.manager.ui.viewmodels
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class HomeViewModelFactory(private val activity: FragmentActivity) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return HomeViewModel(activity) as T
}
}

View File

@ -7,6 +7,7 @@ import android.widget.RadioGroup
import androidx.core.graphics.ColorUtils
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.LinearProgressIndicator
@ -22,14 +23,18 @@ val RadioGroup.checkedButtonTag: String?
checkedRadioButtonId
)?.tag?.toString()
fun DialogFragment.show(activity: FragmentActivity) {
fun DialogFragment.show(fragmentManager: FragmentManager) {
try {
show(activity.supportFragmentManager, "")
show(fragmentManager, "")
} catch (e: Exception) {
log("VMUI", e.stackTraceToString())
}
}
fun DialogFragment.show(activity: FragmentActivity) {
show(activity.supportFragmentManager)
}
fun List<String>.convertToAppVersions(): List<String> = listOf("latest") + reversed()
fun String.formatVersion(context: Context): String =