improve: use flow in batch uninstaller

This commit is contained in:
Hamza417
2025-10-21 23:45:12 +05:30
parent c232fe4bf2
commit 16d1434310
4 changed files with 50 additions and 49 deletions

View File

@@ -30,10 +30,10 @@ class AdapterBatchUninstaller(private val results: ArrayList<BatchUninstaller.Co
holder.name.text = result.packageInfo.packageName
}
holder.result.text = if (result.isSuccessful) {
holder.itemView.context.getString(R.string.uninstalled)
} else {
holder.itemView.context.getString(R.string.failed)
holder.result.text = when (result.isSuccessful) {
null -> holder.itemView.context.getString(R.string.pending)
true -> holder.itemView.context.getString(R.string.uninstalled)
false -> holder.itemView.context.getString(R.string.failed)
}
}
@@ -41,6 +41,16 @@ class AdapterBatchUninstaller(private val results: ArrayList<BatchUninstaller.Co
return results.size
}
fun updateResults(newResults: ArrayList<BatchUninstaller.Companion.BatchUninstallerResult>) {
// Find which items changed and notify only those
for (i in results.indices) {
if (i < newResults.size && results[i].isSuccessful != newResults[i].isSuccessful) {
results[i] = newResults[i]
notifyItemChanged(i)
}
}
}
inner class Holder(itemView: View) : VerticalListViewHolder(itemView) {
val icon: ImageView = itemView.findViewById(R.id.app_icon)
val name: TypeFaceTextView = itemView.findViewById(R.id.app_name)

View File

@@ -6,22 +6,21 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import app.simple.inure.R
import app.simple.inure.adapters.batch.AdapterBatchUninstaller
import app.simple.inure.constants.BundleConstants
import app.simple.inure.decorations.overscroll.CustomVerticalRecyclerView
import app.simple.inure.decorations.views.LoaderImageView
import app.simple.inure.extensions.fragments.ScopedBottomSheetFragment
import app.simple.inure.factories.panels.BatchViewModelFactory
import app.simple.inure.models.BatchPackageInfo
import app.simple.inure.util.ParcelUtils.parcelableArrayList
import app.simple.inure.util.ViewUtils.gone
import app.simple.inure.viewmodels.dialogs.BatchUninstallerViewModel
import app.simple.inure.viewmodels.panels.BatchViewModel
import kotlinx.coroutines.launch
class BatchUninstaller : ScopedBottomSheetFragment() {
private lateinit var loader: LoaderImageView
private lateinit var recyclerView: CustomVerticalRecyclerView
private var adapterBatchUninstaller: AdapterBatchUninstaller? = null
@@ -32,7 +31,6 @@ class BatchUninstaller : ScopedBottomSheetFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.dialog_batch_uninstall, container, false)
loader = view.findViewById(R.id.loader)
recyclerView = view.findViewById(R.id.recycler_view)
appList = requireArguments().parcelableArrayList(BundleConstants.selectedBatchApps)!!
@@ -47,12 +45,17 @@ class BatchUninstaller : ScopedBottomSheetFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loader.start()
batchUninstallerViewModel?.getData()?.observe(viewLifecycleOwner) {
loader.gone()
adapterBatchUninstaller = AdapterBatchUninstaller(it)
recyclerView.adapter = adapterBatchUninstaller
viewLifecycleOwner.lifecycleScope.launch {
batchUninstallerViewModel?.uninstallResults?.collect { results ->
if (adapterBatchUninstaller == null) {
// Initialize adapter with the first emission (all pending)
adapterBatchUninstaller = AdapterBatchUninstaller(results)
recyclerView.adapter = adapterBatchUninstaller
} else {
// Update existing adapter with new results
adapterBatchUninstaller?.updateResults(results)
}
}
}
}
@@ -69,7 +72,7 @@ class BatchUninstaller : ScopedBottomSheetFragment() {
data class BatchUninstallerResult(
val packageInfo: PackageInfo,
val isSuccessful: Boolean
val isSuccessful: Boolean? // null = pending, true = success, false = failed
)
}
}

