Improve the loading time of DataSaverSummary

Use AppListRepository instead of ApplicationsState can achieve better
performance.

Fix: 280280596
Test: Manually on Data Saver page
Test: Unit test
Change-Id: I98cbae50500b90e7e7f6101fb064003339b082c1
This commit is contained in:
Chaohui Wang
2023-05-24 09:50:16 +08:00
parent 128036a053
commit cb9374e294
3 changed files with 150 additions and 64 deletions

View File

@@ -15,33 +15,36 @@
*/
package com.android.settings.datausage
import android.app.Application
import android.app.settings.SettingsEnums
import android.content.Context
import android.net.NetworkPolicyManager
import android.os.Bundle
import android.os.UserHandle
import android.telephony.SubscriptionManager
import android.widget.Switch
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.android.settings.R
import com.android.settings.SettingsActivity
import com.android.settings.SettingsPreferenceFragment
import com.android.settings.applications.AppStateBaseBridge
import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState
import com.android.settings.search.BaseSearchIndexProvider
import com.android.settings.widget.SettingsMainSwitchBar
import com.android.settingslib.applications.ApplicationsState
import com.android.settingslib.search.SearchIndexable
import com.android.settingslib.spa.framework.util.formatString
import com.android.settingslib.spaprivileged.model.app.AppListRepository
import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@SearchIndexable
class DataSaverSummary : SettingsPreferenceFragment() {
private lateinit var switchBar: SettingsMainSwitchBar
private lateinit var dataSaverBackend: DataSaverBackend
private lateinit var unrestrictedAccess: Preference
private var dataUsageBridge: AppStateDataUsageBridge? = null
private var session: ApplicationsState.Session? = null
// Flag used to avoid infinite loop due if user switch it on/off too quick.
private var switching = false
@@ -72,27 +75,15 @@ class DataSaverSummary : SettingsPreferenceFragment() {
override fun onResume() {
super.onResume()
dataSaverBackend.refreshAllowlist()
dataSaverBackend.refreshDenylist()
dataSaverBackend.addListener(dataSaverBackendListener)
dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
?: viewLifecycleOwner.lifecycleScope.launch {
val applicationsState = ApplicationsState.getInstance(
requireContext().applicationContext as Application
)
dataUsageBridge = AppStateDataUsageBridge(
applicationsState, dataUsageBridgeCallbacks, dataSaverBackend
)
session =
applicationsState.newSession(applicationsStateCallbacks, settingsLifecycle)
dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
}
viewLifecycleOwner.lifecycleScope.launch {
unrestrictedAccess.summary = getUnrestrictedSummary(requireContext())
}
}
override fun onPause() {
super.onPause()
dataSaverBackend.remListener(dataSaverBackendListener)
dataUsageBridge?.pause()
}
private fun onSwitchChanged(isChecked: Boolean) {
@@ -115,52 +106,36 @@ class DataSaverSummary : SettingsPreferenceFragment() {
switching = false
}
}
override fun onAllowlistStatusChanged(uid: Int, isAllowlisted: Boolean) {}
override fun onDenylistStatusChanged(uid: Int, isDenylisted: Boolean) {}
}
private val dataUsageBridgeCallbacks = AppStateBaseBridge.Callback {
updateUnrestrictedAccessSummary()
}
private val applicationsStateCallbacks = object : ApplicationsState.Callbacks {
override fun onRunningStateChanged(running: Boolean) {}
override fun onPackageListChanged() {}
override fun onRebuildComplete(apps: ArrayList<ApplicationsState.AppEntry>?) {}
override fun onPackageIconChanged() {}
override fun onPackageSizeChanged(packageName: String?) {}
override fun onAllSizesComputed() {
updateUnrestrictedAccessSummary()
}
override fun onLauncherInfoChanged() {
updateUnrestrictedAccessSummary()
}
override fun onLoadEntriesCompleted() {}
}
private fun updateUnrestrictedAccessSummary() {
if (!isAdded || isFinishingOrDestroyed) return
val allApps = session?.allApps ?: return
val count = allApps.count {
ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(it) &&
(it.extraInfo as? DataUsageState)?.isDataSaverAllowlisted == true
}
unrestrictedAccess.summary =
resources.formatString(R.string.data_saver_unrestricted_summary, "count" to count)
}
companion object {
private const val KEY_UNRESTRICTED_ACCESS = "unrestricted_access"
@VisibleForTesting
suspend fun getUnrestrictedSummary(
context: Context,
appListRepository: AppListRepository =
AppListRepositoryImpl(context.applicationContext),
) = context.formatString(
R.string.data_saver_unrestricted_summary,
"count" to getAllowCount(context.applicationContext, appListRepository),
)
private suspend fun getAllowCount(context: Context, appListRepository: AppListRepository) =
withContext(Dispatchers.IO) {
coroutineScope {
val appsDeferred = async {
appListRepository.loadAndFilterApps(
userId = UserHandle.myUserId(),
isSystemApp = false,
)
}
val uidsAllowed = NetworkPolicyManager.from(context)
.getUidsWithPolicy(NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND)
appsDeferred.await().count { app -> app.uid in uidsAllowed }
}
}
private fun Context.isDataSaverVisible(): Boolean =
resources.getBoolean(R.bool.config_show_data_saver)