diff --git a/res/xml/app_data_usage.xml b/res/xml/app_data_usage.xml index 034015cbb72..88f60ef6ca7 100644 --- a/res/xml/app_data_usage.xml +++ b/res/xml/app_data_usage.xml @@ -24,31 +24,9 @@ android:key="cycle" settings:controller="com.android.settings.datausage.AppDataUsageCycleController" /> - - - - - - - - - + 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 uidList = getAppUidList(mAppItem.uids); initCycle(uidList); @@ -255,15 +244,17 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC @VisibleForTesting void initCycle(List 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); diff --git a/src/com/android/settings/datausage/AppDataUsageSummaryController.kt b/src/com/android/settings/datausage/AppDataUsageSummaryController.kt new file mode 100644 index 00000000000..a764c1d6cfe --- /dev/null +++ b/src/com/android/settings/datausage/AppDataUsageSummaryController.kt @@ -0,0 +1,77 @@ +/* + * 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 } + }) + } + } +} diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java index 1d841fa2505..4b8c9de5349 100644 --- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java +++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java @@ -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}) diff --git a/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageSummaryControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageSummaryControllerTest.kt new file mode 100644 index 00000000000..584295649aa --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageSummaryControllerTest.kt @@ -0,0 +1,65 @@ +/* + * 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 + } +}