diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java index a61584168bc..ebb4790cd96 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java @@ -71,7 +71,7 @@ public class BatteryTipLoader extends AsyncLoader> { tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect()); tips.add(new EarlyWarningDetector(policy, context).detect()); tips.add(new SummaryDetector(policy).detect()); - tips.add(new RestrictAppDetector(policy).detect()); + tips.add(new RestrictAppDetector(context, policy).detect()); Collections.sort(tips); return tips; diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java index 9c49822bb18..886a6d534e6 100644 --- a/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java +++ b/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java @@ -21,7 +21,9 @@ import android.content.Context; import android.support.annotation.VisibleForTesting; import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; import java.util.List; @@ -32,12 +34,15 @@ import java.util.List; public class RestrictAppAction extends BatteryTipAction { private RestrictAppTip mRestrictAppTip; @VisibleForTesting + BatteryDatabaseManager mBatteryDatabaseManager; + @VisibleForTesting BatteryUtils mBatteryUtils; public RestrictAppAction(Context context, RestrictAppTip tip) { super(context); mRestrictAppTip = tip; mBatteryUtils = BatteryUtils.getInstance(context); + mBatteryDatabaseManager = new BatteryDatabaseManager(context); } /** @@ -53,5 +58,7 @@ public class RestrictAppAction extends BatteryTipAction { mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName, AppOpsManager.MODE_IGNORED); } + + mBatteryDatabaseManager.updateAnomalies(appInfos, AnomalyDatabaseHelper.State.HANDLED); } } diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java index 46e241a24d5..e3c9b9ed612 100644 --- a/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java @@ -16,7 +16,13 @@ package com.android.settings.fuelgauge.batterytip.detectors; +import android.content.Context; +import android.support.annotation.VisibleForTesting; +import android.text.format.DateUtils; + +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; @@ -29,18 +35,47 @@ import java.util.List; * {@link BatteryTipDetector} since it need the most up-to-date {@code visibleTips} */ public class RestrictAppDetector implements BatteryTipDetector { + @VisibleForTesting + static final boolean USE_FAKE_DATA = false; private BatteryTipPolicy mPolicy; + @VisibleForTesting + BatteryDatabaseManager mBatteryDatabaseManager; - public RestrictAppDetector(BatteryTipPolicy policy) { + public RestrictAppDetector(Context context, BatteryTipPolicy policy) { mPolicy = policy; + mBatteryDatabaseManager = new BatteryDatabaseManager(context); } @Override public BatteryTip detect() { - // TODO(b/70570352): Detect restrict apps here, get data from database + if (USE_FAKE_DATA) { + return getFakeData(); + } + if (mPolicy.appRestrictionEnabled) { + // TODO(b/72385333): hook up the query timestamp to server side + final long oneDayBeforeMs = System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS; + final List highUsageApps = mBatteryDatabaseManager.queryAllAnomalies( + oneDayBeforeMs, AnomalyDatabaseHelper.State.NEW); + if (!highUsageApps.isEmpty()) { + // If there are new anomalies, show them + return new RestrictAppTip(BatteryTip.StateType.NEW, highUsageApps); + } else { + // Otherwise, show auto-handled one if it exists + final List autoHandledApps = mBatteryDatabaseManager.queryAllAnomalies( + oneDayBeforeMs, AnomalyDatabaseHelper.State.AUTO_HANDLED); + return new RestrictAppTip(autoHandledApps.isEmpty() ? BatteryTip.StateType.INVISIBLE + : BatteryTip.StateType.HANDLED, autoHandledApps); + } + } else { + return new RestrictAppTip(BatteryTip.StateType.INVISIBLE, new ArrayList<>()); + } + } + + private BatteryTip getFakeData() { final List highUsageApps = new ArrayList<>(); - return new RestrictAppTip( - highUsageApps.isEmpty() ? BatteryTip.StateType.INVISIBLE : BatteryTip.StateType.NEW, - highUsageApps); + highUsageApps.add(new AppInfo.Builder() + .setPackageName("com.android.settings") + .build()); + return new RestrictAppTip(BatteryTip.StateType.NEW, highUsageApps); } } diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java index 1d84d7fb94b..a40f292a0eb 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java @@ -81,7 +81,14 @@ public class RestrictAppTip extends BatteryTip { @Override public void updateState(BatteryTip tip) { - mState = tip.mState; + if (tip.mState == StateType.NEW) { + // Display it if new anomaly comes + mState = StateType.NEW; + mRestrictAppList = ((RestrictAppTip) tip).mRestrictAppList; + } else if (mState == StateType.NEW && tip.mState == StateType.INVISIBLE) { + // If anomaly becomes invisible, show it as handled + mState = StateType.HANDLED; + } } public List getRestrictAppList() { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java index 47785d555b3..728bbff991d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java @@ -27,8 +27,10 @@ import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.AppInfo; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; +import com.android.settings.testutils.DatabaseTestUtils; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -69,6 +71,11 @@ public class RestrictAppActionTest { mRestrictAppAction.mBatteryUtils = mBatteryUtils; } + @After + public void cleanUp() { + DatabaseTestUtils.clearDb(mContext); + } + @Test public void testHandlePositiveAction() { mRestrictAppAction.handlePositiveAction(); @@ -79,5 +86,4 @@ public class RestrictAppActionTest { eq(AppOpsManager.MODE_IGNORED)); } - } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetectorTest.java new file mode 100644 index 00000000000..6a25c6d26df --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetectorTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 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.batterytip.detectors; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; + +import com.android.settings.TestConfig; +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; +import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; +import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; +import com.android.settings.testutils.DatabaseTestUtils; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.After; +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; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class RestrictAppDetectorTest { + private static final String PACKAGE_NAME = "com.android.app"; + private Context mContext; + private BatteryTipPolicy mPolicy; + private RestrictAppDetector mRestrictAppDetector; + private List mAppInfoList; + @Mock + private BatteryDatabaseManager mBatteryDatabaseManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mAppInfoList = new ArrayList<>(); + mAppInfoList.add(new AppInfo.Builder() + .setPackageName(PACKAGE_NAME) + .build()); + + mContext = RuntimeEnvironment.application; + mPolicy = spy(new BatteryTipPolicy(mContext)); + mRestrictAppDetector = new RestrictAppDetector(mContext, mPolicy); + mRestrictAppDetector.mBatteryDatabaseManager = mBatteryDatabaseManager; + } + + @After + public void cleanUp() { + DatabaseTestUtils.clearDb(mContext); + } + + @Test + public void testDetect_hasAnomaly_tipNew() { + doReturn(mAppInfoList).when(mBatteryDatabaseManager).queryAllAnomalies(anyLong(), + eq(AnomalyDatabaseHelper.State.NEW)); + + assertThat(mRestrictAppDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW); + } + + @Test + public void testDetect_hasAutoHandledAnomaly_tipHandled() { + doReturn(new ArrayList()).when(mBatteryDatabaseManager).queryAllAnomalies( + anyLong(), eq(AnomalyDatabaseHelper.State.NEW)); + doReturn(mAppInfoList).when(mBatteryDatabaseManager).queryAllAnomalies(anyLong(), + eq(AnomalyDatabaseHelper.State.AUTO_HANDLED)); + + assertThat(mRestrictAppDetector.detect().getState()).isEqualTo( + BatteryTip.StateType.HANDLED); + } + + @Test + public void testDetect_noAnomaly_tipInvisible() { + doReturn(new ArrayList()).when(mBatteryDatabaseManager).queryAllAnomalies( + anyLong(), anyInt()); + + assertThat(mRestrictAppDetector.detect().getState()).isEqualTo( + BatteryTip.StateType.INVISIBLE); + } + + @Test + public void testUseFakeData_alwaysFalse() { + assertThat(RestrictAppDetector.USE_FAKE_DATA).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java index e1dea17c1f6..74536a53031 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java @@ -49,6 +49,7 @@ public class RestrictAppTipTest { private Context mContext; private RestrictAppTip mNewBatteryTip; private RestrictAppTip mHandledBatteryTip; + private RestrictAppTip mInvisibleBatteryTip; private List mUsageAppList; @Mock private ApplicationInfo mApplicationInfo; @@ -71,6 +72,7 @@ public class RestrictAppTipTest { .build()); mNewBatteryTip = new RestrictAppTip(BatteryTip.StateType.NEW, mUsageAppList); mHandledBatteryTip = new RestrictAppTip(BatteryTip.StateType.HANDLED, mUsageAppList); + mInvisibleBatteryTip = new RestrictAppTip(BatteryTip.StateType.INVISIBLE, mUsageAppList); } @Test @@ -108,4 +110,20 @@ public class RestrictAppTipTest { assertThat(mHandledBatteryTip.getSummary(mContext)).isEqualTo( "App changes are in progress"); } + + @Test + public void testUpdate_anomalyBecomeInvisible_stateHandled() { + mNewBatteryTip.updateState(mInvisibleBatteryTip); + + assertThat(mNewBatteryTip.getState()).isEqualTo(BatteryTip.StateType.HANDLED); + } + + @Test + public void testUpdate_newAnomalyComes_stateNew() { + mInvisibleBatteryTip.updateState(mNewBatteryTip); + assertThat(mInvisibleBatteryTip.getState()).isEqualTo(BatteryTip.StateType.NEW); + + mHandledBatteryTip.updateState(mNewBatteryTip); + assertThat(mHandledBatteryTip.getState()).isEqualTo(BatteryTip.StateType.NEW); + } }