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

Commit 2ecc77ef authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Create AppDataUsageSummaryController" into main

parents d1f7df00 07335514
Loading
Loading
Loading
Loading
+3 −25
Original line number Diff line number Diff line
@@ -24,31 +24,9 @@
        android:key="cycle"
        settings:controller="com.android.settings.datausage.AppDataUsageCycleController" />

    <PreferenceCategory
        android:key="app_data_usage_summary_category">

        <Preference
            android:key="total_usage"
            android:title="@string/total_size_label"
            android:selectable="false"
            android:layout="@layout/horizontal_preference"
            android:summary="@string/summary_placeholder" />

        <Preference
            android:key="foreground_usage"
            android:title="@string/data_usage_label_foreground"
            android:selectable="false"
            android:layout="@layout/horizontal_preference"
            android:summary="@string/summary_placeholder" />

        <Preference
            android:key="background_usage"
            android:title="@string/data_usage_label_background"
            android:selectable="false"
            android:layout="@layout/horizontal_preference"
            android:summary="@string/summary_placeholder" />

    </PreferenceCategory>
    <com.android.settings.spa.preference.ComposePreference
        android:key="app_data_usage_summary"
        settings:controller="com.android.settings.datausage.AppDataUsageSummaryController"/>

    <PreferenceCategory
        android:key="app_data_usage_settings_category"
+6 −25
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@ import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.datausage.lib.AppDataUsageDetailsRepository;
import com.android.settings.datausage.lib.NetworkTemplates;
import com.android.settings.datausage.lib.NetworkUsageDetailsData;
import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.widget.EntityHeaderController;
@@ -70,17 +69,11 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
    static final String ARG_NETWORK_CYCLES = "network_cycles";
    static final String ARG_SELECTED_CYCLE = "selected_cycle";

    private static final String KEY_TOTAL_USAGE = "total_usage";
    private static final String KEY_FOREGROUND_USAGE = "foreground_usage";
    private static final String KEY_BACKGROUND_USAGE = "background_usage";
    private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
    private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver";

    private PackageManager mPackageManager;
    private final ArraySet<String> mPackages = new ArraySet<>();
    private Preference mTotalUsage;
    private Preference mForegroundUsage;
    private Preference mBackgroundUsage;
    private RestrictedSwitchPreference mRestrictBackground;

    private Drawable mIcon;
@@ -139,10 +132,6 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
            }
        }

        mTotalUsage = findPreference(KEY_TOTAL_USAGE);
        mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
        mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);

        final List<Integer> uidList = getAppUidList(mAppItem.uids);
        initCycle(uidList);

