Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f915a4c authored by Chaohui Wang's avatar Chaohui Wang Committed by Automerger Merge Worker
Browse files

Merge "Fix Data Saver page crashed when rotate" into udc-dev am: bf9ba71c

parents 648266e8 bf9ba71c
Loading
Loading
Loading
Loading
+0 −234
Original line number Diff line number Diff line
/*
 * 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<AppEntry> 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<String, Object> 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;
                }
            };
}
+176 −0
Original line number Diff line number Diff line
/*
 * 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<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"

        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