diff --git a/src/com/android/settings/datausage/DataSaverSummary.java b/src/com/android/settings/datausage/DataSaverSummary.java deleted file mode 100644 index 67644a6c992..00000000000 --- a/src/com/android/settings/datausage/DataSaverSummary.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.android.settings.datausage; - -import android.app.Application; -import android.app.settings.SettingsEnums; -import android.content.Context; -import android.icu.text.MessageFormat; -import android.os.Bundle; -import android.telephony.SubscriptionManager; -import android.widget.Switch; - -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.Callback; -import com.android.settings.datausage.DataSaverBackend.Listener; -import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settings.widget.SettingsMainSwitchBar; -import com.android.settingslib.applications.ApplicationsState; -import com.android.settingslib.applications.ApplicationsState.AppEntry; -import com.android.settingslib.applications.ApplicationsState.Callbacks; -import com.android.settingslib.applications.ApplicationsState.Session; -import com.android.settingslib.search.SearchIndexable; -import com.android.settingslib.widget.OnMainSwitchChangeListener; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -@SearchIndexable -public class DataSaverSummary extends SettingsPreferenceFragment - implements OnMainSwitchChangeListener, Listener, Callback, Callbacks { - - private static final String KEY_UNRESTRICTED_ACCESS = "unrestricted_access"; - - private SettingsMainSwitchBar mSwitchBar; - private DataSaverBackend mDataSaverBackend; - private Preference mUnrestrictedAccess; - private ApplicationsState mApplicationsState; - private AppStateDataUsageBridge mDataUsageBridge; - private Session mSession; - - // Flag used to avoid infinite loop due if user switch it on/off too quicky. - private boolean mSwitching; - - private Runnable mLoadAppRunnable = () -> { - mApplicationsState = ApplicationsState.getInstance( - (Application) getContext().getApplicationContext()); - mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend); - mSession = mApplicationsState.newSession(this, getSettingsLifecycle()); - mDataUsageBridge.resume(true /* forceLoadAllApps */); - }; - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - if (!isDataSaverVisible(getContext())) { - finishFragment(); - return; - } - - addPreferencesFromResource(R.xml.data_saver); - mUnrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS); - mDataSaverBackend = new DataSaverBackend(getContext()); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar(); - mSwitchBar.setTitle(getContext().getString(R.string.data_saver_switch_title)); - mSwitchBar.show(); - mSwitchBar.addOnSwitchChangeListener(this); - } - - @Override - public void onResume() { - super.onResume(); - mDataSaverBackend.refreshAllowlist(); - mDataSaverBackend.refreshDenylist(); - mDataSaverBackend.addListener(this); - if (mDataUsageBridge != null) { - mDataUsageBridge.resume(true /* forceLoadAllApps */); - } else { - getView().post(mLoadAppRunnable); - } - } - - @Override - public void onPause() { - super.onPause(); - mDataSaverBackend.remListener(this); - if (mDataUsageBridge != null) { - mDataUsageBridge.pause(); - } - } - - @Override - public void onSwitchChanged(Switch switchView, boolean isChecked) { - synchronized (this) { - if (mSwitching) { - return; - } - mSwitching = true; - mDataSaverBackend.setDataSaverEnabled(isChecked); - } - } - - @Override - public int getMetricsCategory() { - return SettingsEnums.DATA_SAVER_SUMMARY; - } - - @Override - public int getHelpResource() { - return R.string.help_url_data_saver; - } - - @Override - public void onDataSaverChanged(boolean isDataSaving) { - synchronized (this) { - mSwitchBar.setChecked(isDataSaving); - mSwitching = false; - } - } - - @Override - public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) { - } - - @Override - public void onDenylistStatusChanged(int uid, boolean isDenylisted) { - } - - @Override - public void onExtraInfoUpdated() { - updateUnrestrictedAccessSummary(); - } - - @Override - public void onRunningStateChanged(boolean running) { - - } - - @Override - public void onPackageListChanged() { - - } - - @Override - public void onRebuildComplete(ArrayList apps) { - - } - - @Override - public void onPackageIconChanged() { - - } - - @Override - public void onPackageSizeChanged(String packageName) { - - } - - @Override - public void onAllSizesComputed() { - updateUnrestrictedAccessSummary(); - } - - @Override - public void onLauncherInfoChanged() { - updateUnrestrictedAccessSummary(); - } - - @Override - public void onLoadEntriesCompleted() { - - } - - private void updateUnrestrictedAccessSummary() { - if (!isAdded() || isFinishingOrDestroyed() || mSession == null) return; - - int count = 0; - for (AppEntry entry : mSession.getAllApps()) { - if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry)) { - continue; - } - if (entry.extraInfo != null && ((AppStateDataUsageBridge.DataUsageState) - entry.extraInfo).isDataSaverAllowlisted) { - count++; - } - } - MessageFormat msgFormat = new MessageFormat( - getResources().getString(R.string.data_saver_unrestricted_summary), - Locale.getDefault()); - Map arguments = new HashMap<>(); - arguments.put("count", count); - mUnrestrictedAccess.setSummary(msgFormat.format(arguments)); - } - - public static boolean isDataSaverVisible(Context context) { - return context.getResources() - .getBoolean(R.bool.config_show_data_saver); - } - - public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider(R.xml.data_saver) { - - @Override - protected boolean isPageSearchEnabled(Context context) { - return isDataSaverVisible(context) - && DataUsageUtils.hasMobileData(context) - && DataUsageUtils.getDefaultSubscriptionId(context) - != SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - }; -} diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt new file mode 100644 index 00000000000..1d9cbb73a66 --- /dev/null +++ b/src/com/android/settings/datausage/DataSaverSummary.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.datausage + +import android.app.Application +import android.app.settings.SettingsEnums +import android.content.Context +import android.os.Bundle +import android.telephony.SubscriptionManager +import android.widget.Switch +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 kotlinx.coroutines.launch + +@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 + + override fun onCreate(bundle: Bundle?) { + super.onCreate(bundle) + + if (!requireContext().isDataSaverVisible()) { + finishFragment() + return + } + + addPreferencesFromResource(R.xml.data_saver) + unrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS)!! + dataSaverBackend = DataSaverBackend(requireContext()) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + switchBar = (activity as SettingsActivity).switchBar.apply { + setTitle(getString(R.string.data_saver_switch_title)) + show() + addOnSwitchChangeListener { _: Switch, isChecked: Boolean -> + onSwitchChanged(isChecked) + } + } + } + + 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) + } + } + + override fun onPause() { + super.onPause() + dataSaverBackend.remListener(dataSaverBackendListener) + dataUsageBridge?.pause() + } + + private fun onSwitchChanged(isChecked: Boolean) { + synchronized(this) { + if (!switching) { + switching = true + dataSaverBackend.isDataSaverEnabled = isChecked + } + } + } + + override fun getMetricsCategory() = SettingsEnums.DATA_SAVER_SUMMARY + + override fun getHelpResource() = R.string.help_url_data_saver + + private val dataSaverBackendListener = object : DataSaverBackend.Listener { + override fun onDataSaverChanged(isDataSaving: Boolean) { + synchronized(this) { + switchBar.isChecked = isDataSaving + 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?) {} + + 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" + + private fun Context.isDataSaverVisible(): Boolean = + resources.getBoolean(R.bool.config_show_data_saver) + + @JvmField + val SEARCH_INDEX_DATA_PROVIDER = object : BaseSearchIndexProvider(R.xml.data_saver) { + override fun isPageSearchEnabled(context: Context): Boolean = + context.isDataSaverVisible() && + DataUsageUtils.hasMobileData(context) && + (DataUsageUtils.getDefaultSubscriptionId(context) != + SubscriptionManager.INVALID_SUBSCRIPTION_ID) + } + } +} \ No newline at end of file