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

Commit c7635b17 authored by Yuchen's avatar Yuchen
Browse files

[Expressive design] Update AppDataUsage.

Migrate EntityHeader to IntroPReference.
Remove background of SpinnerPreference.

Bug: 349681531
Flag: EXEMPT migration
Test: atest AppDataUsageTest
Change-Id: Icb91c418a67489e3069f958356a9675f279ee5f8
parent 224c16db
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -20,11 +20,15 @@
    android:key="app_data_usage_screen"
    android:title="@string/data_usage_app_summary_title">

    <com.android.settingslib.widget.IntroPreference
        android:key="app_header"
        android:order="-10000"/>

    <com.android.settings.datausage.SpinnerPreference
        android:key="cycle"
        settings:controller="com.android.settings.datausage.AppDataUsageCycleController" />

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

+26 −22
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;

import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUid;
import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUidList;
import static com.android.settings.spa.app.appinfo.AppInfoSettingsProvider.startAppInfoSettings;

import android.app.Activity;
import android.app.settings.SettingsEnums;
@@ -45,13 +46,14 @@ import com.android.settings.datausage.lib.AppDataUsageDetailsRepository;
import com.android.settings.datausage.lib.NetworkTemplates;
import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider;
import com.android.settingslib.widget.IntroPreference;

import kotlin.Unit;

@@ -65,6 +67,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
    private static final String TAG = "AppDataUsage";

    static final String ARG_APP_ITEM = "app_item";
    @VisibleForTesting
    static final String ARG_APP_HEADER = "app_header";
    static final String ARG_NETWORK_TEMPLATE = "network_template";
    static final String ARG_NETWORK_CYCLES = "network_cycles";
    static final String ARG_SELECTED_CYCLE = "selected_cycle";
@@ -176,7 +180,7 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
            removePreference(KEY_RESTRICT_BACKGROUND);
        }

        addEntityHeader();
        setupIntroPreference();
    }

    @Override
