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

Commit 4cb19e74 authored by jackqdyulei's avatar jackqdyulei
Browse files

Add anomaly icon for PowerGaugePreference

When the app contains anomaly, we should show anomaly icon(triangle
alert).

This cl also extracts a new method refreshAppListGroup from refreshUi.
New method is used to only refresh appListGroup. Reason for this action:

1. Improve performance(partial refresh)
2. We init AnomalyLoader in refreshUi, invoke refreshUi in
onLoadFinish will create infinite loop.

Bug: 36924669
Test: RunSettingsRoboTests
Change-Id: If54c89349e21763ca714123ac6ae884bd0f6a377
parent b45a9fe0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@
    android:id="@+id/widget_summary"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:drawablePadding="8dp"
    android:gravity="center_vertical|end"
    android:textAlignment="viewEnd"
    android:textAppearance="?android:attr/textAppearanceSmall"
    android:textColor="?android:attr/textColorSecondary" />
+14 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -41,6 +42,7 @@ public class PowerGaugePreference extends TintablePreference {
    private BatteryEntry mInfo;
    private CharSequence mContentDescription;
    private CharSequence mProgress;
    private boolean mShowAnomalyIcon;

    public PowerGaugePreference(Context context, Drawable icon, CharSequence contentDescription,
            BatteryEntry info) {
@@ -63,6 +65,7 @@ public class PowerGaugePreference extends TintablePreference {
        mInfo = info;
        mContentDescription = contentDescription;
        mIconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
        mShowAnomalyIcon = false;
    }

    public void setContentDescription(String name) {
@@ -88,6 +91,11 @@ public class PowerGaugePreference extends TintablePreference {
        return mProgress;
    }

    public void shouldShowAnomalyIcon(boolean showAnomalyIcon) {
        mShowAnomalyIcon = showAnomalyIcon;
        notifyChanged();
    }

    BatteryEntry getInfo() {
        return mInfo;
    }
@@ -98,6 +106,12 @@ public class PowerGaugePreference extends TintablePreference {
        ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
        icon.setLayoutParams(new LinearLayout.LayoutParams(mIconSize, mIconSize));

        final TextView subtitle = (TextView) view.findViewById(R.id.widget_summary);
        subtitle.setText(mProgress);
        if (mShowAnomalyIcon) {
            subtitle.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_warning_24dp, 0,
                    0, 0);
        }
        ((TextView) view.findViewById(R.id.widget_summary)).setText(mProgress);
        if (mContentDescription != null) {
            final TextView titleView = (TextView) view.findViewById(android.R.id.title);
+44 −14
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Loader;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Build;
@@ -122,6 +123,11 @@ public class PowerUsageSummary extends PowerUsageBase implements
    PowerUsageFeatureProvider mPowerFeatureProvider;
    @VisibleForTesting
    BatteryUtils mBatteryUtils;
    /**
     * SparseArray that maps uid to {@link Anomaly}, so we could find {@link Anomaly} by uid
     */
    @VisibleForTesting
    SparseArray<List<Anomaly>> mAnomalySparseArray;

    private LayoutPreference mBatteryLayoutPref;
    private PreferenceGroup mAppListGroup;
@@ -140,6 +146,9 @@ public class PowerUsageSummary extends PowerUsageBase implements
                public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
                    // show high usage preference if possible
                    mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);

                    updateAnomalySparseArray(data);
                    refreshAppListGroup();
                }

                @Override
@@ -162,6 +171,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
        mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
                (SettingsActivity) getActivity(), this);
        mBatteryUtils = BatteryUtils.getInstance(getContext());
        mAnomalySparseArray = new SparseArray<>();

        initFeatureProvider();
    }
@@ -438,14 +448,6 @@ public class PowerUsageSummary extends PowerUsageBase implements

        getLoaderManager().initLoader(ANOMALY_LOADER, null, mAnomalyLoaderCallbacks);

        cacheRemoveAllPrefs(mAppListGroup);
        mAppListGroup.setOrderingAsAdded(false);
        boolean addedSome = false;

        final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
        final BatteryStats stats = mStatsHelper.getStats();
        final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);

        final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
        Intent batteryBroadcast = context.registerReceiver(null,
                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
@@ -453,12 +455,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
                mStatsHelper.getStats(), elapsedRealtimeUs, false);
        updateHeaderPreference(batteryInfo);

        final TypedValue value = new TypedValue();
        context.getTheme().resolveAttribute(android.R.attr.colorControlNormal, value, true);
        final int colorControl = context.getColor(value.resourceId);
        final int dischargeAmount = USE_FAKE_DATA ? 5000
                : stats != null ? stats.getDischargeAmount(mStatsType) : 0;

        final long runningTime = calculateRunningTimeBasedOnStatsType();
        updateScreenPreference();
        updateLastFullChargePreference(runningTime);