@@ -255,15 +244,17 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC

    @VisibleForTesting
    void initCycle(List<Integer> uidList) {
        var controller = use(AppDataUsageCycleController.class);
        var cycleController = use(AppDataUsageCycleController.class);
        var summaryController = use(AppDataUsageSummaryController.class);
        var repository = new AppDataUsageDetailsRepository(mContext, mTemplate, mCycles, uidList);
        controller.init(repository, data -> {
            bindData(data);
        cycleController.init(repository, data -> {
            mIsLoading = false;
            summaryController.update(data);
            return Unit.INSTANCE;
        });
        if (mCycles != null) {
            Log.d(TAG, "setInitialCycles: " + mCycles + " " + mSelectedCycle);
            controller.setInitialCycles(mCycles, mSelectedCycle);
            cycleController.setInitialCycles(mCycles, mSelectedCycle);
        }
    }

@@ -314,16 +305,6 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
        }
    }

    @VisibleForTesting
    void bindData(@NonNull NetworkUsageDetailsData data) {
        mIsLoading = false;
        mTotalUsage.setSummary(DataUsageUtils.formatDataUsage(mContext, data.getTotalUsage()));
        mForegroundUsage.setSummary(
                DataUsageUtils.formatDataUsage(mContext, data.getForegroundUsage()));
        mBackgroundUsage.setSummary(
                DataUsageUtils.formatDataUsage(mContext, data.getBackgroundUsage()));
    }

    private boolean getAppRestrictBackground() {
        final int uid = mAppItem.key;
        final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.datausage.lib.NetworkUsageDetailsData
import com.android.settings.spa.preference.ComposePreferenceController
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.framework.compose.placeholder
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map

class AppDataUsageSummaryController(context: Context, preferenceKey: String) :
    ComposePreferenceController(context, preferenceKey) {

    private val dataFlow = MutableStateFlow(NetworkUsageDetailsData.AllZero)

    private val totalUsageFlow = dataFlow.map {
        DataUsageUtils.formatDataUsage(mContext, it.totalUsage).toString()
    }

    private val foregroundUsageFlow = dataFlow.map {
        DataUsageUtils.formatDataUsage(mContext, it.foregroundUsage).toString()
    }

    private val backgroundUsageFlow = dataFlow.map {
        DataUsageUtils.formatDataUsage(mContext, it.backgroundUsage).toString()
    }

    override fun getAvailabilityStatus() = AVAILABLE

    fun update(data: NetworkUsageDetailsData) {
        dataFlow.value = data
    }

    @Composable
    override fun Content() {
        Column {
            val totalUsage by totalUsageFlow.collectAsStateWithLifecycle(placeholder())
            val foregroundUsage by foregroundUsageFlow.collectAsStateWithLifecycle(placeholder())
            val backgroundUsage by backgroundUsageFlow.collectAsStateWithLifecycle(placeholder())
            Preference(object : PreferenceModel {
                override val title = stringResource(R.string.total_size_label)
                override val summary = { totalUsage }
            })
            Preference(object : PreferenceModel {
                override val title = stringResource(R.string.data_usage_label_foreground)
                override val summary = { foregroundUsage }
            })
            Preference(object : PreferenceModel {
                override val title = stringResource(R.string.data_usage_label_background)
                override val summary = { backgroundUsage }
            })
        }
    }
}
+0 −31
Original line number Diff line number Diff line
@@ -42,16 +42,13 @@ import android.os.Bundle;
import android.os.Process;
import android.telephony.SubscriptionManager;
import android.util.ArraySet;
import android.util.Range;

import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;

import com.android.settings.applications.AppInfoBase;
import com.android.settings.datausage.lib.NetworkUsageDetailsData;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowDataUsageUtils;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
@@ -252,34 +249,6 @@ public class AppDataUsageTest {
        verify(unrestrictedDataPref).setDisabledByAdmin(any(EnforcedAdmin.class));
    }

    @Test
    public void bindData_shouldUpdateUsageSummary() {
        mFragment = spy(new TestFragment());
        final Context context = RuntimeEnvironment.application;
        ReflectionHelpers.setField(mFragment, "mContext", context);
        final long backgroundBytes = 1234L;
        final long foregroundBytes = 5678L;
        final NetworkUsageDetailsData appUsage = new NetworkUsageDetailsData(
                new Range<>(1L, 2L),
                backgroundBytes + foregroundBytes,
                foregroundBytes,
                backgroundBytes
        );
        final Preference backgroundPref = mock(Preference.class);
        ReflectionHelpers.setField(mFragment, "mBackgroundUsage", backgroundPref);
        final Preference foregroundPref = mock(Preference.class);
        ReflectionHelpers.setField(mFragment, "mForegroundUsage", foregroundPref);
        final Preference totalPref = mock(Preference.class);
        ReflectionHelpers.setField(mFragment, "mTotalUsage", totalPref);

        mFragment.bindData(appUsage);

        verify(totalPref).setSummary(
                DataUsageUtils.formatDataUsage(context, backgroundBytes + foregroundBytes));
        verify(backgroundPref).setSummary(DataUsageUtils.formatDataUsage(context, backgroundBytes));
        verify(foregroundPref).setSummary(DataUsageUtils.formatDataUsage(context, foregroundBytes));
    }

    @Test
    @Config(shadows = {ShadowDataUsageUtils.class, ShadowSubscriptionManager.class,
            ShadowFragment.class})
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.content.Context
import android.util.Range
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.lib.NetworkUsageDetailsData
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class AppDataUsageSummaryControllerTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    private val context: Context = ApplicationProvider.getApplicationContext()

    private val controller = AppDataUsageSummaryController(context, TEST_KEY)

    @Test
    fun summary() {
        val appUsage = NetworkUsageDetailsData(
            range = Range(1L, 2L),
            totalUsage = BACKGROUND_BYTES + FOREGROUND_BYTES,
            foregroundUsage = FOREGROUND_BYTES,
            backgroundUsage = BACKGROUND_BYTES,
        )

        controller.update(appUsage)
        composeTestRule.setContent {
            controller.Content()
        }

        composeTestRule.onNodeWithText("6.75 kB").assertIsDisplayed()
        composeTestRule.onNodeWithText("5.54 kB").assertIsDisplayed()
        composeTestRule.onNodeWithText("1.21 kB").assertIsDisplayed()
    }

    private companion object {
        const val TEST_KEY = "test_key"
        const val BACKGROUND_BYTES = 1234L
        const val FOREGROUND_BYTES = 5678L
    }
}