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

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

Merge "Fix flicker for Mobile data & Wi-Fi page" into tm-dev

parents 1d0f85a3 ddedb31f
Loading
Loading
Loading
Loading
+7 −5
Original line number Original line Diff line number Diff line
@@ -21,8 +21,7 @@
    android:title="@string/data_usage_app_summary_title">
    android:title="@string/data_usage_app_summary_title">


    <com.android.settings.datausage.SpinnerPreference
    <com.android.settings.datausage.SpinnerPreference
        android:key="cycle"
        android:key="cycle" />
        settings:isPreferenceVisible="false" />


    <PreferenceCategory
    <PreferenceCategory
        android:key="app_data_usage_summary_category">
        android:key="app_data_usage_summary_category">
@@ -31,19 +30,22 @@
            android:key="total_usage"
            android:key="total_usage"
            android:title="@string/total_size_label"
            android:title="@string/total_size_label"
            android:selectable="false"
            android:selectable="false"
            android:layout="@layout/horizontal_preference" />
            android:layout="@layout/horizontal_preference"
            android:summary="@string/summary_placeholder" />


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


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


    </PreferenceCategory>
    </PreferenceCategory>


+18 −8
Original line number Original line Diff line number Diff line
@@ -144,8 +144,7 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
        mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
        mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
        mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);
        mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);


        mCycle = findPreference(KEY_CYCLE);
        initCycle();
        mCycleAdapter = new CycleAdapter(mContext, mCycle, mCycleListener);


        final UidDetailProvider uidDetailProvider = getUidDetailProvider();
        final UidDetailProvider uidDetailProvider = getUidDetailProvider();


@@ -211,6 +210,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
            removePreference(KEY_RESTRICT_BACKGROUND);
            removePreference(KEY_RESTRICT_BACKGROUND);
            removePreference(KEY_APP_LIST);
            removePreference(KEY_APP_LIST);
        }
        }

        addEntityHeader();
    }
    }


    @Override
    @Override