@@ -320,32 +324,32 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
    }

    @VisibleForTesting
    void addEntityHeader() {
        String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
        int uid = 0;
        if (pkg != null) {
    void setupIntroPreference() {
        final Preference pref = getPreferenceScreen().findPreference(ARG_APP_HEADER);
        if (pref != null) {
            pref.setIcon(mIcon);
            pref.setTitle(mLabel);
            pref.setSelectable(true);
        }
    }

    @Override
    public boolean onPreferenceTreeClick(Preference preference) {
        if (!(preference instanceof IntroPreference)) return false;

        String pkg = !mPackages.isEmpty() ? mPackages.valueAt(0) : null;
        if (mAppItem.key > 0 && pkg != null) {
            try {
                uid = mPackageManager.getPackageUidAsUser(pkg,
                int uid = mPackageManager.getPackageUidAsUser(pkg,
                        UserHandle.getUserId(mAppItem.key));
                startAppInfoSettings(pkg, uid, this, 0 /* request */,
                        FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
                                .getMetricsCategory(this));
            } catch (PackageManager.NameNotFoundException e) {
                Log.w(TAG, "Skipping UID because cannot find package " + pkg);
            }
        }

        final boolean showInfoButton = mAppItem.key > 0;

        final Activity activity = getActivity();
        final Preference pref = EntityHeaderController
                .newInstance(activity, this, null /* header */)
                .setUid(uid)
                .setHasAppInfoLink(showInfoButton)
                .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
                        EntityHeaderController.ActionType.ACTION_NONE)
                .setIcon(mIcon)
                .setLabel(mLabel)
                .setPackageName(pkg)
                .done(getPrefContext());
        getPreferenceScreen().addPreference(pref);
        return true;
    }

    @Override
+2 −2
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
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
@@ -28,6 +27,7 @@ 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.spa.widget.ui.Category
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
@@ -60,7 +60,7 @@ class AppDataUsageSummaryController(context: Context, preferenceKey: String) :

    @Composable
    override fun Content() {
        Column {
        Category {
            val totalUsage by totalUsageFlow.collectAsStateWithLifecycle(emptyDataUsage)
            val foregroundUsage by foregroundUsageFlow.collectAsStateWithLifecycle(emptyDataUsage)
            val backgroundUsage by backgroundUsageFlow.collectAsStateWithLifecycle(emptyDataUsage)
+3 −1
Original line number Diff line number Diff line
@@ -25,8 +25,10 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

import com.android.settings.R;
import com.android.settingslib.widget.GroupSectionDividerMixin;

public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface {
public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface,
        GroupSectionDividerMixin {

    private CycleAdapter mAdapter;
    @Nullable
+34 −24
Original line number Diff line number Diff line
@@ -28,11 +28,13 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -42,6 +44,7 @@ import android.os.Bundle;
import android.os.Process;
import android.telephony.SubscriptionManager;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;

import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
@@ -51,22 +54,20 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowDataUsageUtils;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider;
import com.android.settingslib.widget.IntroPreference;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
@@ -79,27 +80,25 @@ import org.robolectric.util.ReflectionHelpers;
import java.util.List;

@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowEntityHeaderController.class, ShadowRestrictedLockUtilsInternal.class})
@Config(shadows = {ShadowRestrictedLockUtilsInternal.class})
public class AppDataUsageTest {

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private EntityHeaderController mHeaderController;
    @Mock
    private PackageManager mPackageManager;

    private IntroPreference mIntroPreference;

    private AppDataUsage mFragment;

    private Context mContext;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        ShadowEntityHeaderController.setUseMock(mHeaderController);
        when(mHeaderController.setUid(anyInt())).thenReturn(mHeaderController);
    }

    @After
    public void tearDown() {
        ShadowEntityHeaderController.reset();
        mContext = spy(RuntimeEnvironment.application);
        mIntroPreference = new IntroPreference(mContext);
        FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_ENABLE_SPA, true);
    }

    @Test
@@ -161,6 +160,7 @@ public class AppDataUsageTest {
    }

    @Test
    @Config(shadows = ShadowFragment.class)
    public void bindAppHeader_allWorkApps_shouldNotShowAppInfoLink() {
        mFragment = spy(new TestFragment());

@@ -169,12 +169,20 @@ public class AppDataUsageTest {
        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
        ReflectionHelpers.setField(mFragment, "mAppItem", mock(AppItem.class));

        mFragment.addEntityHeader();
        when(mFragment.getPreferenceScreen().findPreference(AppDataUsage.ARG_APP_HEADER))
                .thenReturn(mIntroPreference);
        when(mFragment.getContext()).thenReturn(mContext);
        doNothing().when(mContext).startActivity(any());

        verify(mHeaderController).setHasAppInfoLink(false);
        mFragment.setupIntroPreference();
        mFragment.onPreferenceTreeClick(mIntroPreference);

        verify(mFragment, never()).getActivity();
        verify(mContext, never()).startActivity(any(Intent.class));
    }

    @Test
    @Config(shadows = ShadowFragment.class)
    public void bindAppHeader_workApp_shouldSetWorkAppUid()
            throws PackageManager.NameNotFoundException {
        final int fakeUserId = 100;
@@ -188,19 +196,21 @@ public class AppDataUsageTest {
        ReflectionHelpers.setField(mFragment, "mAppItem", appItem);
        ReflectionHelpers.setField(mFragment, "mPackages", packages);

        when(mPackageManager.getPackageUidAsUser(anyString(), anyInt()))
                .thenReturn(fakeUserId);

        when(mHeaderController.setHasAppInfoLink(anyBoolean())).thenReturn(mHeaderController);

        when(mPackageManager.getPackageUidAsUser(anyString(), anyInt())).thenReturn(fakeUserId);
        when(mFragment.getPreferenceManager())
                .thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS));
        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();

        mFragment.addEntityHeader();
        when(mFragment.getPreferenceScreen().findPreference(AppDataUsage.ARG_APP_HEADER))
                .thenReturn(mIntroPreference);
        when(mFragment.getContext()).thenReturn(mContext);
        doNothing().when(mContext).startActivity(any());

        mFragment.setupIntroPreference();
        mFragment.onPreferenceTreeClick(mIntroPreference);

        verify(mHeaderController).setHasAppInfoLink(true);
        verify(mHeaderController).setUid(fakeUserId);
        ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext).startActivity(argumentCaptor.capture());
    }

    @Test