View File

@@ -3,8 +3,6 @@ package app.simple.inure.viewmodels.dialogs
import android.app.Application
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import app.simple.inure.apk.utils.PackageUtils.safeApplicationInfo
import app.simple.inure.constants.Warnings
@@ -14,39 +12,38 @@ import app.simple.inure.helpers.ShizukuServiceHelper
import app.simple.inure.models.BatchPackageInfo
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class BatchUninstallerViewModel(application: Application, val list: ArrayList<BatchPackageInfo>) : RootShizukuViewModel(application) {
private val data: MutableLiveData<ArrayList<BatchUninstallerResult>> by lazy {
MutableLiveData<ArrayList<BatchUninstallerResult>>().also {
initializeCoreFramework()
}
}
private val _uninstallResults = MutableStateFlow<ArrayList<BatchUninstallerResult>>(arrayListOf())
val uninstallResults: StateFlow<ArrayList<BatchUninstallerResult>> = _uninstallResults.asStateFlow()
fun getData(): LiveData<ArrayList<BatchUninstallerResult>> {
return data
init {
// Initialize with pending state for all apps
val initialResults = ArrayList(list.map { BatchUninstallerResult(it.packageInfo, null) })
_uninstallResults.value = initialResults
initializeCoreFramework()
}
override fun onShellCreated(shell: Shell?) {
viewModelScope.launch(Dispatchers.IO) {
val uninstalledApps = arrayListOf<BatchUninstallerResult>()
val results = ArrayList(_uninstallResults.value)
for (app in list) {
for ((index, app) in list.withIndex()) {
runCatching {
Shell.cmd(app.packageInfo.getUninstallCommand()).exec().let {
if (it.isSuccess) {
uninstalledApps.add(BatchUninstallerResult(app.packageInfo, true))
} else {
uninstalledApps.add(BatchUninstallerResult(app.packageInfo, false))
}
results[index] = BatchUninstallerResult(app.packageInfo, it.isSuccess)
_uninstallResults.value = ArrayList(results)
}
}.onFailure {
uninstalledApps.add(BatchUninstallerResult(app.packageInfo, false))
results[index] = BatchUninstallerResult(app.packageInfo, false)
_uninstallResults.value = ArrayList(results)
}
}
data.postValue(uninstalledApps)
}
}
@@ -64,23 +61,19 @@ class BatchUninstallerViewModel(application: Application, val list: ArrayList<Ba
override fun onShizukuCreated(shizukuServiceHelper: ShizukuServiceHelper) {
viewModelScope.launch(Dispatchers.IO) {
val uninstalledApps = arrayListOf<BatchUninstallerResult>()
val results = ArrayList(_uninstallResults.value)
for (app in list) {
for ((index, app) in list.withIndex()) {
runCatching {
shizukuServiceHelper.service!!.simpleExecute(app.packageInfo.getUninstallCommand()).let {
if (it.isSuccess) {
uninstalledApps.add(BatchUninstallerResult(app.packageInfo, true))
} else {
uninstalledApps.add(BatchUninstallerResult(app.packageInfo, false))
}
results[index] = BatchUninstallerResult(app.packageInfo, it.isSuccess)
_uninstallResults.value = ArrayList(results)
}
}.onFailure {
uninstalledApps.add(BatchUninstallerResult(app.packageInfo, false))
results[index] = BatchUninstallerResult(app.packageInfo, false)
_uninstallResults.value = ArrayList(results)
}
}
data.postValue(uninstalledApps)
}
}
}

View File

@@ -25,11 +25,6 @@
app:isTopFadingEdgeOnly="false"
app:statusBarPaddingRequired="false" />
<app.simple.inure.decorations.views.LoaderImageView
android:id="@+id/loader"
android:layout_gravity="center"
android:layout_width="@dimen/loader_size"
android:layout_height="@dimen/loader_size" />
</app.simple.inure.decorations.corners.DynamicCornerFrameLayout>