@@ -276,6 +277,17 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
        return new UidDetailProvider(mContext);
        return new UidDetailProvider(mContext);
    }
    }


    private void initCycle() {
        mCycle = findPreference(KEY_CYCLE);
        mCycleAdapter = new CycleAdapter(mContext, mCycle, mCycleListener);
        if (mCycles != null) {
            // If coming from a page like DataUsageList where already has a selected cycle, display
            // that before loading to reduce flicker.
            mCycleAdapter.setInitialCycleList(mCycles, mSelectedCycle);
            mCycle.setHasCycles(true);
        }
    }

    private void updatePrefs(boolean restrictBackground, boolean unrestrictData) {
    private void updatePrefs(boolean restrictBackground, boolean unrestrictData) {
        final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfMeteredDataRestricted(
        final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfMeteredDataRestricted(
                mContext, mPackageName, UserHandle.getUserId(mAppItem.key));
                mContext, mPackageName, UserHandle.getUserId(mAppItem.key));
@@ -308,9 +320,9 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
        final long backgroundBytes, foregroundBytes;
        final long backgroundBytes, foregroundBytes;
        if (mUsageData == null || position >= mUsageData.size()) {
        if (mUsageData == null || position >= mUsageData.size()) {
            backgroundBytes = foregroundBytes = 0;
            backgroundBytes = foregroundBytes = 0;
            mCycle.setVisible(false);
            mCycle.setHasCycles(false);
        } else {
        } else {
            mCycle.setVisible(true);
            mCycle.setHasCycles(true);
            final NetworkCycleDataForUid data = mUsageData.get(position);
            final NetworkCycleDataForUid data = mUsageData.get(position);
            backgroundBytes = data.getBackgroudUsage();
            backgroundBytes = data.getBackgroudUsage();
            foregroundBytes = data.getForegroudUsage();
            foregroundBytes = data.getForegroudUsage();
@@ -335,10 +347,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
        return false;
        return false;
    }
    }


    @Override
    @VisibleForTesting
    public void onViewCreated(View view, Bundle savedInstanceState) {
    void addEntityHeader() {
        super.onViewCreated(view, savedInstanceState);

        String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
        String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
        int uid = 0;
        int uid = 0;
        if (pkg != null) {
        if (pkg != null) {
+6 −135
Original line number Original line Diff line number Diff line
@@ -13,24 +13,13 @@
 */
 */
package com.android.settings.datausage;
package com.android.settings.datausage;


import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.content.Context;
import android.content.Context;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.text.format.DateUtils;
import android.util.Pair;
import android.util.Range;
import android.widget.AdapterView;
import android.widget.AdapterView;


import com.android.net.module.util.NetworkStatsUtils;
import com.android.settings.Utils;
import com.android.settings.Utils;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.NetworkCycleData;
import com.android.settingslib.net.NetworkCycleData;
import com.android.settingslib.widget.SettingsSpinnerAdapter;
import com.android.settingslib.widget.SettingsSpinnerAdapter;


import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Objects;


@@ -45,7 +34,6 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem>
        mSpinner = spinner;
        mSpinner = spinner;
        mListener = listener;
        mListener = listener;
        mSpinner.setAdapter(this);
        mSpinner.setAdapter(this);
        mSpinner.setOnItemSelectedListener(mListener);
    }
    }


    /**
    /**
@@ -65,128 +53,14 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem>
        return 0;
        return 0;
    }
    }


    protected static long getTotalBytesForTimeRange(List<NetworkStats.Bucket> stats,
    void setInitialCycleList(List<Long> cycles, long selectedCycle) {
            Range<Long> range) {
        long bytes = 0L;
        for (NetworkStats.Bucket bucket : stats) {
            final Range<Long> bucketSpan = new Range<>(
                    bucket.getStartTimeStamp(), bucket.getEndTimeStamp());
            // Only record bytes that overlapped with the given time range. For partially
            // overlapped bucket, record rational bytes assuming the traffic is uniform
            // distributed within the bucket.
            try {
                final Range<Long> overlapped = range.intersect(bucketSpan);
                final long totalOfBucket = bucket.getRxBytes() + bucket.getTxBytes();
                bytes += NetworkStatsUtils.multiplySafeByRational(totalOfBucket,
                        overlapped.getUpper() - overlapped.getLower(),
                        bucketSpan.getUpper() - bucketSpan.getLower());
            } catch (IllegalArgumentException e) {
                // Range disjoint, ignore.
                continue;
            }
        }
        return bytes;
    }

    @NonNull
    private Range getTimeRangeOf(@NonNull List<NetworkStats.Bucket> stats) {
        long start = Long.MAX_VALUE;
        long end = Long.MIN_VALUE;
        for (NetworkStats.Bucket bucket : stats) {
            start = Math.min(start, bucket.getStartTimeStamp());
            end = Math.max(end, bucket.getEndTimeStamp());
        }
        return new Range(start, end);
    }

    /**
     * Rebuild list based on {@link NetworkPolicy} and available
     * {@link List<NetworkStats.Bucket>} data. Always selects the newest item,
     * updating the inspection range on chartData.
     */
    @Deprecated
    public boolean updateCycleList(NetworkPolicy policy, ChartData chartData) {
        // stash away currently selected cycle to try restoring below
        final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
                mSpinner.getSelectedItem();
        clear();
        clear();

        for (int i = 0; i < cycles.size() - 1; i++) {
        final Context context = getContext();
            add(new CycleAdapter.CycleItem(getContext(), cycles.get(i + 1), cycles.get(i)));

            if (cycles.get(i) == selectedCycle) {
        long historyStart;
                mSpinner.setSelection(i);
        long historyEnd;
        try {
            final Range<Long> historyTimeRange = getTimeRangeOf(chartData.network);
            historyStart = historyTimeRange.getLower();
            historyEnd = historyTimeRange.getUpper();
        } catch (IllegalArgumentException e) {
            // Empty history.
            final long now = System.currentTimeMillis();
            historyStart = now;
            historyEnd = now + 1;
        }

        boolean hasCycles = false;
        if (policy != null) {
            final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = NetworkPolicyManager
                    .cycleIterator(policy);
            while (it.hasNext()) {
                final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
                final long cycleStart = cycle.first.toInstant().toEpochMilli();
                final long cycleEnd = cycle.second.toInstant().toEpochMilli();

                final boolean includeCycle;
                if (chartData != null) {
                    final long bytesInCycle = getTotalBytesForTimeRange(chartData.network,
                            new Range<>(cycleStart, cycleEnd));
                    includeCycle = bytesInCycle > 0;
                } else {
                    includeCycle = true;
                }

                if (includeCycle) {
                    add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
                    hasCycles = true;
                }
            }
        }

        if (!hasCycles) {
            // no policy defined cycles; show entry for each four-week period
            long cycleEnd = historyEnd;
            while (cycleEnd > historyStart) {
                final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);

                final boolean includeCycle;
                if (chartData != null) {
                    final long bytesInCycle = getTotalBytesForTimeRange(chartData.network,
                            new Range<>(cycleStart, cycleEnd));
                    includeCycle = bytesInCycle > 0;
                } else {
                    includeCycle = true;
                }

                if (includeCycle) {
                    add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
                }
                cycleEnd = cycleStart;
            }
        }

        // force pick the current cycle (first item)
        if (getCount() > 0) {
            final int position = findNearestPosition(previousItem);
            mSpinner.setSelection(position);

            // only force-update cycle when changed; skipping preserves any
            // user-defined inspection region.
            final CycleAdapter.CycleItem selectedItem = getItem(position);
            if (!Objects.equals(selectedItem, previousItem)) {
                mListener.onItemSelected(null, null, position, 0);
                return false;
            }
            }
        }
        }
        return true;
    }
    }


    /**
    /**
@@ -194,6 +68,7 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem>
     * updating the inspection range on chartData.
     * updating the inspection range on chartData.
     */
     */
    public boolean updateCycleList(List<? extends NetworkCycleData> cycleData) {
    public boolean updateCycleList(List<? extends NetworkCycleData> cycleData) {
        mSpinner.setOnItemSelectedListener(mListener);
        // stash away currently selected cycle to try restoring below
        // stash away currently selected cycle to try restoring below
        final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
        final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
                mSpinner.getSelectedItem();
                mSpinner.getSelectedItem();
@@ -228,10 +103,6 @@ public class CycleAdapter extends SettingsSpinnerAdapter<CycleAdapter.CycleItem>
        public long start;
        public long start;
        public long end;
        public long end;


        public CycleItem(CharSequence label) {
            this.label = label;
        }

        public CycleItem(Context context, long start, long end) {
        public CycleItem(Context context, long start, long end) {
            this.label = Utils.formatDateRange(context, start, end);
            this.label = Utils.formatDateRange(context, start, end);
            this.start = start;
            this.start = start;
+14 −0
Original line number Original line Diff line number Diff line
@@ -31,6 +31,8 @@ public class SpinnerPreference extends Preference implements CycleAdapter.Spinne
    private AdapterView.OnItemSelectedListener mListener;
    private AdapterView.OnItemSelectedListener mListener;
    private Object mCurrentObject;
    private Object mCurrentObject;
    private int mPosition;
    private int mPosition;
    private View mItemView;
    private boolean mItemViewVisible = false;


    public SpinnerPreference(Context context, AttributeSet attrs) {
    public SpinnerPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        super(context, attrs);
@@ -63,12 +65,24 @@ public class SpinnerPreference extends Preference implements CycleAdapter.Spinne
    @Override
    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        super.onBindViewHolder(holder);
        mItemView = holder.itemView;
        mItemView.setVisibility(mItemViewVisible ? View.VISIBLE : View.INVISIBLE);
        Spinner spinner = (Spinner) holder.findViewById(R.id.cycles_spinner);
        Spinner spinner = (Spinner) holder.findViewById(R.id.cycles_spinner);
        spinner.setAdapter(mAdapter);
        spinner.setAdapter(mAdapter);
        spinner.setSelection(mPosition);
        spinner.setSelection(mPosition);
        spinner.setOnItemSelectedListener(mOnSelectedListener);
        spinner.setOnItemSelectedListener(mOnSelectedListener);
    }
    }


    void setHasCycles(boolean hasData) {
        setVisible(hasData);
        if (hasData) {
            mItemViewVisible = true;
            if (mItemView != null) {
                mItemView.setVisibility(View.VISIBLE);
            }
        }
    }

    @Override
    @Override
    protected void performClick(View view) {
    protected void performClick(View view) {
        view.findViewById(R.id.cycles_spinner).performClick();
        view.findViewById(R.id.cycles_spinner).performClick();
+8 −12
Original line number Original line Diff line number Diff line
@@ -43,7 +43,6 @@ import android.os.Process;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager;
import android.text.format.DateUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.ArraySet;
import android.view.View;


import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import androidx.preference.Preference;
@@ -96,6 +95,10 @@ public class AppDataUsageTest {
    @Before
    @Before
    public void setUp() {
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);

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


    @After
    @After
@@ -163,10 +166,6 @@ public class AppDataUsageTest {


    @Test
    @Test
    public void bindAppHeader_allWorkApps_shouldNotShowAppInfoLink() {
    public void bindAppHeader_allWorkApps_shouldNotShowAppInfoLink() {
        ShadowEntityHeaderController.setUseMock(mHeaderController);
        when(mHeaderController.setRecyclerView(any(), any())).thenReturn(mHeaderController);
        when(mHeaderController.setUid(anyInt())).thenReturn(mHeaderController);

        mFragment = spy(new AppDataUsage());
        mFragment = spy(new AppDataUsage());


        when(mFragment.getPreferenceManager())
        when(mFragment.getPreferenceManager())
@@ -174,7 +173,7 @@ public class AppDataUsageTest {
        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
        ReflectionHelpers.setField(mFragment, "mAppItem", mock(AppItem.class));
        ReflectionHelpers.setField(mFragment, "mAppItem", mock(AppItem.class));


        mFragment.onViewCreated(new View(RuntimeEnvironment.application), new Bundle());
        mFragment.addEntityHeader();


        verify(mHeaderController).setHasAppInfoLink(false);
        verify(mHeaderController).setHasAppInfoLink(false);
    }
    }
@@ -196,16 +195,13 @@ public class AppDataUsageTest {
        when(mPackageManager.getPackageUidAsUser(anyString(), anyInt()))
        when(mPackageManager.getPackageUidAsUser(anyString(), anyInt()))
                .thenReturn(fakeUserId);
                .thenReturn(fakeUserId);


        ShadowEntityHeaderController.setUseMock(mHeaderController);
        when(mHeaderController.setRecyclerView(any(), any())).thenReturn(mHeaderController);
        when(mHeaderController.setUid(fakeUserId)).thenReturn(mHeaderController);
        when(mHeaderController.setHasAppInfoLink(anyBoolean())).thenReturn(mHeaderController);
        when(mHeaderController.setHasAppInfoLink(anyBoolean())).thenReturn(mHeaderController);


        when(mFragment.getPreferenceManager())
        when(mFragment.getPreferenceManager())
                .thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS));
                .thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS));
        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
        doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();


        mFragment.onViewCreated(new View(RuntimeEnvironment.application), new Bundle());
        mFragment.addEntityHeader();


        verify(mHeaderController).setHasAppInfoLink(true);
        verify(mHeaderController).setHasAppInfoLink(true);
        verify(mHeaderController).setUid(fakeUserId);
        verify(mHeaderController).setUid(fakeUserId);
@@ -268,7 +264,7 @@ public class AppDataUsageTest {


        mFragment.bindData(0 /* position */);
        mFragment.bindData(0 /* position */);


        verify(cycle).setVisible(false);
        verify(cycle).setHasCycles(false);
    }
    }


    @Test
    @Test
@@ -293,7 +289,7 @@ public class AppDataUsageTest {


        mFragment.bindData(0 /* position */);
        mFragment.bindData(0 /* position */);


        verify(cycle).setVisible(true);
        verify(cycle).setHasCycles(true);
        verify(totalPref).setSummary(
        verify(totalPref).setSummary(
                DataUsageUtils.formatDataUsage(context, backgroundBytes + foregroundBytes));
                DataUsageUtils.formatDataUsage(context, backgroundBytes + foregroundBytes));
        verify(backgroundPref).setSummary(DataUsageUtils.formatDataUsage(context, backgroundBytes));
        verify(backgroundPref).setSummary(DataUsageUtils.formatDataUsage(context, backgroundBytes));