@@ -467,6 +463,27 @@ public class PowerUsageSummary extends PowerUsageBase implements
        mAppListGroup.setTitle(
                TextUtils.expandTemplate(getText(R.string.power_usage_list_summary), timeSequence));

        refreshAppListGroup();
    }

    private void refreshAppListGroup() {
        final Context context = getContext();
        final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
        final BatteryStats stats = mStatsHelper.getStats();
        final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
        boolean addedSome = false;

        TypedArray array = context.obtainStyledAttributes(
                new int[]{android.R.attr.colorControlNormal});
        final int colorControl = array.getColor(0, 0);
        array.recycle();

        final int dischargeAmount = USE_FAKE_DATA ? 5000
                : stats != null ? stats.getDischargeAmount(mStatsType) : 0;

        cacheRemoveAllPrefs(mAppListGroup);
        mAppListGroup.setOrderingAsAdded(false);

        if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
            final List<BatterySipper> usageList = getCoalescedUsageList(
                    USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());
@@ -532,6 +549,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
                pref.setTitle(entry.getLabel());
                pref.setOrder(i + 1);
                pref.setPercent(percentOfTotal);
                pref.shouldShowAnomalyIcon(mAnomalySparseArray.get(sipper.getUid()) != null);
                if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
                    sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
                            BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
@@ -659,6 +677,18 @@ public class PowerUsageSummary extends PowerUsageBase implements
                .getPowerUsageFeatureProvider(context);
    }

    @VisibleForTesting
    void updateAnomalySparseArray(List<Anomaly> anomalies) {
        mAnomalySparseArray.clear();
        for (int i = 0, size = anomalies.size(); i < size; i++) {
            final Anomaly anomaly = anomalies.get(i);
            if (mAnomalySparseArray.get(anomaly.uid) == null) {
                mAnomalySparseArray.append(anomaly.uid, new ArrayList<>());
            }
            mAnomalySparseArray.get(anomaly.uid).add(anomaly);
        }
    }

    private static List<BatterySipper> getFakeStats() {
        ArrayList<BatterySipper> stats = new ArrayList<>();
        float use = 5;
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.fuelgauge;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
import android.os.PowerManager;
import android.support.v7.preference.PreferenceViewHolder;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.widget.MasterSwitchPreference;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class PowerGaugePreferenceTest {
    private static final String SUBTITLE = "Summary";
    private static final String CONTENT_DESCRIPTION = "Content description";
    private Context mContext;
    private PowerGaugePreference mPowerGaugePreference;
    private View mRootView;
    private View mWidgetView;
    private PreferenceViewHolder mPreferenceViewHolder;

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

        mContext = RuntimeEnvironment.application;
        mRootView = LayoutInflater.from(mContext).inflate(R.layout.preference_material_settings,
                null);
        mWidgetView = LayoutInflater.from(mContext).inflate(R.layout.preference_widget_summary,
                null);
        ((LinearLayout) mRootView.findViewById(android.R.id.widget_frame)).addView(mWidgetView);
        mPreferenceViewHolder = PreferenceViewHolder.createInstanceForTests(mRootView);

        mPowerGaugePreference = new PowerGaugePreference(mContext);
    }

    @Test
    public void testOnBindViewHolder_bindSubtitle() {
        mPowerGaugePreference.setSubtitle(SUBTITLE);
        mPowerGaugePreference.onBindViewHolder(mPreferenceViewHolder);

        assertThat(((TextView) mPreferenceViewHolder.findViewById(
                R.id.widget_summary)).getText()).isEqualTo(SUBTITLE);
    }

    @Test
    public void testOnBindViewHolder_bindAnomalyIcon() {
        mPowerGaugePreference.shouldShowAnomalyIcon(true);
        mPowerGaugePreference.onBindViewHolder(mPreferenceViewHolder);

        final Drawable[] drawables = ((TextView) mPreferenceViewHolder.findViewById(
                R.id.widget_summary)).getCompoundDrawablesRelative();

        assertThat(drawables[0]).isInstanceOf(VectorDrawable.class);
    }

    @Test
    public void testOnBindViewHolder_bindContentDescription() {
        mPowerGaugePreference.setContentDescription(CONTENT_DESCRIPTION);
        mPowerGaugePreference.onBindViewHolder(mPreferenceViewHolder);

        assertThat(mPreferenceViewHolder.findViewById(android.R.id.title).getContentDescription())
                .isEqualTo(CONTENT_DESCRIPTION);
    }
}
+22 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -37,6 +38,7 @@ import com.android.settings.TestConfig;
import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.PreferenceController;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
@@ -91,6 +93,7 @@ public class PowerUsageSummaryTest {
    private static final String STUB_STRING = "stub_string";
    private static final int BATTERY_LEVEL = 55;
    private static final int UID = 123;
    private static final int UID_2 = 234;
    private static final int POWER_MAH = 100;
    private static final long REMAINING_TIME_US = 100000;
    private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
@@ -148,6 +151,7 @@ public class PowerUsageSummaryTest {
    private PowerGaugePreference mPreference;
    private PowerGaugePreference mScreenUsagePref;
    private PowerGaugePreference mLastFullChargePref;
    private SparseArray<List<Anomaly>> mAnomalySparseArray;

    @Before
    public void setUp() {
@@ -464,6 +468,24 @@ public class PowerUsageSummaryTest {
        assertThat(preferenceScreenKeys).containsAllIn(preferenceKeys);
    }

    @Test
    public void testUpdateAnomalySparseArray() {
        mFragment.mAnomalySparseArray = new SparseArray<>();
        final List<Anomaly> anomalies = new ArrayList<>();
        final Anomaly anomaly1 = new Anomaly.Builder().setUid(UID).build();
        final Anomaly anomaly2 = new Anomaly.Builder().setUid(UID).build();
        final Anomaly anomaly3 = new Anomaly.Builder().setUid(UID_2).build();
        anomalies.add(anomaly1);
        anomalies.add(anomaly2);
        anomalies.add(anomaly3);

        mFragment.updateAnomalySparseArray(anomalies);

        assertThat(mFragment.mAnomalySparseArray.get(UID)).containsExactly(anomaly1, anomaly2);
        assertThat(mFragment.mAnomalySparseArray.get(UID_2)).containsExactly(anomaly3);
    }


    public static class TestFragment extends PowerUsageSummary {

        private Context mContext;