From 232ebf1ca73d67d58f8c46d7c6f4aa7105301300 Mon Sep 17 00:00:00 2001 From: Daniel Nishi Date: Fri, 19 Jan 2018 14:11:20 -0800 Subject: [PATCH 01/13] Add an intent for the advanced connected devices. This allows us to intent into it for setting specific advanced settings. Bug: 66679618 Test: BT notification punts to correct screen Change-Id: I6ff4fbcea2ebf563d634419eac0713365061f88d --- AndroidManifest.xml | 19 +++++++++++++++++++ src/com/android/settings/Settings.java | 5 +++++ .../core/gateway/SettingsGateway.java | 4 +++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9e4dc396635..5a4db20d8aa 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3311,6 +3311,25 @@ + + + + + + + + + + + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 5e815bc2c27..d13a62dcbb3 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -178,5 +178,10 @@ public class Settings extends SettingsActivity { public static class StorageDashboardActivity extends SettingsActivity {} public static class UserAndAccountDashboardActivity extends SettingsActivity {} public static class SystemDashboardActivity extends SettingsActivity {} + public static class AdvancedConnectedDeviceActivity extends SettingsActivity { + public static final boolean isEnabled() { + return FeatureFlagUtils.isEnabled(null /* context */, CONNECTED_DEVICE_V2); + } + } } diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 0a4b1f2d095..026cc2bd777 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -57,6 +57,7 @@ import com.android.settings.applications.assist.ManageAssist; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment; import com.android.settings.bluetooth.BluetoothSettings; +import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld; import com.android.settings.datausage.DataPlanUsageSummary; @@ -254,7 +255,8 @@ public class SettingsGateway { LockscreenDashboardFragment.class.getName(), BluetoothDeviceDetailsFragment.class.getName(), DataUsageList.class.getName(), - DirectoryAccessDetails.class.getName() + DirectoryAccessDetails.class.getName(), + AdvancedConnectedDeviceDashboardFragment.class.getName() }; public static final String[] SETTINGS_FOR_RESTRICTED = { From 92dcd78ad32978db9b50d54052ad00ee2e024eec Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Thu, 18 Jan 2018 16:04:42 -0800 Subject: [PATCH 02/13] Change high usage detector logic Based on the requirement, change it to detect: "whether battery draining is larger than x in the last y hours" Bug: 70570352 Test: RunSettingsRoboTests Change-Id: I9fb1a9f2fe38d5d64681dafe26311aeab7f3fe9c --- .../settings/fuelgauge/BatteryInfo.java | 2 +- .../batterytip/BatteryTipPolicy.java | 26 ++++++ .../batterytip/HighUsageDataParser.java | 79 +++++++++++++++++ .../detectors/HighUsageDetector.java | 49 +++++++---- .../batterytip/BatteryTipLoaderTest.java | 3 +- .../batterytip/BatteryTipPolicyTest.java | 7 ++ .../batterytip/HighUsageDataParserTest.java | 86 +++++++++++++++++++ .../detectors/HighUsageDetectorTest.java | 12 ++- 8 files changed, 241 insertions(+), 23 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java index acd81446b57..c4c795b29b1 100644 --- a/src/com/android/settings/fuelgauge/BatteryInfo.java +++ b/src/com/android/settings/fuelgauge/BatteryInfo.java @@ -272,7 +272,7 @@ public class BatteryInfo { void onParsingDone(); } - private static void parse(BatteryStats stats, BatteryDataParser... parsers) { + public static void parse(BatteryStats stats, BatteryDataParser... parsers) { long startWalltime = 0; long endWalltime = 0; long historyStart = 0; diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java index 6af859b4569..a580db1d311 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java @@ -19,9 +19,12 @@ package com.android.settings.fuelgauge.batterytip; import android.content.Context; import android.provider.Settings; import android.support.annotation.VisibleForTesting; +import android.text.format.DateUtils; import android.util.KeyValueListParser; import android.util.Log; +import java.time.Duration; + /** * Class to store the policy for battery tips, which comes from * {@link Settings.Global} @@ -34,6 +37,8 @@ public class BatteryTipPolicy { private static final String KEY_BATTERY_SAVER_TIP_ENABLED = "battery_saver_tip_enabled"; private static final String KEY_HIGH_USAGE_ENABLED = "high_usage_enabled"; private static final String KEY_HIGH_USAGE_APP_COUNT = "high_usage_app_count"; + private static final String KEY_HIGH_USAGE_PERIOD_MS = "high_usage_period_ms"; + private static final String KEY_HIGH_USAGE_BATTERY_DRAINING = "high_usage_battery_draining"; private static final String KEY_APP_RESTRICTION_ENABLED = "app_restriction_enabled"; private static final String KEY_REDUCED_BATTERY_ENABLED = "reduced_battery_enabled"; private static final String KEY_REDUCED_BATTERY_PERCENT = "reduced_battery_percent"; @@ -80,6 +85,24 @@ public class BatteryTipPolicy { */ public final int highUsageAppCount; + /** + * The size of the window(milliseconds) for checking if the device is being heavily used + * + * @see Settings.Global#BATTERY_TIP_CONSTANTS + * @see #KEY_HIGH_USAGE_PERIOD_MS + */ + public final long highUsagePeriodMs; + + /** + * The battery draining threshold to detect whether device is heavily used. + * If battery drains more than {@link #highUsageBatteryDraining} in last {@link + * #highUsagePeriodMs}, treat device as heavily used. + * + * @see Settings.Global#BATTERY_TIP_CONSTANTS + * @see #KEY_HIGH_USAGE_BATTERY_DRAINING + */ + public final int highUsageBatteryDraining; + /** * {@code true} if app restriction tip is enabled * @@ -143,6 +166,9 @@ public class BatteryTipPolicy { batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true); highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true); highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3); + highUsagePeriodMs = mParser.getLong(KEY_HIGH_USAGE_PERIOD_MS, + Duration.ofHours(2).toMillis()); + highUsageBatteryDraining = mParser.getInt(KEY_HIGH_USAGE_BATTERY_DRAINING, 25); appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true); reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, false); reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50); diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java new file mode 100644 index 00000000000..cc5aed679dd --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java @@ -0,0 +1,79 @@ +/* + * 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; + +import android.os.BatteryStats; + +import com.android.settings.fuelgauge.BatteryInfo; + +/** + * DataParser used to go through battery data and detect whether battery is + * heavily used. + */ +public class HighUsageDataParser implements BatteryInfo.BatteryDataParser { + /** + * time period to check the battery usage + */ + private final long mTimePeriodMs; + /** + * treat device as heavily used if battery usage is more than {@code threshold}. 1 means 1% + * battery usage. + */ + private int mThreshold; + private long mEndTimeMs; + private byte mEndBatteryLevel; + private byte mLastPeriodBatteryLevel; + private int mBatteryDrain; + + public HighUsageDataParser(long timePeriodMs, int threshold) { + mTimePeriodMs = timePeriodMs; + mThreshold = threshold; + } + + @Override + public void onParsingStarted(long startTime, long endTime) { + mEndTimeMs = endTime; + } + + @Override + public void onDataPoint(long time, BatteryStats.HistoryItem record) { + if (record.currentTime <= mEndTimeMs - mTimePeriodMs) { + // Since onDataPoint is invoked sorted by time, so we could use this way to get the + // closet battery level 'mTimePeriodMs' time ago. + mLastPeriodBatteryLevel = record.batteryLevel; + } + mEndBatteryLevel = record.batteryLevel; + } + + @Override + public void onDataGap() { + // do nothing + } + + @Override + public void onParsingDone() { + mBatteryDrain = mLastPeriodBatteryLevel - mEndBatteryLevel; + } + + /** + * Return {@code true} if the battery drain in {@link #mTimePeriodMs} is too much + */ + public boolean isDeviceHeavilyUsed() { + return mBatteryDrain > mThreshold; + } +} + diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java index 3c696671e95..ed3fa04c1e0 100644 --- a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java @@ -19,13 +19,14 @@ package com.android.settings.fuelgauge.batterytip.detectors; import android.content.Context; import android.os.BatteryStats; import android.support.annotation.VisibleForTesting; -import android.text.format.DateUtils; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.BatteryInfo; +import com.android.settings.fuelgauge.batterytip.HighUsageDataParser; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip; @@ -43,6 +44,8 @@ public class HighUsageDetector implements BatteryTipDetector { private List mHighUsageAppList; private Context mContext; @VisibleForTesting + HighUsageDataParser mDataParser; + @VisibleForTesting BatteryUtils mBatteryUtils; public HighUsageDetector(Context context, BatteryTipPolicy policy, @@ -52,32 +55,42 @@ public class HighUsageDetector implements BatteryTipDetector { mBatteryStatsHelper = batteryStatsHelper; mHighUsageAppList = new ArrayList<>(); mBatteryUtils = BatteryUtils.getInstance(context); + mDataParser = new HighUsageDataParser(mPolicy.highUsagePeriodMs, + mPolicy.highUsageBatteryDraining); } @Override public BatteryTip detect() { final long screenUsageTimeMs = mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper); - //TODO(b/70570352): Change it to detect whether battery drops 25% in last 2 hours - if (mPolicy.highUsageEnabled && screenUsageTimeMs > DateUtils.HOUR_IN_MILLIS) { - final List batterySippers = mBatteryStatsHelper.getUsageList(); - for (int i = 0, size = batterySippers.size(); i < size; i++) { - final BatterySipper batterySipper = batterySippers.get(i); - if (!mBatteryUtils.shouldHideSipper(batterySipper)) { - final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs( - BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj, - BatteryStats.STATS_SINCE_CHARGED); - mHighUsageAppList.add(new AppInfo.Builder() - .setPackageName(mBatteryUtils.getPackageName(batterySipper.getUid())) - .setScreenOnTimeMs(foregroundTimeMs) - .build()); + if (mPolicy.highUsageEnabled) { + parseBatteryData(); + if (mDataParser.isDeviceHeavilyUsed()) { + final List batterySippers = mBatteryStatsHelper.getUsageList(); + for (int i = 0, size = batterySippers.size(); i < size; i++) { + final BatterySipper batterySipper = batterySippers.get(i); + if (!mBatteryUtils.shouldHideSipper(batterySipper)) { + final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs( + BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj, + BatteryStats.STATS_SINCE_CHARGED); + mHighUsageAppList.add(new AppInfo.Builder() + .setPackageName( + mBatteryUtils.getPackageName(batterySipper.getUid())) + .setScreenOnTimeMs(foregroundTimeMs) + .build()); + } } - } - mHighUsageAppList = mHighUsageAppList.subList(0, - Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size())); - Collections.sort(mHighUsageAppList, Collections.reverseOrder()); + mHighUsageAppList = mHighUsageAppList.subList(0, + Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size())); + Collections.sort(mHighUsageAppList, Collections.reverseOrder()); + } } return new HighUsageTip(screenUsageTimeMs, mHighUsageAppList); } + + @VisibleForTesting + void parseBatteryData() { + BatteryInfo.parse(mBatteryStatsHelper.getStats(), mDataParser); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java index 53c9766ad55..83b32258009 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java @@ -37,6 +37,7 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @@ -54,7 +55,7 @@ public class BatteryTipLoaderTest { BatteryTip.TipType.BATTERY_SAVER, BatteryTip.TipType.LOW_BATTERY, BatteryTip.TipType.SUMMARY}; - @Mock + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private BatteryStatsHelper mBatteryStatsHelper; @Mock private PowerManager mPowerManager; diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java index bb9a37bd50c..78c86f8d3e6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.spy; import android.content.Context; import android.provider.Settings; +import android.text.format.DateUtils; import com.android.settings.TestConfig; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -42,6 +43,8 @@ public class BatteryTipPolicyTest { + ",battery_saver_tip_enabled=false" + ",high_usage_enabled=true" + ",high_usage_app_count=5" + + ",high_usage_period_ms=2000" + + ",high_usage_battery_draining=30" + ",app_restriction_enabled=true" + ",reduced_battery_enabled=true" + ",reduced_battery_percent=30" @@ -66,6 +69,8 @@ public class BatteryTipPolicyTest { assertThat(batteryTipPolicy.batterySaverTipEnabled).isFalse(); assertThat(batteryTipPolicy.highUsageEnabled).isTrue(); assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(5); + assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2000); + assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(30); assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue(); assertThat(batteryTipPolicy.reducedBatteryEnabled).isTrue(); assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(30); @@ -85,6 +90,8 @@ public class BatteryTipPolicyTest { assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue(); assertThat(batteryTipPolicy.highUsageEnabled).isTrue(); assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3); + assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2 * DateUtils.HOUR_IN_MILLIS); + assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(25); assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue(); assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse(); assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java new file mode 100644 index 00000000000..5bdae0ccc2d --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java @@ -0,0 +1,86 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryStats; +import android.text.format.DateUtils; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import java.time.Duration; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class HighUsageDataParserTest { + private static final long PERIOD_ONE_MINUTE_MS = Duration.ofMinutes(1).toMillis(); + private static final long END_TIME_MS = 2 * PERIOD_ONE_MINUTE_MS; + private static final int THRESHOLD_LOW = 10; + private static final int THRESHOLD_HIGH = 20; + private HighUsageDataParser mDataParser; + private BatteryStats.HistoryItem mFirstItem; + private BatteryStats.HistoryItem mSecondItem; + private BatteryStats.HistoryItem mThirdItem; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mFirstItem = new BatteryStats.HistoryItem(); + mFirstItem.batteryLevel = 100; + mFirstItem.currentTime = 0; + mSecondItem = new BatteryStats.HistoryItem(); + mSecondItem.batteryLevel = 95; + mSecondItem.currentTime = PERIOD_ONE_MINUTE_MS; + mThirdItem = new BatteryStats.HistoryItem(); + mThirdItem.batteryLevel = 80; + mThirdItem.currentTime = END_TIME_MS; + } + + @Test + public void testDataParser_thresholdLow_isHeavilyUsed() { + mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_LOW); + parseData(); + + assertThat(mDataParser.isDeviceHeavilyUsed()).isTrue(); + } + + @Test + public void testDataParser_thresholdHigh_notHeavilyUsed() { + mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_HIGH); + parseData(); + + assertThat(mDataParser.isDeviceHeavilyUsed()).isFalse(); + } + + private void parseData() { + mDataParser.onParsingStarted(0, END_TIME_MS); + mDataParser.onDataPoint(0, mFirstItem); + mDataParser.onDataPoint(PERIOD_ONE_MINUTE_MS, mSecondItem); + mDataParser.onDataPoint(END_TIME_MS, mThirdItem); + + mDataParser.onParsingDone(); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java index 2a719916fb0..8df7c56d9ef 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java @@ -18,6 +18,8 @@ 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.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -31,6 +33,7 @@ import com.android.settings.TestConfig; import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; +import com.android.settings.fuelgauge.batterytip.HighUsageDataParser; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; @@ -55,6 +58,8 @@ public class HighUsageDetectorTest { private BatteryUtils mBatteryUtils; @Mock private BatterySipper mBatterySipper; + @Mock + private HighUsageDataParser mDataParser; private BatteryTipPolicy mPolicy; private HighUsageDetector mHighUsageDetector; @@ -66,8 +71,10 @@ public class HighUsageDetectorTest { mContext = RuntimeEnvironment.application; mPolicy = spy(new BatteryTipPolicy(mContext)); - mHighUsageDetector = new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper); + mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper)); mHighUsageDetector.mBatteryUtils = mBatteryUtils; + mHighUsageDetector.mDataParser = mDataParser; + doNothing().when(mHighUsageDetector).parseBatteryData(); mUsageList = new ArrayList<>(); mUsageList.add(mBatterySipper); @@ -82,8 +89,7 @@ public class HighUsageDetectorTest { @Test public void testDetect_containsHighUsageApp_tipVisible() { - doReturn(2 * DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).calculateScreenUsageTime( - mBatteryStatsHelper); + doReturn(true).when(mDataParser).isDeviceHeavilyUsed(); doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); doReturn(DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).getProcessTimeMs( BatteryUtils.StatusType.FOREGROUND, mBatterySipper.uidObj, From a5f12c6b5ce5cafc1c957645108149ceaaca9728 Mon Sep 17 00:00:00 2001 From: Lenka Trochtova Date: Wed, 17 Jan 2018 11:05:30 +0100 Subject: [PATCH 03/13] Policy transparency for mandatory backups. Bug: 64012357 Test: manual Change-Id: I6f3dd294cca26fc0572ce6b8723a616902b1752f --- res/values/strings.xml | 2 ++ src/com/android/settings/ShowAdminSupportDetailsDialog.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index 164f6d5de29..7c1c8c131f1 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8335,6 +8335,8 @@ Camera not allowed Screenshot not allowed + + Can\'t turn off backups This action is disabled. To learn more, contact your organization\'s admin. diff --git a/src/com/android/settings/ShowAdminSupportDetailsDialog.java b/src/com/android/settings/ShowAdminSupportDetailsDialog.java index c1cd6f567fb..321f93d10c3 100644 --- a/src/com/android/settings/ShowAdminSupportDetailsDialog.java +++ b/src/com/android/settings/ShowAdminSupportDetailsDialog.java @@ -150,6 +150,9 @@ public class ShowAdminSupportDetailsDialog extends Activity case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE: titleView.setText(R.string.disabled_by_policy_title_screen_capture); break; + case DevicePolicyManager.POLICY_MANDATORY_BACKUPS: + titleView.setText(R.string.disabled_by_policy_title_turn_off_backups); + break; default: // Use general text if no specialized title applies titleView.setText(R.string.disabled_by_policy_title); From b9d8a97b78f314a52296e259ac925c90cbf2cfac Mon Sep 17 00:00:00 2001 From: Salvador Martinez Date: Mon, 22 Jan 2018 10:09:09 -0800 Subject: [PATCH 04/13] Remove reference to missing string. A support string was being used by non-support xml. These references have been replaced with new ones so that things don't break. Test: Robotests still pass Change-Id: I5574aedca110b9795a68c2947be0a866bb21cb63 Fixes: 71858657 --- .../settings/wifi/details/WifiDetailPreferenceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java index 70ee20de03d..82ffc38c967 100644 --- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -280,7 +280,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController .setButton1Text(R.string.forget) .setButton1Positive(false) .setButton1OnClickListener(view -> forgetNetwork()) - .setButton2Text(R.string.support_sign_in_button_text) + .setButton2Text(R.string.wifi_sign_in_button_text) .setButton2Positive(true) .setButton2OnClickListener(view -> signIntoNetwork()); From 02945bd6e48e85ff090e98591f60996027d74f3d Mon Sep 17 00:00:00 2001 From: Joachim Sauer Date: Mon, 22 Jan 2018 15:47:24 +0000 Subject: [PATCH 05/13] Fix crash in new time zone picker. When entering the new time zone picker with a fixed offset time zone (such as UTC) selected, it would crash with a NullPointerException. This fixes the crash. Bug: 72143165 Bug: 62255208 Test: manual Change-Id: I1f54d3276edb98ab7bd6a092be4497386a69d6be --- .../datetime/timezone/ZonePicker.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/com/android/settings/datetime/timezone/ZonePicker.java b/src/com/android/settings/datetime/timezone/ZonePicker.java index eafbaa29bf2..d0d17202595 100644 --- a/src/com/android/settings/datetime/timezone/ZonePicker.java +++ b/src/com/android/settings/datetime/timezone/ZonePicker.java @@ -31,6 +31,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.LinearLayout; import android.widget.Spinner; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; @@ -56,11 +57,14 @@ public class ZonePicker extends InstrumentedFragment private List mRegions; private Map> mZoneInfos; private List mFixedOffsetTimeZones; - private TimeZoneAdapter mTimeZoneAdapter; private String mSelectedTimeZone; private boolean mSelectByRegion; private DataLoader mDataLoader; + private TimeZoneAdapter mTimeZoneAdapter; + private RecyclerView mRecyclerView; + private LinearLayout mRegionSpinnerLayout; + private Spinner mRegionSpinner; @Override public int getMetricsCategory() { @@ -88,15 +92,17 @@ public class ZonePicker extends InstrumentedFragment final ArrayAdapter regionAdapter = new ArrayAdapter<>(getContext(), R.layout.filter_spinner_item, mRegions); regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - final Spinner spinner = view.findViewById(R.id.tz_region_spinner); - spinner.setAdapter(regionAdapter); - spinner.setOnItemSelectedListener(this); - setupForCurrentTimeZone(spinner); + + mRegionSpinnerLayout = view.findViewById(R.id.tz_region_spinner_layout); + mRegionSpinner = view.findViewById(R.id.tz_region_spinner); + mRegionSpinner.setAdapter(regionAdapter); + mRegionSpinner.setOnItemSelectedListener(this); + setupForCurrentTimeZone(); setHasOptionsMenu(true); return view; } - private void setupForCurrentTimeZone(Spinner spinner) { + private void setupForCurrentTimeZone() { final String localeRegionId = mLocale.getCountry().toUpperCase(Locale.ROOT); final String currentTimeZone = TimeZone.getDefault().getID(); boolean fixedOffset = currentTimeZone.startsWith("Etc/GMT") || @@ -105,12 +111,12 @@ public class ZonePicker extends InstrumentedFragment for (int regionIndex = 0; regionIndex < mRegions.size(); regionIndex++) { final RegionInfo region = mRegions.get(regionIndex); if (localeRegionId.equals(region.getId())) { - spinner.setSelection(regionIndex); + mRegionSpinner.setSelection(regionIndex); } if (!fixedOffset) { for (String timeZoneId: region.getTimeZoneIds()) { if (TextUtils.equals(timeZoneId, mSelectedTimeZone)) { - spinner.setSelection(regionIndex); + mRegionSpinner.setSelection(regionIndex); return; } } @@ -179,16 +185,15 @@ public class ZonePicker extends InstrumentedFragment private void setSelectByRegion(boolean selectByRegion) { mSelectByRegion = selectByRegion; - getView().findViewById(R.id.tz_region_spinner_layout).setVisibility( + mRegionSpinnerLayout.setVisibility( mSelectByRegion ? View.VISIBLE : View.GONE); List tzInfos; if (selectByRegion) { - Spinner regionSpinner = getView().findViewById(R.id.tz_region_spinner); - int selectedRegion = regionSpinner.getSelectedItemPosition(); + int selectedRegion = mRegionSpinner.getSelectedItemPosition(); if (selectedRegion == -1) { // Arbitrarily pick the first item if no region was selected above. selectedRegion = 0; - regionSpinner.setSelection(selectedRegion); + mRegionSpinner.setSelection(selectedRegion); } tzInfos = getTimeZoneInfos(mRegions.get(selectedRegion)); } else { From 6d88d79aa5e3e06c7199e47e614ac806e352fabc Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Fri, 19 Jan 2018 17:34:23 -0800 Subject: [PATCH 06/13] Finished initial implementation of StorageAccessDetails. It still needs some polishing and unit tests, which will be added later. Test: manual verification Bug: 63720392 Change-Id: I8da1094aaaf4ebbe5d388021e859265091fd429c --- res/xml/directory_access_details.xml | 20 ++ res/xml/special_access.xml | 4 +- .../AppStateDirectoryAccessBridge.java | 27 +- .../applications/DirectoryAccessDetails.java | 236 ++++++++++++++---- 4 files changed, 234 insertions(+), 53 deletions(-) create mode 100644 res/xml/directory_access_details.xml diff --git a/res/xml/directory_access_details.xml b/res/xml/directory_access_details.xml new file mode 100644 index 00000000000..4448ba65aeb --- /dev/null +++ b/res/xml/directory_access_details.xml @@ -0,0 +1,20 @@ + + + + diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml index 0843b79fb16..7205eaf28ab 100644 --- a/res/xml/special_access.xml +++ b/res/xml/special_access.xml @@ -111,7 +111,6 @@ android:value="com.android.settings.Settings$VrListenersSettingsActivity" /> - diff --git a/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java b/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java index 8cd4444ed4c..1c2a0af8a74 100644 --- a/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java +++ b/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java @@ -33,11 +33,15 @@ import com.android.settingslib.applications.ApplicationsState.AppFilter; import java.util.Set; -// TODO(b/63720392): add unit tests +// TODO(b/72055774): add unit tests public class AppStateDirectoryAccessBridge extends AppStateBaseBridge { private static final String TAG = "DirectoryAccessBridge"; + // TODO(b/72055774): set to false once feature is ready (or use Log.isLoggable) + static final boolean DEBUG = true; + static final boolean VERBOSE = true; + public AppStateDirectoryAccessBridge(ApplicationsState appState, Callback callback) { super(appState, callback); } @@ -59,27 +63,34 @@ public class AppStateDirectoryAccessBridge extends AppStateBaseBridge { @Override public void init(Context context) { - try (Cursor cursor = context.getContentResolver().query( - new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*") - .build(), TABLE_PACKAGES_COLUMNS, null, null)) { + mPackages = null; + final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*") + .build(); + try (Cursor cursor = context.getContentResolver().query(providerUri, + TABLE_PACKAGES_COLUMNS, null, null)) { if (cursor == null) { - Log.w(TAG, "didn't get cursor"); + Log.w(TAG, "Didn't get cursor for " + providerUri); return; } final int count = cursor.getCount(); if (count == 0) { - Log.d(TAG, "no packages"); + if (DEBUG) { + Log.d(TAG, "No packages anymore (was " + mPackages + ")"); + } return; } mPackages = new ArraySet<>(count); while (cursor.moveToNext()) { mPackages.add(cursor.getString(TABLE_PACKAGES_COL_PACKAGE)); } - Log.v(TAG, "init(): " + mPackages); + if (DEBUG) { + Log.d(TAG, "init(): " + mPackages); + } } } + @Override public boolean filterApp(AppEntry info) { return mPackages != null && mPackages.contains(info.info.packageName); diff --git a/src/com/android/settings/applications/DirectoryAccessDetails.java b/src/com/android/settings/applications/DirectoryAccessDetails.java index 1f7a81a9336..43422d0f90b 100644 --- a/src/com/android/settings/applications/DirectoryAccessDetails.java +++ b/src/com/android/settings/applications/DirectoryAccessDetails.java @@ -17,84 +17,236 @@ package com.android.settings.applications; import static android.os.storage.StorageVolume.ScopedAccessProviderContract.AUTHORITY; +import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_DIRECTORY; +import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_GRANTED; +import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_PACKAGE; +import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_VOLUME_UUID; import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS; import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COLUMNS; import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_DIRECTORY; -import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_GRANTED; import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_PACKAGE; import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_VOLUME_UUID; +import static com.android.settings.applications.AppStateDirectoryAccessBridge.DEBUG; +import static com.android.settings.applications.AppStateDirectoryAccessBridge.VERBOSE; + +import android.annotation.Nullable; +import android.app.Activity; import android.app.AlertDialog; import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; +import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.Preference.OnPreferenceClickListener; +import android.text.TextUtils; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceScreen; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.IconDrawableFactory; import android.util.Log; +import android.util.Pair; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.widget.EntityHeaderController; +import com.android.settings.widget.EntityHeaderController.ActionType; +import com.android.settingslib.applications.AppUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Detailed settings for an app's directory access permissions (A.K.A Scoped Directory Access). + * + *

Currently, it shows the entry for which the user denied access with the "Do not ask again" + * flag checked on: the user than can use the settings toggle to reset that deniel. + * + *

This fragments dynamically lists all such permissions, starting with one preference per + * directory in the primary storage, then adding additional entries for the external volumes (one + * entry for the whole volume). */ -// TODO(b/63720392): explain its layout -// TODO(b/63720392): add unit tests -public class DirectoryAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener, - OnPreferenceClickListener { - private static final String MY_TAG = "DirectoryAccessDetails"; +// TODO(b/72055774): add unit tests +public class DirectoryAccessDetails extends AppInfoBase { + + @SuppressWarnings("hiding") + private static final String TAG = "DirectoryAccessDetails"; + + private boolean mCreated; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + if (mCreated) { + Log.w(TAG, "onActivityCreated(): ignoring duplicate call"); + return; + } + mCreated = true; + if (mPackageInfo == null) { + Log.w(TAG, "onActivityCreated(): no package info"); + return; + } + final Activity activity = getActivity(); + final Preference pref = EntityHeaderController + .newInstance(activity, this, /* header= */ null ) + .setRecyclerView(getListView(), getLifecycle()) + .setIcon(IconDrawableFactory.newInstance(getPrefContext()) + .getBadgedIcon(mPackageInfo.applicationInfo)) + .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) + .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) + .setPackageName(mPackageName) + .setUid(mPackageInfo.applicationInfo.uid) + .setHasAppInfoLink(false) + .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE) + .done(activity, getPrefContext()); + getPreferenceScreen().addPreference(pref); + } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (true) { - // TODO(b/63720392): temporarily hack so the screen doesn't crash.. - addPreferencesFromResource(R.xml.app_ops_permissions_details); - // ... we need to dynamically create the preferences by calling the provider instead: - try (Cursor cursor = getContentResolver().query( - new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*") - .build(), - TABLE_PERMISSIONS_COLUMNS, null, null)) { - if (cursor == null) { - Log.w(TAG, "didn't get cursor"); - return; + final Context context = getPrefContext(); + addPreferencesFromResource(R.xml.directory_access_details); + final PreferenceScreen prefsGroup = getPreferenceScreen(); + + // Set external directory UUIDs. + ArraySet externalDirectoryUuids = null; + + final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*") + .build(); + // Query provider for entries. + try (Cursor cursor = context.getContentResolver().query(providerUri, + TABLE_PERMISSIONS_COLUMNS, null, new String[] { mPackageName }, null)) { + if (cursor == null) { + Log.w(TAG, "Didn't get cursor for " + mPackageName); + return; + } + final int count = cursor.getCount(); + if (count == 0) { + if (DEBUG) { + Log.d(TAG, "No permissions for " + mPackageName); } - final int count = cursor.getCount(); - if (count == 0) { - Log.d(TAG, "no permissions"); - return; + // TODO(b/72055774): display empty message + return; + } + + while (cursor.moveToNext()) { + final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE); + final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID); + final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY); + if (VERBOSE) { + Log.v(TAG, "Pkg:" + pkg + " uuid: " + uuid + " dir: " + dir); } - while (cursor.moveToNext()) { - final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE); - final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID); - final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY); - final boolean granted = cursor.getInt(TABLE_PERMISSIONS_COL_GRANTED) == 1; - Log.v(MY_TAG, "pkg:" + pkg + " uuid: " + uuid + " dir: " + dir + "> " - + granted); + + if (!mPackageName.equals(pkg)) { + // Sanity check, shouldn't happen + Log.w(TAG, "Ignoring " + uuid + "/" + dir + " due to package mismatch: " + + "expected " + mPackageName + ", got " + pkg); + continue; + } + + if (uuid == null) { + // Primary storage entry: add right away + prefsGroup.addPreference( + newPreference(context, dir, providerUri, /* uuid= */ null, dir)); + } else { + // External volume entry: save it for later. + if (externalDirectoryUuids == null) { + externalDirectoryUuids = new ArraySet<>(1); + } + externalDirectoryUuids.add(uuid); } } } + + // Add entries from external volumes + if (externalDirectoryUuids != null) { + if (VERBOSE) { + Log.v(TAG, "adding external directories: " + externalDirectoryUuids); + } + + // Query StorageManager to get the user-friendly volume names. + final StorageManager sm = context.getSystemService(StorageManager.class); + final List volumes = sm.getVolumes(); + if (volumes.isEmpty()) { + Log.w(TAG, "StorageManager returned no secondary volumes"); + return; + } + final Map volumeNames = new HashMap<>(volumes.size()); + for (VolumeInfo volume : volumes) { + final String uuid = volume.getFsUuid(); + if (uuid == null) continue; // Primary storage; not used. + + String name = sm.getBestVolumeDescription(volume); + if (name == null) { + Log.w(TAG, "No description for " + volume + "; using uuid instead: " + uuid); + name = uuid; + } + volumeNames.put(uuid, name); + } + if (VERBOSE) { + Log.v(TAG, "UUID -> name mapping: " + volumeNames); + } + + externalDirectoryUuids.forEach((uuid) ->{ + final String name = volumeNames.get(uuid); + // TODO(b/72055774): add separator + prefsGroup.addPreference( + newPreference(context, name, providerUri, uuid, /* dir= */ null)); + }); + } + return; + } + + + private SwitchPreference newPreference(Context context, String title, Uri providerUri, + String uuid, String dir) { + final SwitchPreference pref = new SwitchPreference(context); + pref.setKey(String.format("%s:%s", uuid, dir)); + pref.setTitle(title); + pref.setChecked(false); + pref.setOnPreferenceChangeListener((unused, value) -> { + resetDoNotAskAgain(context, value, providerUri, uuid, dir); + return true; + }); + return pref; + } + + private void resetDoNotAskAgain(Context context, Object value, Uri providerUri, + @Nullable String uuid, @Nullable String directory) { + if (!Boolean.class.isInstance(value)) { + // Sanity check + Log.wtf(TAG, "Invalid value from switch: " + value); + return; + } + final boolean newValue = ((Boolean) value).booleanValue(); + if (DEBUG) { + Log.d(TAG, "Asking " + providerUri + " to update " + uuid + "/" + directory + " to " + + newValue); + } + final ContentValues values = new ContentValues(1); + values.put(COL_GRANTED, newValue); + final int updated = context.getContentResolver().update(providerUri, values, + null, new String[] { mPackageName, uuid, directory }); + if (DEBUG) { + Log.d(TAG, "Updated " + updated + " entries for " + uuid + "/" + directory); + } } - @Override - public boolean onPreferenceClick(Preference preference) { - // TODO(b/63720392): implement or remove listener - return false; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - // TODO(b/63720392): implement or remove listener - return false; - } - @Override protected boolean refreshUi() { - // TODO(b/63720392): implement return true; } From cab0ee611d375d70a3803d857e62c745ccd98de2 Mon Sep 17 00:00:00 2001 From: Leif Wilden Date: Mon, 22 Jan 2018 20:22:02 +0000 Subject: [PATCH 07/13] Revert "Migrate to use instrumentation classes from settingslib." This reverts commit 1546cca529bf56430332e15b05ceb6efb37e57bf. Reason for revert: Broke fingerprint setup flow. b/72267201 Change-Id: I8321265ae64732c526325882ddea51080decddf5 --- .../android/settings/AirplaneModeEnabler.java | 2 +- .../android/settings/DeviceAdminSettings.java | 16 +- .../android/settings/SettingsActivity.java | 12 +- .../settings/SettingsPreferenceFragment.java | 2 +- src/com/android/settings/Utils.java | 3 +- .../accounts/AccountPreferenceController.java | 2 +- .../applications/UsageAccessDetails.java | 2 +- .../bluetooth/BluetoothDevicePreference.java | 2 +- ...toothDeviceRenamePreferenceController.java | 2 +- .../settings/bluetooth/BluetoothEnabler.java | 2 +- .../BluetoothFilesPreferenceController.java | 2 +- .../settings/core/InstrumentedActivity.java | 8 +- .../settings/core/InstrumentedFragment.java | 22 +- .../core/InstrumentedPreferenceFragment.java | 22 +- .../core/instrumentation/EventLogWriter.java | 110 ++++++++ .../core/instrumentation/Instrumentable.java | 28 ++ .../InstrumentedDialogFragment.java | 11 +- .../core/instrumentation/LogWriter.java | 84 ++++++ .../MetricsFeatureProvider.java | 159 +++++++++++ .../SharedPreferencesLogger.java | 259 ++++++++++++++++++ .../VisibilityLoggerMixin.java | 100 +++++++ .../settings/dashboard/DashboardAdapter.java | 2 +- .../dashboard/DashboardAdapterV2.java | 2 +- .../DashboardFeatureProviderImpl.java | 8 +- .../dashboard/conditional/Condition.java | 2 +- .../conditional/ConditionAdapter.java | 2 +- .../conditional/ConditionAdapterV2.java | 2 +- .../suggestions/SuggestionAdapter.java | 2 +- .../suggestions/SuggestionAdapterV2.java | 2 +- .../SuggestionFeatureProviderImpl.java | 2 +- .../settings/datausage/DataSaverBackend.java | 2 +- .../android/settings/datetime/ZonePicker.java | 21 +- ...aticStorageManagerSwitchBarController.java | 4 +- .../BuildNumberPreferenceController.java | 2 +- ...eManagementSwitchPreferenceController.java | 2 +- .../StorageItemPreferenceController.java | 2 +- ...playNotificationsPreferenceController.java | 2 +- .../display/AmbientDisplaySettings.java | 2 +- .../AutoRotatePreferenceController.java | 2 +- .../display/ThemePreferenceController.java | 2 +- .../AppButtonsPreferenceController.java | 2 +- .../BatteryAppListPreferenceController.java | 2 +- .../settings/fuelgauge/PowerUsageSummary.java | 2 +- .../fuelgauge/PowerUsageSummaryLegacy.java | 2 +- .../fuelgauge/anomaly/AnomalyUtils.java | 2 +- .../anomaly/action/AnomalyAction.java | 2 +- .../batterytip/actions/BatteryTipAction.java | 2 +- .../actions/SmartBatteryAction.java | 2 +- .../inputmethod/UserDictionarySettings.java | 17 +- .../AirplaneModePreferenceController.java | 2 +- .../network/NetworkDashboardFragment.java | 2 +- .../AbstractZenModePreferenceController.java | 2 +- .../notification/ZenRulePreference.java | 4 +- .../settings/overlay/FeatureFactory.java | 2 +- .../settings/overlay/FeatureFactoryImpl.java | 2 +- .../android/settings/widget/SwitchBar.java | 2 +- .../android/settings/wifi/WifiEnabler.java | 2 +- .../WifiMasterSwitchPreferenceController.java | 2 +- .../WifiDetailPreferenceController.java | 2 +- .../settings/SettingsDialogFragmentTest.java | 7 +- .../InstantAppButtonsControllerTest.java | 2 +- .../BluetoothDevicePreferenceTest.java | 2 +- .../bluetooth/BluetoothEnablerTest.java | 2 +- .../android/settings/bluetooth/UtilsTest.java | 2 +- .../InstrumentableFragmentCodeInspector.java | 1 - .../InstrumentedDialogFragmentTest.java | 1 - .../MetricsFeatureProviderTest.java | 66 ++++- .../SharedPreferenceLoggerTest.java | 185 +++++++++++++ .../VisibilityLoggerMixinTest.java | 123 +++++++++ .../DashboardFeatureProviderImplTest.java | 3 +- .../dashboard/DashboardFragmentTest.java | 2 +- .../dashboard/conditional/ConditionTest.java | 2 +- .../settings/datausage/DataUsageListTest.java | 1 - .../settings/datetime/ZonePickerTest.java | 2 +- ...StorageManagerSwitchBarControllerTest.java | 2 +- ...agementSwitchPreferenceControllerTest.java | 2 +- .../StorageItemPreferenceControllerTest.java | 2 +- ...eSummaryDonutPreferenceControllerTest.java | 2 +- ...NotificationsPreferenceControllerTest.java | 2 +- .../FingerprintEnrollEnrollingTest.java | 4 - .../FingerprintEnrollFindSensorTest.java | 4 - .../FingerprintSuggestionActivityTest.java | 4 - .../SetupFingerprintEnrollFindSensorTest.java | 4 - ...etupFingerprintEnrollIntroductionTest.java | 4 - .../fuelgauge/anomaly/AnomalyUtilsTest.java | 2 +- .../localepicker/LocaleListEditorTest.java | 10 - .../testutils/FakeFeatureFactory.java | 2 +- .../shadow/ShadowEventLogWriter.java | 2 +- .../webview/WebViewAppPickerTest.java | 2 +- .../settings/wifi/WifiEnablerTest.java | 2 +- ...iMasterSwitchPreferenceControllerTest.java | 2 +- .../WifiDetailPreferenceControllerTest.java | 2 +- 92 files changed, 1249 insertions(+), 176 deletions(-) create mode 100644 src/com/android/settings/core/instrumentation/EventLogWriter.java create mode 100644 src/com/android/settings/core/instrumentation/Instrumentable.java create mode 100644 src/com/android/settings/core/instrumentation/LogWriter.java create mode 100644 src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java create mode 100644 src/com/android/settings/core/instrumentation/SharedPreferencesLogger.java create mode 100644 src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java create mode 100644 tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java create mode 100644 tests/robotests/src/com/android/settings/core/instrumentation/VisibilityLoggerMixinTest.java diff --git a/src/com/android/settings/AirplaneModeEnabler.java b/src/com/android/settings/AirplaneModeEnabler.java index 5f93589c92d..4fc205d263d 100644 --- a/src/com/android/settings/AirplaneModeEnabler.java +++ b/src/com/android/settings/AirplaneModeEnabler.java @@ -30,8 +30,8 @@ import android.support.v7.preference.Preference; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.telephony.PhoneStateIntentReceiver; import com.android.internal.telephony.TelephonyProperties; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.WirelessUtils; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListener { diff --git a/src/com/android/settings/DeviceAdminSettings.java b/src/com/android/settings/DeviceAdminSettings.java index bb53018dfd6..9391439267d 100644 --- a/src/com/android/settings/DeviceAdminSettings.java +++ b/src/com/android/settings/DeviceAdminSettings.java @@ -49,9 +49,8 @@ import android.widget.Switch; import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; -import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; +import com.android.settings.core.instrumentation.Instrumentable; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; import org.xmlpull.v1.XmlPullParserException; @@ -64,7 +63,8 @@ import java.util.List; public class DeviceAdminSettings extends ListFragment implements Instrumentable { static final String TAG = "DeviceAdminSettings"; - private VisibilityLoggerMixin mVisibilityLoggerMixin; + private final VisibilityLoggerMixin mVisibilityLoggerMixin = + new VisibilityLoggerMixin(getMetricsCategory()); private DevicePolicyManager mDPM; private UserManager mUm; @@ -85,6 +85,12 @@ public class DeviceAdminSettings extends ListFragment implements Instrumentable } } + @Override + public void onAttach(Context context) { + super.onAttach(context); + mVisibilityLoggerMixin.onAttach(context); + } + /** * Internal collection of device admin info objects for all profiles associated with the current * user. @@ -115,8 +121,6 @@ public class DeviceAdminSettings extends ListFragment implements Instrumentable @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(), - FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()); } @Override diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index 5cb7c06c21a..d3580d18b86 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -56,13 +56,13 @@ import com.android.settings.Settings.WifiSettingsActivity; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.backup.BackupSettingsActivity; import com.android.settings.core.gateway.SettingsGateway; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.core.instrumentation.SharedPreferencesLogger; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.DashboardSummary; import com.android.settings.overlay.FeatureFactory; import com.android.settings.wfd.WifiDisplaySettings; import com.android.settings.widget.SwitchBar; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.instrumentation.SharedPreferencesLogger; import com.android.settingslib.development.DevelopmentSettingsEnabler; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.SettingsDrawerActivity; @@ -92,6 +92,11 @@ public class SettingsActivity extends SettingsDrawerActivity */ public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment"; + /** + * The metrics category constant for logging source when a setting fragment is opened. + */ + public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics"; + /** * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, * this extra can also be specified to supply a Bundle of arguments to pass @@ -215,8 +220,7 @@ public class SettingsActivity extends SettingsDrawerActivity @Override public SharedPreferences getSharedPreferences(String name, int mode) { if (name.equals(getPackageName() + "_preferences")) { - return new SharedPreferencesLogger(this, getMetricsTag(), - FeatureFactory.getFactory(this).getMetricsFeatureProvider()); + return new SharedPreferencesLogger(this, getMetricsTag()); } return super.getSharedPreferences(name, mode); } diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java index c5d477aad0b..d9e264bf33d 100644 --- a/src/com/android/settings/SettingsPreferenceFragment.java +++ b/src/com/android/settings/SettingsPreferenceFragment.java @@ -45,6 +45,7 @@ import android.widget.Button; import com.android.settings.applications.LayoutPreference; import com.android.settings.core.InstrumentedPreferenceFragment; +import com.android.settings.core.instrumentation.Instrumentable; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.search.actionbar.SearchMenuController; import com.android.settings.support.actionbar.HelpMenuController; @@ -52,7 +53,6 @@ import com.android.settings.support.actionbar.HelpResourceProvider; import com.android.settings.widget.LoadingViewController; import com.android.settingslib.CustomDialogPreference; import com.android.settingslib.CustomEditTextPreference; -import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.widget.FooterPreferenceMixin; import java.util.UUID; diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 1c674b68f15..ad951216781 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -110,7 +110,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.wrapper.DevicePolicyManagerWrapper; import com.android.settings.wrapper.FingerprintManagerWrapper; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import java.net.InetAddress; import java.util.ArrayList; @@ -577,7 +576,7 @@ public final class Utils extends com.android.settingslib.Utils { intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut); - intent.putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory); + intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory); return intent; } diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java index 6127ab9bccc..c0bf7d21ef9 100644 --- a/src/com/android/settings/accounts/AccountPreferenceController.java +++ b/src/com/android/settings/accounts/AccountPreferenceController.java @@ -51,12 +51,12 @@ import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.SearchIndexableRaw; import com.android.settingslib.RestrictedPreference; import com.android.settingslib.accounts.AuthenticatorHelper; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java index c172137664b..c10fb55c9ec 100644 --- a/src/com/android/settings/applications/UsageAccessDetails.java +++ b/src/com/android/settings/applications/UsageAccessDetails.java @@ -37,8 +37,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.applications.AppStateUsageBridge.UsageState; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener, OnPreferenceClickListener { diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index 3fd7ced3e03..a0ce73386a5 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -33,10 +33,10 @@ import android.widget.ImageView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.GearPreference; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java index cea0147cecf..a12d1a8ecb2 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java @@ -23,9 +23,9 @@ import android.support.v7.preference.Preference; import android.text.TextUtils; import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; public class BluetoothDeviceRenamePreferenceController extends diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java index 0f294bd5748..87fa43d2db2 100644 --- a/src/com/android/settings/bluetooth/BluetoothEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java @@ -27,12 +27,12 @@ import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.widget.SwitchWidgetController; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.WirelessUtils; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** * BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox diff --git a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java index 1ecfed42515..450c7b2320a 100644 --- a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java @@ -23,9 +23,9 @@ import android.support.v7.preference.Preference; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** * Controller that shows received files diff --git a/src/com/android/settings/core/InstrumentedActivity.java b/src/com/android/settings/core/InstrumentedActivity.java index 294de2cb957..9b24756b19e 100644 --- a/src/com/android/settings/core/InstrumentedActivity.java +++ b/src/com/android/settings/core/InstrumentedActivity.java @@ -16,9 +16,8 @@ package com.android.settings.core; -import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; +import com.android.settings.core.instrumentation.Instrumentable; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.core.lifecycle.ObservableActivity; /** @@ -28,8 +27,7 @@ public abstract class InstrumentedActivity extends ObservableActivity implements public InstrumentedActivity() { // Mixin that logs visibility change for activity. - getLifecycle().addObserver(new VisibilityLoggerMixin(getMetricsCategory(), - FeatureFactory.getFactory(this).getMetricsFeatureProvider())); + getLifecycle().addObserver(new VisibilityLoggerMixin(getMetricsCategory())); } } diff --git a/src/com/android/settings/core/InstrumentedFragment.java b/src/com/android/settings/core/InstrumentedFragment.java index b1215b9ac4a..45db836efcc 100644 --- a/src/com/android/settings/core/InstrumentedFragment.java +++ b/src/com/android/settings/core/InstrumentedFragment.java @@ -18,28 +18,30 @@ package com.android.settings.core; import android.content.Context; +import com.android.settings.core.instrumentation.Instrumentable; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; import com.android.settings.overlay.FeatureFactory; import com.android.settings.survey.SurveyMixin; -import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.core.lifecycle.ObservableFragment; public abstract class InstrumentedFragment extends ObservableFragment implements Instrumentable { protected MetricsFeatureProvider mMetricsFeatureProvider; - private VisibilityLoggerMixin mVisibilityLoggerMixin; + private final VisibilityLoggerMixin mVisibilityLoggerMixin; + + public InstrumentedFragment() { + // Mixin that logs visibility change for activity. + mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory()); + getLifecycle().addObserver(mVisibilityLoggerMixin); + getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName())); + } @Override public void onAttach(Context context) { - mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); - mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(), - mMetricsFeatureProvider); - // Mixin that logs visibility change for activity. - getLifecycle().addObserver(mVisibilityLoggerMixin); - getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName())); super.onAttach(context); + mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } @Override diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java index 278676c52ea..7e37115bab0 100644 --- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java +++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java @@ -23,11 +23,11 @@ import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import android.util.Log; +import com.android.settings.core.instrumentation.Instrumentable; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; import com.android.settings.overlay.FeatureFactory; import com.android.settings.survey.SurveyMixin; -import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment; /** @@ -44,17 +44,19 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc // metrics placeholder value. Only use this for development. protected final int PLACEHOLDER_METRIC = 10000; - private VisibilityLoggerMixin mVisibilityLoggerMixin; + private final VisibilityLoggerMixin mVisibilityLoggerMixin; + + public InstrumentedPreferenceFragment() { + // Mixin that logs visibility change for activity. + mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory()); + getLifecycle().addObserver(mVisibilityLoggerMixin); + getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName())); + } @Override public void onAttach(Context context) { - mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); - // Mixin that logs visibility change for activity. - mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(), - mMetricsFeatureProvider); - getLifecycle().addObserver(mVisibilityLoggerMixin); - getLifecycle().addObserver(new SurveyMixin(this, getClass().getSimpleName())); super.onAttach(context); + mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } @Override diff --git a/src/com/android/settings/core/instrumentation/EventLogWriter.java b/src/com/android/settings/core/instrumentation/EventLogWriter.java new file mode 100644 index 00000000000..3196f76b323 --- /dev/null +++ b/src/com/android/settings/core/instrumentation/EventLogWriter.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +import android.content.Context; +import android.metrics.LogMaker; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; + +/** + * {@link LogWriter} that writes data to eventlog. + */ +public class EventLogWriter implements LogWriter { + + private final MetricsLogger mMetricsLogger = new MetricsLogger(); + + public void visible(Context context, int source, int category) { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_OPEN) + .addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); + MetricsLogger.action(logMaker); + } + + public void hidden(Context context, int category) { + MetricsLogger.hidden(context, category); + } + + public void action(int category, int value, Pair... taggedData) { + if (taggedData == null || taggedData.length == 0) { + mMetricsLogger.action(category, value); + } else { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION) + .setSubtype(value); + for (Pair pair : taggedData) { + logMaker.addTaggedData(pair.first, pair.second); + } + mMetricsLogger.write(logMaker); + } + } + + public void action(int category, boolean value, Pair... taggedData) { + action(category, value ? 1 : 0, taggedData); + } + + public void action(Context context, int category, Pair... taggedData) { + action(context, category, "", taggedData); + } + + public void actionWithSource(Context context, int source, int category) { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION); + if (source != MetricsProto.MetricsEvent.VIEW_UNKNOWN) { + logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); + } + MetricsLogger.action(logMaker); + } + + /** @deprecated use {@link #action(int, int, Pair[])} */ + @Deprecated + public void action(Context context, int category, int value) { + MetricsLogger.action(context, category, value); + } + + /** @deprecated use {@link #action(int, boolean, Pair[])} */ + @Deprecated + public void action(Context context, int category, boolean value) { + MetricsLogger.action(context, category, value); + } + + public void action(Context context, int category, String pkg, + Pair... taggedData) { + if (taggedData == null || taggedData.length == 0) { + MetricsLogger.action(context, category, pkg); + } else { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION) + .setPackageName(pkg); + for (Pair pair : taggedData) { + logMaker.addTaggedData(pair.first, pair.second); + } + MetricsLogger.action(logMaker); + } + } + + public void count(Context context, String name, int value) { + MetricsLogger.count(context, name, value); + } + + public void histogram(Context context, String name, int bucket) { + MetricsLogger.histogram(context, name, bucket); + } +} diff --git a/src/com/android/settings/core/instrumentation/Instrumentable.java b/src/com/android/settings/core/instrumentation/Instrumentable.java new file mode 100644 index 00000000000..f58e140b62a --- /dev/null +++ b/src/com/android/settings/core/instrumentation/Instrumentable.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +public interface Instrumentable { + + int METRICS_CATEGORY_UNKNOWN = 0; + + /** + * Instrumented name for a view as defined in + * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}. + */ + int getMetricsCategory(); +} diff --git a/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java b/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java index 0a214f17315..5a9ab56ea99 100644 --- a/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java +++ b/src/com/android/settings/core/instrumentation/InstrumentedDialogFragment.java @@ -19,9 +19,6 @@ import android.content.Context; import com.android.settings.DialogCreatable; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.core.lifecycle.ObservableDialogFragment; public abstract class InstrumentedDialogFragment extends ObservableDialogFragment @@ -41,15 +38,13 @@ public abstract class InstrumentedDialogFragment extends ObservableDialogFragmen public InstrumentedDialogFragment(DialogCreatable dialogCreatable, int dialogId) { mDialogCreatable = dialogCreatable; mDialogId = dialogId; + mLifecycle.addObserver(new VisibilityLoggerMixin(getMetricsCategory())); } + @Override public void onAttach(Context context) { super.onAttach(context); - mMetricsFeatureProvider = FeatureFactory.getFactory(context) - .getMetricsFeatureProvider(); - mLifecycle.addObserver(new VisibilityLoggerMixin(getMetricsCategory(), - mMetricsFeatureProvider)); - mLifecycle.onAttach(context); + mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } } diff --git a/src/com/android/settings/core/instrumentation/LogWriter.java b/src/com/android/settings/core/instrumentation/LogWriter.java new file mode 100644 index 00000000000..062d46f759f --- /dev/null +++ b/src/com/android/settings/core/instrumentation/LogWriter.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +import android.content.Context; +import android.util.Pair; + +/** + * Generic log writer interface. + */ +public interface LogWriter { + + /** + * Logs a visibility event when view becomes visible. + */ + void visible(Context context, int source, int category); + + /** + * Logs a visibility event when view becomes hidden. + */ + void hidden(Context context, int category); + + /** + * Logs a user action. + */ + void action(int category, int value, Pair... taggedData); + + /** + * Logs a user action. + */ + void action(int category, boolean value, Pair... taggedData); + + /** + * Logs an user action. + */ + void action(Context context, int category, Pair... taggedData); + + /** + * Logs an user action. + */ + void actionWithSource(Context context, int source, int category); + + /** + * Logs an user action. + * @deprecated use {@link #action(int, int, Pair[])} + */ + @Deprecated + void action(Context context, int category, int value); + + /** + * Logs an user action. + * @deprecated use {@link #action(int, boolean, Pair[])} + */ + @Deprecated + void action(Context context, int category, boolean value); + + /** + * Logs an user action. + */ + void action(Context context, int category, String pkg, Pair... taggedData); + + /** + * Logs a count. + */ + void count(Context context, String name, int value); + + /** + * Logs a histogram event. + */ + void histogram(Context context, String name, int bucket); +} diff --git a/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java new file mode 100644 index 00000000000..166cbb84471 --- /dev/null +++ b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; +import android.util.Pair; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + +import java.util.ArrayList; +import java.util.List; + +/** + * FeatureProvider for metrics. + */ +public class MetricsFeatureProvider { + private List mLoggerWriters; + + public MetricsFeatureProvider() { + mLoggerWriters = new ArrayList<>(); + installLogWriters(); + } + + protected void installLogWriters() { + mLoggerWriters.add(new EventLogWriter()); + } + + public void visible(Context context, int source, int category) { + for (LogWriter writer : mLoggerWriters) { + writer.visible(context, source, category); + } + } + + public void hidden(Context context, int category) { + for (LogWriter writer : mLoggerWriters) { + writer.hidden(context, category); + } + } + + public void actionWithSource(Context context, int source, int category) { + for (LogWriter writer : mLoggerWriters) { + writer.actionWithSource(context, source, category); + } + } + + /** + * Logs a user action. Includes the elapsed time since the containing + * fragment has been visible. + */ + public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(category, value, + sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); + } + } + + /** + * Logs a user action. Includes the elapsed time since the containing + * fragment has been visible. + */ + public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(category, value, + sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); + } + } + + public void action(Context context, int category, Pair... taggedData) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, taggedData); + } + } + + /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */ + @Deprecated + public void action(Context context, int category, int value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, value); + } + } + + /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */ + @Deprecated + public void action(Context context, int category, boolean value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, value); + } + } + + public void action(Context context, int category, String pkg, + Pair... taggedData) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, pkg, taggedData); + } + } + + public void count(Context context, String name, int value) { + for (LogWriter writer : mLoggerWriters) { + writer.count(context, name, value); + } + } + + public void histogram(Context context, String name, int bucket) { + for (LogWriter writer : mLoggerWriters) { + writer.histogram(context, name, bucket); + } + } + + public int getMetricsCategory(Object object) { + if (object == null || !(object instanceof Instrumentable)) { + return MetricsEvent.VIEW_UNKNOWN; + } + return ((Instrumentable) object).getMetricsCategory(); + } + + public void logDashboardStartIntent(Context context, Intent intent, + int sourceMetricsCategory) { + if (intent == null) { + return; + } + final ComponentName cn = intent.getComponent(); + if (cn == null) { + final String action = intent.getAction(); + if (TextUtils.isEmpty(action)) { + // Not loggable + return; + } + action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action, + Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); + return; + } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) { + // Going to a Setting internal page, skip click logging in favor of page's own + // visibility logging. + return; + } + action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(), + Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); + } + + private Pair sinceVisibleTaggedData(long timestamp) { + return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp); + } +} diff --git a/src/com/android/settings/core/instrumentation/SharedPreferencesLogger.java b/src/com/android/settings/core/instrumentation/SharedPreferencesLogger.java new file mode 100644 index 00000000000..dee40c043ce --- /dev/null +++ b/src/com/android/settings/core/instrumentation/SharedPreferencesLogger.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.overlay.FeatureFactory; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +public class SharedPreferencesLogger implements SharedPreferences { + + private static final String LOG_TAG = "SharedPreferencesLogger"; + + private final String mTag; + private final Context mContext; + private final MetricsFeatureProvider mMetricsFeature; + private final Set mPreferenceKeySet; + + public SharedPreferencesLogger(Context context, String tag) { + mContext = context; + mTag = tag; + mMetricsFeature = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); + mPreferenceKeySet = new ConcurrentSkipListSet<>(); + } + + @Override + public Map getAll() { + return null; + } + + @Override + public String getString(String key, @Nullable String defValue) { + return defValue; + } + + @Override + public Set getStringSet(String key, @Nullable Set defValues) { + return defValues; + } + + @Override + public int getInt(String key, int defValue) { + return defValue; + } + + @Override + public long getLong(String key, long defValue) { + return defValue; + } + + @Override + public float getFloat(String key, float defValue) { + return defValue; + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + return defValue; + } + + @Override + public boolean contains(String key) { + return false; + } + + @Override + public Editor edit() { + return new EditorLogger(); + } + + @Override + public void registerOnSharedPreferenceChangeListener( + OnSharedPreferenceChangeListener listener) { + } + + @Override + public void unregisterOnSharedPreferenceChangeListener( + OnSharedPreferenceChangeListener listener) { + } + + private void logValue(String key, Object value) { + logValue(key, value, false /* forceLog */); + } + + private void logValue(String key, Object value, boolean forceLog) { + final String prefKey = buildPrefKey(mTag, key); + if (!forceLog && !mPreferenceKeySet.contains(prefKey)) { + // Pref key doesn't exist in set, this is initial display so we skip metrics but + // keeps track of this key. + mPreferenceKeySet.add(prefKey); + return; + } + // TODO: Remove count logging to save some resource. + mMetricsFeature.count(mContext, buildCountName(prefKey, value), 1); + + final Pair valueData; + if (value instanceof Long) { + final Long longVal = (Long) value; + final int intVal; + if (longVal > Integer.MAX_VALUE) { + intVal = Integer.MAX_VALUE; + } else if (longVal < Integer.MIN_VALUE) { + intVal = Integer.MIN_VALUE; + } else { + intVal = longVal.intValue(); + } + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, + intVal); + } else if (value instanceof Integer) { + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, + value); + } else if (value instanceof Boolean) { + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, + (Boolean) value ? 1 : 0); + } else if (value instanceof Float) { + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, + value); + } else if (value instanceof String) { + Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value); + valueData = null; + } else { + Log.w(LOG_TAG, "Tried to log unloggable object" + value); + valueData = null; + } + if (valueData != null) { + // Pref key exists in set, log it's change in metrics. + mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, + Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey), + valueData); + } + } + + @VisibleForTesting + void logPackageName(String key, String value) { + final String prefKey = mTag + "/" + key; + mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value, + Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey)); + } + + private void safeLogValue(String key, String value) { + new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value); + } + + public static String buildCountName(String prefKey, Object value) { + return prefKey + "|" + value; + } + + public static String buildPrefKey(String tag, String key) { + return tag + "/" + key; + } + + private class AsyncPackageCheck extends AsyncTask { + @Override + protected Void doInBackground(String... params) { + String key = params[0]; + String value = params[1]; + PackageManager pm = mContext.getPackageManager(); + try { + // Check if this might be a component. + ComponentName name = ComponentName.unflattenFromString(value); + if (value != null) { + value = name.getPackageName(); + } + } catch (Exception e) { + } + try { + pm.getPackageInfo(value, PackageManager.MATCH_ANY_USER); + logPackageName(key, value); + } catch (PackageManager.NameNotFoundException e) { + // Clearly not a package, and it's unlikely this preference is in prefSet, so + // lets force log it. + logValue(key, value, true /* forceLog */); + } + return null; + } + } + + public class EditorLogger implements Editor { + @Override + public Editor putString(String key, @Nullable String value) { + safeLogValue(key, value); + return this; + } + + @Override + public Editor putStringSet(String key, @Nullable Set values) { + safeLogValue(key, TextUtils.join(",", values)); + return this; + } + + @Override + public Editor putInt(String key, int value) { + logValue(key, value); + return this; + } + + @Override + public Editor putLong(String key, long value) { + logValue(key, value); + return this; + } + + @Override + public Editor putFloat(String key, float value) { + logValue(key, value); + return this; + } + + @Override + public Editor putBoolean(String key, boolean value) { + logValue(key, value); + return this; + } + + @Override + public Editor remove(String key) { + return this; + } + + @Override + public Editor clear() { + return this; + } + + @Override + public boolean commit() { + return true; + } + + @Override + public void apply() { + } + } +} diff --git a/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java new file mode 100644 index 00000000000..2fe2a3beb1d --- /dev/null +++ b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import android.os.SystemClock; +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.SettingsActivity; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnAttach; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +import static com.android.settings.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; + +/** + * Logs visibility change of a fragment. + */ +public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause, OnAttach { + + private static final String TAG = "VisibilityLoggerMixin"; + + private final int mMetricsCategory; + + private MetricsFeatureProvider mMetricsFeature; + private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN; + private long mVisibleTimestamp; + + public VisibilityLoggerMixin(int metricsCategory) { + // MetricsFeature will be set during onAttach. + this(metricsCategory, null /* metricsFeature */); + } + + public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) { + mMetricsCategory = metricsCategory; + mMetricsFeature = metricsFeature; + } + + @Override + public void onAttach(Context context) { + mMetricsFeature = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); + } + + @Override + public void onResume() { + mVisibleTimestamp = SystemClock.elapsedRealtime(); + if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { + mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory); + } + } + + @Override + public void onPause() { + mVisibleTimestamp = 0; + if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { + mMetricsFeature.hidden(null /* context */, mMetricsCategory); + } + } + + /** + * Sets source metrics category for this logger. Source is the caller that opened this UI. + */ + public void setSourceMetricsCategory(Activity activity) { + if (mSourceMetricsCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN || activity == null) { + return; + } + final Intent intent = activity.getIntent(); + if (intent == null) { + return; + } + mSourceMetricsCategory = intent.getIntExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, + MetricsProto.MetricsEvent.VIEW_UNKNOWN); + } + + /** Returns elapsed time since onResume() */ + public long elapsedTimeSinceVisible() { + if (mVisibleTimestamp == 0) { + return 0; + } + return SystemClock.elapsedRealtime() - mVisibleTimestamp; + } +} diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java index 2d35ea78d33..97eef1329d5 100644 --- a/src/com/android/settings/dashboard/DashboardAdapter.java +++ b/src/com/android/settings/dashboard/DashboardAdapter.java @@ -41,6 +41,7 @@ import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.R.id; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardData.SuggestionConditionHeaderData; import com.android.settings.dashboard.conditional.Condition; import com.android.settings.dashboard.conditional.ConditionAdapter; @@ -49,7 +50,6 @@ import com.android.settings.dashboard.suggestions.SuggestionControllerMixin; import com.android.settings.dashboard.suggestions.SuggestionDismissController; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.Utils; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.Tile; diff --git a/src/com/android/settings/dashboard/DashboardAdapterV2.java b/src/com/android/settings/dashboard/DashboardAdapterV2.java index f98e8331adb..cc511c5f875 100644 --- a/src/com/android/settings/dashboard/DashboardAdapterV2.java +++ b/src/com/android/settings/dashboard/DashboardAdapterV2.java @@ -39,13 +39,13 @@ import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.R.id; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardDataV2.ConditionHeaderData; import com.android.settings.dashboard.conditional.Condition; import com.android.settings.dashboard.conditional.ConditionAdapterV2; import com.android.settings.dashboard.suggestions.SuggestionAdapterV2; import com.android.settings.dashboard.suggestions.SuggestionControllerMixin; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index a14d9e9a76d..a06fee9fbc1 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -41,9 +41,8 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.FeatureFlags; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.drawer.CategoryManager; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.ProfileSelectDialog; @@ -160,8 +159,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { pref.setFragment(clsName); } else if (tile.intent != null) { final Intent intent = new Intent(tile.intent); - intent.putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, - sourceMetricsCategory); + intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory); if (action != null) { intent.setAction(action); } @@ -210,7 +208,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { return; } final Intent intent = new Intent(tile.intent) - .putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, + .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, MetricsEvent.DASHBOARD_SUMMARY) .putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); diff --git a/src/com/android/settings/dashboard/conditional/Condition.java b/src/com/android/settings/dashboard/conditional/Condition.java index d66440e9e89..05783bde798 100644 --- a/src/com/android/settings/dashboard/conditional/Condition.java +++ b/src/com/android/settings/dashboard/conditional/Condition.java @@ -24,8 +24,8 @@ import android.os.PersistableBundle; import android.support.annotation.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public abstract class Condition { diff --git a/src/com/android/settings/dashboard/conditional/ConditionAdapter.java b/src/com/android/settings/dashboard/conditional/ConditionAdapter.java index d84aa7c9eac..eb768e50d0e 100644 --- a/src/com/android/settings/dashboard/conditional/ConditionAdapter.java +++ b/src/com/android/settings/dashboard/conditional/ConditionAdapter.java @@ -27,13 +27,13 @@ import android.widget.Button; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardAdapter; import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder; import com.android.settings.dashboard.DashboardData; import com.android.settings.dashboard.DashboardData.HeaderMode; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.WirelessUtils; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.List; import java.util.Objects; diff --git a/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java b/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java index 8db57f79678..3f3e5c91daf 100644 --- a/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java +++ b/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java @@ -27,10 +27,10 @@ import android.widget.Button; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.WirelessUtils; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.List; import java.util.Objects; diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java index 2b79a9babfe..fc1102901c0 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java @@ -27,10 +27,10 @@ import android.view.ViewGroup; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder; import com.android.settings.dashboard.DashboardAdapter.IconCache; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.List; import java.util.Objects; diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java index 56cd4529eab..89c731f6aa4 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java @@ -31,10 +31,10 @@ import android.widget.LinearLayout; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder; import com.android.settings.dashboard.DashboardAdapterV2.IconCache; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java index 8cd1675ac2b..fe19b958c2d 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java @@ -36,6 +36,7 @@ import com.android.settings.Settings.DoubleTapPowerSuggestionActivity; import com.android.settings.Settings.DoubleTwistSuggestionActivity; import com.android.settings.Settings.NightDisplaySuggestionActivity; import com.android.settings.Settings.SwipeToNotificationSuggestionActivity; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.fingerprint.FingerprintEnrollSuggestionActivity; import com.android.settings.fingerprint.FingerprintSuggestionActivity; import com.android.settings.gestures.DoubleTapPowerPreferenceController; @@ -48,7 +49,6 @@ import com.android.settings.password.ScreenLockSuggestionActivity; import com.android.settings.support.NewDeviceIntroSuggestionActivity; import com.android.settings.wallpaper.WallpaperSuggestionActivity; import com.android.settings.wifi.WifiCallingSuggestionActivity; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.drawer.Tile; import java.util.List; diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java index a0d388df1d0..041a81f06be 100644 --- a/src/com/android/settings/datausage/DataSaverBackend.java +++ b/src/com/android/settings/datausage/DataSaverBackend.java @@ -25,8 +25,8 @@ import android.os.RemoteException; import android.util.SparseIntArray; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; import java.util.ArrayList; diff --git a/src/com/android/settings/datetime/ZonePicker.java b/src/com/android/settings/datetime/ZonePicker.java index dc691275e31..57c340c6809 100644 --- a/src/com/android/settings/datetime/ZonePicker.java +++ b/src/com/android/settings/datetime/ZonePicker.java @@ -35,9 +35,8 @@ import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; -import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; +import com.android.settings.core.instrumentation.Instrumentable; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.datetime.ZoneGetter; import java.text.Collator; @@ -58,7 +57,8 @@ public class ZonePicker extends ListFragment implements Instrumentable { private static final int MENU_TIMEZONE = Menu.FIRST+1; private static final int MENU_ALPHABETICAL = Menu.FIRST; - private VisibilityLoggerMixin mVisibilityLoggerMixin; + private final VisibilityLoggerMixin mVisibilityLoggerMixin = + new VisibilityLoggerMixin(getMetricsCategory()); private boolean mSortedByTimezone; @@ -144,6 +144,12 @@ public class ZonePicker extends ListFragment implements Instrumentable { return -1; } + @Override + public void onAttach(Context context) { + super.onAttach(context); + mVisibilityLoggerMixin.onAttach(context); + } + @Override public int getMetricsCategory() { return MetricsProto.MetricsEvent.ZONE_PICKER; @@ -163,13 +169,6 @@ public class ZonePicker extends ListFragment implements Instrumentable { activity.setTitle(R.string.date_time_set_timezone); } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(), - FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()); - } - @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java index a20afa1060a..8ab1a07fb13 100644 --- a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java +++ b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarController.java @@ -23,10 +23,10 @@ import android.provider.Settings; import android.support.v7.preference.Preference; import android.widget.Switch; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.Preconditions; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.widget.SwitchBar; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; /** Handles the logic for flipping the storage management toggle on a {@link SwitchBar}. */ public class AutomaticStorageManagerSwitchBarController diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java index 0f3bfb8ec76..2641f5d941f 100644 --- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java +++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java @@ -34,11 +34,11 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnResume; diff --git a/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java index 8ab21b36fdb..717d7650b75 100644 --- a/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java +++ b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java @@ -25,12 +25,12 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.PreferenceScreen; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.deletionhelper.ActivationWarningFragment; import com.android.settings.widget.MasterSwitchController; import com.android.settings.widget.MasterSwitchPreference; import com.android.settings.widget.SwitchWidgetController; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnResume; diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java index 36232985934..1149b99bce6 100644 --- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java +++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java @@ -38,11 +38,11 @@ import com.android.settings.Settings; import com.android.settings.Utils; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.deviceinfo.PrivateVolumeSettings.SystemInfoFragment; import com.android.settings.deviceinfo.StorageItemPreference; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.deviceinfo.StorageMeasurement; import com.android.settingslib.deviceinfo.StorageVolumeProvider; diff --git a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java index f3d17d58939..68a21cebf9c 100644 --- a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java +++ b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java @@ -27,11 +27,11 @@ import android.support.v7.preference.Preference; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.search.DatabaseIndexingUtils; import com.android.settings.search.InlineSwitchPayload; import com.android.settings.search.ResultPayload; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class AmbientDisplayNotificationsPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, diff --git a/src/com/android/settings/display/AmbientDisplaySettings.java b/src/com/android/settings/display/AmbientDisplaySettings.java index 187325c0c7f..24aede05ef6 100644 --- a/src/com/android/settings/display/AmbientDisplaySettings.java +++ b/src/com/android/settings/display/AmbientDisplaySettings.java @@ -23,13 +23,13 @@ import android.provider.SearchIndexableResource; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.gestures.DoubleTapScreenPreferenceController; import com.android.settings.gestures.PickupGesturePreferenceController; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.ArrayList; diff --git a/src/com/android/settings/display/AutoRotatePreferenceController.java b/src/com/android/settings/display/AutoRotatePreferenceController.java index 2134b882638..c7f6af175a1 100644 --- a/src/com/android/settings/display/AutoRotatePreferenceController.java +++ b/src/com/android/settings/display/AutoRotatePreferenceController.java @@ -20,9 +20,9 @@ import android.support.v7.preference.TwoStatePreference; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.view.RotationPolicy; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; diff --git a/src/com/android/settings/display/ThemePreferenceController.java b/src/com/android/settings/display/ThemePreferenceController.java index 9c1314ebc5e..d1341dd7435 100644 --- a/src/com/android/settings/display/ThemePreferenceController.java +++ b/src/com/android/settings/display/ThemePreferenceController.java @@ -29,9 +29,9 @@ import android.text.TextUtils; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import libcore.util.Objects; diff --git a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java index a52433b374c..c0347466812 100644 --- a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java +++ b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java @@ -48,6 +48,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.ActionButtonPreference; import com.android.settings.wrapper.DevicePolicyManagerWrapper; @@ -55,7 +56,6 @@ import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnDestroy; diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java index 91f35e2533a..5d95dd2ec71 100644 --- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java @@ -49,10 +49,10 @@ import com.android.settings.SettingsActivity; import com.android.settings.core.FeatureFlags; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.Utils; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnDestroy; diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index e0954e57f25..ec54291e3ed 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -47,6 +47,7 @@ import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.manageapplications.ManageApplications; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.SummaryLoader; import com.android.settings.display.AmbientDisplayPreferenceController; import com.android.settings.display.AutoBrightnessPreferenceController; @@ -60,7 +61,6 @@ import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.ArrayList; diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java index 605591dda15..c50d5808629 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummaryLegacy.java @@ -54,6 +54,7 @@ import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.manageapplications.ManageApplications; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.SummaryLoader; import com.android.settings.display.AmbientDisplayPreferenceController; import com.android.settings.display.AutoBrightnessPreferenceController; @@ -66,7 +67,6 @@ import com.android.settings.fuelgauge.anomaly.AnomalyLoader; import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController; import com.android.settings.fuelgauge.anomaly.AnomalyUtils; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.AbstractPreferenceController; import java.util.ArrayList; diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java index 3dde95eff5b..39d51dc08af 100644 --- a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java +++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java @@ -24,6 +24,7 @@ import android.util.SparseIntArray; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.BatteryStatsHelper; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; import com.android.settings.fuelgauge.anomaly.action.ForceStopAction; import com.android.settings.fuelgauge.anomaly.action.LocationCheckAction; @@ -32,7 +33,6 @@ import com.android.settings.fuelgauge.anomaly.checker.AnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.ArrayList; import java.util.List; diff --git a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java index d7de5a7ac2a..3ee89d1c4a2 100644 --- a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java +++ b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java @@ -20,9 +20,9 @@ import android.content.Context; import android.util.Pair; import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** * Abstract class for anomaly action, which is triggered if we need to handle the anomaly diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java index 1bf08b7b64d..9fa69fd02c0 100644 --- a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java +++ b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryTipAction.java @@ -18,7 +18,7 @@ package com.android.settings.fuelgauge.batterytip.actions; import android.content.Context; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; /** * Abstract class for battery tip action, which is triggered if we need to handle the battery tip diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java index cbd15812e5a..a19471e6d7b 100644 --- a/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java +++ b/src/com/android/settings/fuelgauge/batterytip/actions/SmartBatteryAction.java @@ -22,8 +22,8 @@ import android.support.v14.preference.PreferenceFragment; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.fuelgauge.SmartBatterySettings; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class SmartBatteryAction extends BatteryTipAction { private SettingsActivity mSettingsActivity; diff --git a/src/com/android/settings/inputmethod/UserDictionarySettings.java b/src/com/android/settings/inputmethod/UserDictionarySettings.java index 3bbc581ac82..9680af10c60 100644 --- a/src/com/android/settings/inputmethod/UserDictionarySettings.java +++ b/src/com/android/settings/inputmethod/UserDictionarySettings.java @@ -42,11 +42,10 @@ import android.widget.SimpleCursorAdapter; import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; -import com.android.settings.overlay.FeatureFactory; import com.android.settings.R; import com.android.settings.SettingsActivity; -import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; +import com.android.settings.core.instrumentation.Instrumentable; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; public class UserDictionarySettings extends ListFragment implements Instrumentable, LoaderManager.LoaderCallbacks { @@ -60,7 +59,8 @@ public class UserDictionarySettings extends ListFragment implements Instrumentab private static final int OPTIONS_MENU_ADD = Menu.FIRST; private static final int LOADER_ID = 1; - private VisibilityLoggerMixin mVisibilityLoggerMixin; + private final VisibilityLoggerMixin mVisibilityLoggerMixin = + new VisibilityLoggerMixin(getMetricsCategory()); private Cursor mCursor; private String mLocale; @@ -70,13 +70,16 @@ public class UserDictionarySettings extends ListFragment implements Instrumentab return MetricsProto.MetricsEvent.USER_DICTIONARY_SETTINGS; } + @Override + public void onAttach(Context context) { + super.onAttach(context); + mVisibilityLoggerMixin.onAttach(context); + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(), - FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()); - final Intent intent = getActivity().getIntent(); final String localeFromIntent = null == intent ? null : intent.getStringExtra("locale"); diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java index 0b771791be7..17cf211e9dd 100644 --- a/src/com/android/settings/network/AirplaneModePreferenceController.java +++ b/src/com/android/settings/network/AirplaneModePreferenceController.java @@ -28,10 +28,10 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; import com.android.settings.AirplaneModeEnabler; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.R; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java index 74c191006d4..4b1da310bdd 100644 --- a/src/com/android/settings/network/NetworkDashboardFragment.java +++ b/src/com/android/settings/network/NetworkDashboardFragment.java @@ -31,13 +31,13 @@ import android.view.MenuInflater; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.SummaryLoader; import com.android.settings.network.MobilePlanPreferenceController.MobilePlanPreferenceHost; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.wifi.WifiMasterSwitchPreferenceController; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.ArrayList; diff --git a/src/com/android/settings/notification/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/AbstractZenModePreferenceController.java index 9180791fd55..81ceca19c77 100644 --- a/src/com/android/settings/notification/AbstractZenModePreferenceController.java +++ b/src/com/android/settings/notification/AbstractZenModePreferenceController.java @@ -34,9 +34,9 @@ import android.support.v7.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; diff --git a/src/com/android/settings/notification/ZenRulePreference.java b/src/com/android/settings/notification/ZenRulePreference.java index fee390f5edf..71938732a2b 100644 --- a/src/com/android/settings/notification/ZenRulePreference.java +++ b/src/com/android/settings/notification/ZenRulePreference.java @@ -30,10 +30,10 @@ import android.view.View; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.utils.ManagedServiceSettings; import com.android.settings.utils.ZenServiceListing; import com.android.settingslib.TwoTargetPreference; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.Map; @@ -145,4 +145,4 @@ public class ZenRulePreference extends TwoTargetPreference { ? mContext.getResources().getString(R.string.switch_off_text) : mContext.getResources().getString(R.string.switch_on_text); } -} +} \ No newline at end of file diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 08057664c1c..dc9df8399f9 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -24,6 +24,7 @@ import com.android.settings.R; import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.connecteddevice.SmsMirroringFeatureProvider; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.datausage.DataPlanFeatureProvider; @@ -35,7 +36,6 @@ import com.android.settings.security.SecurityFeatureProvider; import com.android.settings.search.SearchFeatureProvider; import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** * Abstract class for creating feature controllers. Allows OEM implementations to define their own diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index f817d4bb218..275ebb66865 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -29,6 +29,7 @@ import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.bluetooth.BluetoothFeatureProviderImpl; import com.android.settings.connecteddevice.SmsMirroringFeatureProvider; import com.android.settings.connecteddevice.SmsMirroringFeatureProviderImpl; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProviderImpl; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; @@ -54,7 +55,6 @@ import com.android.settings.users.UserFeatureProviderImpl; import com.android.settings.wrapper.ConnectivityManagerWrapper; import com.android.settings.wrapper.DevicePolicyManagerWrapper; import com.android.settings.wrapper.IPackageManagerWrapper; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.wrapper.PackageManagerWrapper; /** diff --git a/src/com/android/settings/widget/SwitchBar.java b/src/com/android/settings/widget/SwitchBar.java index 3be5eca3c2e..749ec0a919b 100644 --- a/src/com/android/settings/widget/SwitchBar.java +++ b/src/com/android/settings/widget/SwitchBar.java @@ -39,9 +39,9 @@ import android.widget.Switch; import android.widget.TextView; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.RestrictedLockUtils; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.ArrayList; import java.util.List; diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java index c5e79b2db49..9c431422371 100644 --- a/src/com/android/settings/wifi/WifiEnabler.java +++ b/src/com/android/settings/wifi/WifiEnabler.java @@ -33,9 +33,9 @@ import android.widget.Toast; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.widget.SwitchWidgetController; import com.android.settings.wrapper.ConnectivityManagerWrapper; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.WirelessUtils; diff --git a/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java b/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java index 8843d93775a..de1b030f3dc 100644 --- a/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java +++ b/src/com/android/settings/wifi/WifiMasterSwitchPreferenceController.java @@ -19,12 +19,12 @@ import android.content.Context; import android.support.v7.preference.PreferenceScreen; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.R; import com.android.settings.widget.SummaryUpdater; import com.android.settings.widget.MasterSwitchPreference; import com.android.settings.widget.MasterSwitchController; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java index 13ffd5bee44..70ee20de03d 100644 --- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -54,6 +54,7 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.applications.LayoutPreference; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.widget.ActionButtonPreference; import com.android.settings.widget.EntityHeaderController; import com.android.settings.wifi.WifiDetailPreference; @@ -62,7 +63,6 @@ import com.android.settings.wifi.WifiDialog.WifiDialogListener; import com.android.settings.wifi.WifiUtils; import com.android.settings.wrapper.ConnectivityManagerWrapper; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; diff --git a/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java b/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java index 3a7d094747f..942634a0a71 100644 --- a/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/SettingsDialogFragmentTest.java @@ -17,7 +17,6 @@ package com.android.settings; import android.app.Dialog; import android.app.Fragment; -import android.content.Context; import org.junit.Before; import org.junit.Test; @@ -39,8 +38,6 @@ public class SettingsDialogFragmentTest { private static final int DIALOG_ID = 15; - @Mock - private Context mContext; @Mock private DialogCreatableFragment mDialogCreatable; private SettingsPreferenceFragment.SettingsDialogFragment mDialogFragment; @@ -56,10 +53,9 @@ public class SettingsDialogFragmentTest { mDialogFragment = new SettingsPreferenceFragment.SettingsDialogFragment(mDialogCreatable, DIALOG_ID); - mDialogFragment.onAttach(mContext); mDialogFragment.getMetricsCategory(); - // getDialogMetricsCategory called in onAttach, and explicitly in test. + // getDialogMetricsCategory called in constructor, and explicitly in test. verify(mDialogCreatable, times(2)).getDialogMetricsCategory(DIALOG_ID); } @@ -70,7 +66,6 @@ public class SettingsDialogFragmentTest { try { mDialogFragment = new SettingsPreferenceFragment.SettingsDialogFragment( mDialogCreatable, DIALOG_ID); - mDialogFragment.onAttach(mContext); } catch (IllegalStateException e) { // getDialogMetricsCategory called in constructor verify(mDialogCreatable).getDialogMetricsCategory(DIALOG_ID); diff --git a/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java b/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java index f85d43aac6a..5c0badcc475 100644 --- a/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/instantapps/InstantAppButtonsControllerTest.java @@ -42,9 +42,9 @@ import android.widget.Button; import com.android.settings.R; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.wrapper.PackageManagerWrapper; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java index 71020be315a..e9d37f6a0c2 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java @@ -32,11 +32,11 @@ import android.os.UserManager; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java index 828b5a17335..b973edb674d 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java @@ -29,13 +29,13 @@ import android.widget.Switch; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.widget.MasterSwitchController; import com.android.settings.widget.MasterSwitchPreference; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.BeforeClass; diff --git a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java index c3b22b3a98c..8666ce3c13c 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java @@ -26,11 +26,11 @@ import android.content.Context; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java index 867b5df7336..4455549db12 100644 --- a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java +++ b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java @@ -20,7 +20,6 @@ import android.app.Fragment; import android.util.ArraySet; import com.android.settings.core.codeinspection.CodeInspector; -import com.android.settingslib.core.instrumentation.Instrumentable; import java.util.ArrayList; import java.util.List; diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java index 8ad2d696846..9e37896fa62 100644 --- a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentedDialogFragmentTest.java @@ -21,7 +21,6 @@ import android.os.Bundle; import com.android.settings.TestConfig; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java index 2950c072ad6..da48f15c4c4 100644 --- a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java +++ b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java @@ -31,9 +31,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.TestConfig; import com.android.settings.overlay.FeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settingslib.core.instrumentation.LogWriter; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import org.junit.Before; import org.junit.Test; @@ -61,6 +58,7 @@ public class MetricsFeatureProviderTest { @Mock private VisibilityLoggerMixin mockVisibilityLogger; private Context mContext; + private MetricsFeatureProvider mProvider; @Captor private ArgumentCaptor mPairCaptor; @@ -69,6 +67,12 @@ public class MetricsFeatureProviderTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; + mProvider = new MetricsFeatureProvider(); + List writers = new ArrayList<>(); + writers.add(mockLogWriter); + ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers); + + when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME); } @Test @@ -80,4 +84,60 @@ public class MetricsFeatureProviderTest { assertThat(feature1 == feature2).isTrue(); } + + @Test + public void logDashboardStartIntent_intentEmpty_shouldNotLog() { + mProvider.logDashboardStartIntent(mContext, null /* intent */, + MetricsEvent.SETTINGS_GESTURES); + + verifyNoMoreInteractions(mockLogWriter); + } + + @Test + public void logDashboardStartIntent_intentHasNoComponent_shouldLog() { + final Intent intent = new Intent(Intent.ACTION_ASSIST); + + mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); + + verify(mockLogWriter).action( + eq(mContext), + eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), + anyString(), + eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); + } + + @Test + public void logDashboardStartIntent_intentIsExternal_shouldLog() { + final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); + + mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); + + verify(mockLogWriter).action( + eq(mContext), + eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), + anyString(), + eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); + } + + @Test + public void action_BooleanLogsElapsedTime() { + mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN); + verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture()); + + Pair value = mPairCaptor.getValue(); + assertThat(value.first instanceof Integer).isTrue(); + assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); + assertThat(value.second).isEqualTo(ELAPSED_TIME); + } + + @Test + public void action_IntegerLogsElapsedTime() { + mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER); + verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture()); + + Pair value = mPairCaptor.getValue(); + assertThat(value.first instanceof Integer).isTrue(); + assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); + assertThat(value.second).isEqualTo(ELAPSED_TIME); + } } diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java new file mode 100644 index 00000000000..c80e3a89d8d --- /dev/null +++ b/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Pair; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import com.google.common.truth.Platform; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SharedPreferenceLoggerTest { + + private static final String TEST_TAG = "tag"; + private static final String TEST_KEY = "key"; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + + private ArgumentMatcher> mNamePairMatcher; + private FakeFeatureFactory mFactory; + private MetricsFeatureProvider mMetricsFeature; + private SharedPreferencesLogger mSharedPrefLogger; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + mFactory = FakeFeatureFactory.setupForTest(); + mMetricsFeature = mFactory.metricsFeatureProvider; + + mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG); + mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class); + } + + @Test + public void putInt_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putInt(TEST_KEY, 1); + editor.putInt(TEST_KEY, 1); + editor.putInt(TEST_KEY, 1); + editor.putInt(TEST_KEY, 2); + editor.putInt(TEST_KEY, 2); + editor.putInt(TEST_KEY, 2); + editor.putInt(TEST_KEY, 2); + + verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); + } + + @Test + public void putBoolean_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putBoolean(TEST_KEY, true); + editor.putBoolean(TEST_KEY, true); + editor.putBoolean(TEST_KEY, false); + editor.putBoolean(TEST_KEY, false); + editor.putBoolean(TEST_KEY, false); + + + verify(mMetricsFeature).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true))); + verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false))); + } + + @Test + public void putLong_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 2); + + verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); + } + + @Test + public void putLong_biggerThanIntMax_shouldLogIntMax() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + final long veryBigNumber = 500L + Integer.MAX_VALUE; + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, veryBigNumber); + + verify(mMetricsFeature).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches( + FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE))); + } + + @Test + public void putLong_smallerThanIntMin_shouldLogIntMin() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + final long veryNegativeNumber = -500L + Integer.MIN_VALUE; + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, veryNegativeNumber); + + verify(mMetricsFeature).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches( + FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE))); + } + + @Test + public void putFloat_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 2); + + verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class))); + } + + @Test + public void logPackage_shouldUseLogPackageApi() { + mSharedPrefLogger.logPackageName("key", "com.android.settings"); + verify(mMetricsFeature).action(any(Context.class), + eq(ACTION_SETTINGS_PREFERENCE_CHANGE), + eq("com.android.settings"), + any(Pair.class)); + } + + private ArgumentMatcher> pairMatches(int tag, Class clazz) { + return pair -> pair.first == tag && Platform.isInstanceOfType(pair.second, clazz); + } + + private ArgumentMatcher> pairMatches(int tag, boolean bool) { + return pair -> pair.first == tag + && Platform.isInstanceOfType(pair.second, Integer.class) + && pair.second.equals((bool ? 1 : 0)); + } + + private ArgumentMatcher> pairMatches(int tag, int val) { + return pair -> pair.first == tag + && Platform.isInstanceOfType(pair.second, Integer.class) + && pair.second.equals(val); + } +} diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/VisibilityLoggerMixinTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/VisibilityLoggerMixinTest.java new file mode 100644 index 00000000000..1a47a66f25e --- /dev/null +++ b/tests/robotests/src/com/android/settings/core/instrumentation/VisibilityLoggerMixinTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 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.core.instrumentation; + +import static com.android.settings.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; + +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.SettingsActivity; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class VisibilityLoggerMixinTest { + + @Mock + private MetricsFeatureProvider mMetricsFeature; + + private VisibilityLoggerMixin mMixin; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature); + } + + @Test + public void shouldLogVisibleOnResume() { + mMixin.onResume(); + + verify(mMetricsFeature, times(1)) + .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN), + eq(TestInstrumentable.TEST_METRIC)); + } + + @Test + public void shouldLogVisibleWithSource() { + final Intent sourceIntent = new Intent() + .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, + MetricsProto.MetricsEvent.SETTINGS_GESTURES); + final Activity activity = mock(Activity.class); + when(activity.getIntent()).thenReturn(sourceIntent); + mMixin.setSourceMetricsCategory(activity); + mMixin.onResume(); + + verify(mMetricsFeature, times(1)) + .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES), + eq(TestInstrumentable.TEST_METRIC)); + } + + @Test + public void shouldLogHideOnPause() { + mMixin.onPause(); + + verify(mMetricsFeature, times(1)) + .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC)); + } + + @Test + public void shouldNotLogIfMetricsFeatureIsNull() { + mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC); + mMixin.onResume(); + mMixin.onPause(); + + verify(mMetricsFeature, never()) + .hidden(nullable(Context.class), anyInt()); + } + + @Test + public void shouldNotLogIfMetricsCategoryIsUnknown() { + mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature); + + mMixin.onResume(); + mMixin.onPause(); + + verify(mMetricsFeature, never()) + .hidden(nullable(Context.class), anyInt()); + } + + private final class TestInstrumentable implements Instrumentable { + + public static final int TEST_METRIC = 12345; + + @Override + public int getMetricsCategory() { + return TEST_METRIC; + } + } +} diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java index afa914cf93f..741f2bc084c 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java @@ -51,7 +51,6 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settings.testutils.shadow.ShadowTileUtils; import com.android.settings.testutils.shadow.ShadowUserManager; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.CategoryManager; import com.android.settingslib.drawer.DashboardCategory; @@ -373,7 +372,7 @@ public class DashboardFeatureProviderImplTest { final Intent launchIntent = shadowActivity.getNextStartedActivityForResult().intent; assertThat(launchIntent.getAction()) .isEqualTo("TestAction"); - assertThat(launchIntent.getIntExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, 0)) + assertThat(launchIntent.getIntExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, 0)) .isEqualTo(MetricsProto.MetricsEvent.SETTINGS_GESTURES); } diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java index 40e590a665c..6c663ab6cef 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java @@ -33,10 +33,10 @@ import android.support.v7.preference.PreferenceScreen; import com.android.settings.TestConfig; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.TileUtils; diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java b/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java index 1a3fa5e7092..d077e6fcd77 100644 --- a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java @@ -22,7 +22,7 @@ import android.graphics.drawable.Icon; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java index 8b3c7708230..9ab88d3a805 100644 --- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java +++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java @@ -60,7 +60,6 @@ public class DataUsageListTest { @Test public void resumePause_shouldListenUnlistenDataStateChange() { - mDataUsageList.onAttach(mContext); mDataUsageList.onResume(); verify(mListener).setListener(true, 0, mContext); diff --git a/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java b/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java index 9f6d0ef984a..92807e9fcf3 100644 --- a/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java +++ b/tests/robotests/src/com/android/settings/datetime/ZonePickerTest.java @@ -28,8 +28,8 @@ import android.view.ViewGroup; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.VisibilityLoggerMixin; import com.android.settings.testutils.shadow.ShadowZoneGetter; -import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java index ab32fa28705..66ccc6ec3dd 100644 --- a/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerSwitchBarControllerTest.java @@ -31,11 +31,11 @@ import android.support.v7.preference.Preference; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowSystemProperties; import com.android.settings.widget.SwitchBar; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java index 9c566113149..90ce3953ee3 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java @@ -37,13 +37,13 @@ import android.support.v7.preference.PreferenceScreen; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.RoSystemProperties; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.deletionhelper.ActivationWarningFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowSystemProperties; import com.android.settings.widget.MasterSwitchPreference; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.After; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java index 2da756f5a1d..1a3139d509c 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java @@ -50,13 +50,13 @@ import com.android.settings.SettingsActivity; import com.android.settings.SubSettings; import com.android.settings.TestConfig; import com.android.settings.applications.manageapplications.ManageApplications; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.deviceinfo.PrivateVolumeSettings; import com.android.settings.deviceinfo.StorageItemPreference; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settingslib.applications.StorageStatsSource; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.deviceinfo.StorageVolumeProvider; import org.junit.After; diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java index e251be0db6b..6ad37ce2cec 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java @@ -38,10 +38,10 @@ import android.widget.LinearLayout; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.deviceinfo.StorageVolumeProvider; import org.junit.After; diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java index c003f176613..e1ce6945c13 100644 --- a/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java @@ -32,11 +32,11 @@ import android.support.v14.preference.SwitchPreference; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.search.InlinePayload; import com.android.settings.search.InlineSwitchPayload; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowSecureSettings; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java index 5418ead24d4..c590fd34dc1 100644 --- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java +++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java @@ -36,7 +36,6 @@ import android.widget.TextView; import com.android.settings.R; import com.android.settings.TestConfig; import com.android.settings.password.ChooseLockSettingsHelper; -import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settings.testutils.shadow.ShadowVibrator; @@ -70,15 +69,12 @@ public class FingerprintEnrollEnrollingTest { private FingerprintEnrollEnrolling mActivity; - private FakeFeatureFactory mFactory; - @Before public void setUp() { MockitoAnnotations.initMocks(this); ShadowUtils.setFingerprintManager(mFingerprintManager); ShadowVibrator.addToServiceMap(); - mFactory = FakeFeatureFactory.setupForTest(); mActivity = Robolectric.buildActivity( FingerprintEnrollEnrolling.class, new Intent() diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java index be53aa57487..d495b74e903 100644 --- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java @@ -33,7 +33,6 @@ import android.widget.Button; import com.android.settings.R; import com.android.settings.TestConfig; import com.android.settings.password.ChooseLockSettingsHelper; -import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowEventLogWriter; @@ -70,13 +69,10 @@ public class FingerprintEnrollFindSensorTest { private FingerprintEnrollFindSensor mActivity; - private FakeFeatureFactory mFactory; - @Before public void setUp() { MockitoAnnotations.initMocks(this); ShadowUtils.setFingerprintManager(mFingerprintManager); - mFactory = FakeFeatureFactory.setupForTest(); mActivity = Robolectric.buildActivity( FingerprintEnrollFindSensor.class, diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java index f52f437414b..0254bcb997b 100644 --- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java +++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintSuggestionActivityTest.java @@ -30,7 +30,6 @@ import android.widget.Button; import com.android.settings.R; import com.android.settings.TestConfig; -import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowEventLogWriter; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; @@ -64,12 +63,9 @@ public class FingerprintSuggestionActivityTest { private ActivityController mController; - private FakeFeatureFactory mFactory; - @Before public void setUp() { MockitoAnnotations.initMocks(this); - mFactory = FakeFeatureFactory.setupForTest(); final Intent intent = new Intent(); mController = Robolectric.buildActivity(FingerprintSuggestionActivity.class, intent); diff --git a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java index c786608386a..c3899e9b01d 100644 --- a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensorTest.java @@ -27,7 +27,6 @@ import android.widget.Button; import com.android.settings.R; import com.android.settings.TestConfig; import com.android.settings.password.ChooseLockSettingsHelper; -import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowEventLogWriter; @@ -62,13 +61,10 @@ public class SetupFingerprintEnrollFindSensorTest { private SetupFingerprintEnrollFindSensor mActivity; - private FakeFeatureFactory mFactory; - @Before public void setUp() { MockitoAnnotations.initMocks(this); ShadowUtils.setFingerprintManager(mFingerprintManager); - mFactory = FakeFeatureFactory.setupForTest(); } private void createActivity(Intent intent) { diff --git a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java index f5b0c8a9ad0..2d98bf44511 100644 --- a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java @@ -35,7 +35,6 @@ import com.android.settings.fingerprint.SetupFingerprintEnrollIntroductionTest import com.android.settings.password.SetupChooseLockGeneric.SetupChooseLockGenericFragment; import com.android.settings.password.SetupSkipDialog; import com.android.settings.password.StorageManagerWrapper; -import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowEventLogWriter; import com.android.settings.testutils.shadow.ShadowFingerprintManager; @@ -73,7 +72,6 @@ public class SetupFingerprintEnrollIntroductionTest { @Mock private UserInfo mUserInfo; - private FakeFeatureFactory mFactory; private ActivityController mController; @@ -85,8 +83,6 @@ public class SetupFingerprintEnrollIntroductionTest { .setSystemFeature(PackageManager.FEATURE_FINGERPRINT, true); ShadowFingerprintManager.addToServiceMap(); - mFactory = FakeFeatureFactory.setupForTest(); - final Intent intent = new Intent(); mController = Robolectric.buildActivity(SetupFingerprintEnrollIntroduction.class, intent); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java index 3e33823b82c..38391c9350b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java @@ -24,6 +24,7 @@ import android.os.Build; import android.util.Pair; import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.fuelgauge.anomaly.action.StopAndBackgroundCheckAction; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; @@ -31,7 +32,6 @@ import com.android.settings.fuelgauge.anomaly.action.ForceStopAction; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.testutils.shadow.ShadowKeyValueListParserWrapperImpl; import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java index 737c16d5001..1ee52ca463c 100644 --- a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java +++ b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java @@ -20,7 +20,6 @@ import android.content.Context; import android.view.View; import android.widget.TextView; import com.android.settings.TestConfig; -import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment; @@ -28,7 +27,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; @@ -41,11 +39,6 @@ public class LocaleListEditorTest { private LocaleListEditor mLocaleListEditor; - @Mock - private Context mContext; - - private FakeFeatureFactory mFactory; - @Before public void setUp() { mLocaleListEditor = new LocaleListEditor(); @@ -55,13 +48,11 @@ public class LocaleListEditorTest { RuntimeEnvironment.application.getSystemService(Context.RESTRICTIONS_SERVICE)); ReflectionHelpers.setField(mLocaleListEditor, "mUserManager", RuntimeEnvironment.application.getSystemService(Context.USER_SERVICE)); - mFactory = FakeFeatureFactory.setupForTest(); } @Test public void testDisallowConfigLocale_unrestrict() { ReflectionHelpers.setField(mLocaleListEditor, "mIsUiRestricted", true); - mLocaleListEditor.onAttach(mContext); mLocaleListEditor.onResume(); Assert.assertEquals(View.GONE, mLocaleListEditor.getEmptyTextView().getVisibility()); } @@ -69,7 +60,6 @@ public class LocaleListEditorTest { @Test public void testDisallowConfigLocale_restrict() { ReflectionHelpers.setField(mLocaleListEditor, "mIsUiRestricted", false); - mLocaleListEditor.onAttach(mContext); mLocaleListEditor.onResume(); Assert.assertEquals(View.VISIBLE, mLocaleListEditor.getEmptyTextView().getVisibility()); } diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index b4592b8c7f2..fb2b62e7413 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -24,6 +24,7 @@ import android.content.Context; import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.connecteddevice.SmsMirroringFeatureProvider; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.datausage.DataPlanFeatureProvider; @@ -38,7 +39,6 @@ import com.android.settings.search.SearchFeatureProvider; import com.android.settings.security.SecurityFeatureProvider; import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.mockito.Answers; diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java index 9caf09f3e8d..dcced4e40d0 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowEventLogWriter.java @@ -19,7 +19,7 @@ package com.android.settings.testutils.shadow; import android.content.Context; -import com.android.settingslib.core.instrumentation.EventLogWriter; +import com.android.settings.core.instrumentation.EventLogWriter; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; diff --git a/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java b/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java index 59a08ae8801..e44be0e8781 100644 --- a/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java +++ b/tests/robotests/src/com/android/settings/webview/WebViewAppPickerTest.java @@ -44,10 +44,10 @@ import android.os.UserManager; import com.android.settings.TestConfig; import com.android.settings.applications.defaultapps.DefaultAppInfo; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.widget.RadioButtonPreference; import com.android.settings.wrapper.UserPackageWrapper; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.wrapper.PackageManagerWrapper; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java index 84549a62388..63f89e62c5c 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java @@ -22,9 +22,9 @@ import android.net.wifi.WifiManager; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.widget.SwitchWidgetController; import com.android.settings.wrapper.ConnectivityManagerWrapper; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java index 82569c74054..1708e364e0f 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiMasterSwitchPreferenceControllerTest.java @@ -30,10 +30,10 @@ import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.PreferenceScreen; import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.widget.MasterSwitchPreference; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index ca2cac09257..4f774357612 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -65,6 +65,7 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.TestConfig; import com.android.settings.applications.LayoutPreference; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowBidiFormatter; import com.android.settings.testutils.shadow.ShadowDevicePolicyManagerWrapper; @@ -75,7 +76,6 @@ import com.android.settings.widget.ActionButtonPreferenceTest; import com.android.settings.widget.EntityHeaderController; import com.android.settings.wifi.WifiDetailPreference; import com.android.settings.wrapper.ConnectivityManagerWrapper; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.wifi.AccessPoint; From 4b796e5d2ea23d45f67bd46590eddcac8b3148b4 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Sun, 21 Jan 2018 02:04:45 -0800 Subject: [PATCH 08/13] Add Settings support for Bluetooth Multi-A2DP and Multi-HFP When there are multiple connected A2DP/HFP devices, if a connected device's name is clicked on, that device will be chosen as Active - i.e., it will be the device chosen for audio out / phone call. Test: Manual: multiple connected A2DP devices, and selecting each as the Active Device. Bug: 64767509 Change-Id: Iee76286110ed87703d6a968f50273c22cd7c7086 --- .../bluetooth/BluetoothDeviceUpdater.java | 53 +++++++++++++++++++ .../bluetooth/BluetoothSummaryUpdater.java | 4 ++ .../DeviceListPreferenceFragment.java | 3 ++ 3 files changed, 60 insertions(+) diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java index 127730ba39a..2862083115d 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java @@ -27,14 +27,19 @@ import com.android.settings.SettingsActivity; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.widget.GearPreference; +import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothDeviceFilter; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.HeadsetProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Objects; + +import android.util.Log; /** * Update the bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} using @@ -45,6 +50,7 @@ import java.util.Map; * whether the {@link CachedBluetoothDevice} is relevant. */ public abstract class BluetoothDeviceUpdater implements BluetoothCallback { + private static final String TAG = "BluetoothDeviceUpdater"; private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY = "persist.bluetooth.showdeviceswithoutnames"; @@ -55,6 +61,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback { private final boolean mShowDeviceWithoutNames; private DashboardFragment mFragment; + private Preference.OnPreferenceClickListener mDevicePreferenceClickListener = null; @VisibleForTesting final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> { @@ -73,6 +80,38 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback { }; + private class PreferenceClickListener implements + Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + final CachedBluetoothDevice device = + ((BluetoothDevicePreference) preference).getBluetoothDevice(); + if (device == null) { + return false; + } + + // Set the device as active per profile only if the device supports that profile + // TODO: The active device selector location might change in the future + Log.i(TAG, "OnPreferenceClickListener: device=" + device); + boolean result = false; + A2dpProfile a2dpProfile = mLocalManager.getProfileManager().getA2dpProfile(); + if ((a2dpProfile != null) && device.isConnectedProfile(a2dpProfile)) { + if (a2dpProfile.setActiveDevice(device.getDevice())) { + Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + device); + result = true; + } + } + HeadsetProfile headsetProfile = mLocalManager.getProfileManager().getHeadsetProfile(); + if ((headsetProfile != null) && device.isConnectedProfile(headsetProfile)) { + if (headsetProfile.setActiveDevice(device.getDevice())) { + Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + device); + result = true; + } + } + return result; + } + } + public BluetoothDeviceUpdater(DashboardFragment fragment, DevicePreferenceCallback devicePreferenceCallback) { this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(fragment.getContext())); @@ -87,6 +126,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback { BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false); mPreferenceMap = new HashMap<>(); mLocalManager = localManager; + mDevicePreferenceClickListener = new PreferenceClickListener(); } /** @@ -141,6 +181,18 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback { @Override public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {} + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { + Collection cachedDevices = + mLocalManager.getCachedDeviceManager().getCachedDevicesCopy(); + // TODO: The state update of the Cached Bluetooth Devices should be + // moved to the device manager: b/72316092 + for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) { + boolean isActive = Objects.equals(cachedBluetoothDevice, activeDevice); + cachedBluetoothDevice.setActiveDevice(isActive, bluetoothProfile); + } + } + /** * Set the context to generate the {@link Preference}, so it could get the correct theme. */ @@ -176,6 +228,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback { new BluetoothDevicePreference(mPrefContext, cachedDevice, mShowDeviceWithoutNames); btPreference.setOnGearClickListener(mDeviceProfilesListener); + btPreference.setOnPreferenceClickListener(mDevicePreferenceClickListener); mPreferenceMap.put(device, btPreference); mDevicePreferenceCallback.onDeviceAdded(btPreference); } diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java index 662cd701de2..43d25e7b49d 100644 --- a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java +++ b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java @@ -76,6 +76,10 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { } + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { + } + @Override public void register(boolean listening) { if (mBluetoothAdapter == null) { diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java index 0a90edc1dbf..207a4b0643e 100644 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java @@ -277,6 +277,9 @@ public abstract class DeviceListPreferenceFragment extends public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { } + @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { } + /** * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices */ From 08c8820b290ee6d53bfbe9d4209961c416ad7fc9 Mon Sep 17 00:00:00 2001 From: Carlos Valdivia Date: Sun, 21 Jan 2018 18:57:28 -0800 Subject: [PATCH 09/13] AR/FR: Tests using stubbed values. Also a simplifying change to MasterClear. Test: Standard robotests Bug: 63937032 Change-Id: I54fe60c2e38d938148f89d043bf8a7f7698edc42 --- src/com/android/settings/MasterClear.java | 11 ++- .../com/android/settings/MasterClearTest.java | 82 ++++++++++++++++++- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java index 3cc722bbffa..dd7ef8093f7 100644 --- a/src/com/android/settings/MasterClear.java +++ b/src/com/android/settings/MasterClear.java @@ -76,7 +76,7 @@ public class MasterClear extends InstrumentedPreferenceFragment { private static final String TAG = "MasterClear"; private static final int KEYGUARD_REQUEST = 55; - private static final int CREDENTIAL_CONFIRM_REQUEST = 56; + @VisibleForTesting static final int CREDENTIAL_CONFIRM_REQUEST = 56; static final String ERASE_EXTERNAL_EXTRA = "erase_sd"; static final String ERASE_ESIMS_EXTRA = "erase_esim"; @@ -157,9 +157,12 @@ public class MasterClear extends InstrumentedPreferenceFragment { .setAction("android.accounts.action.PRE_FACTORY_RESET"); // Check to make sure that the intent is supported. final PackageManager pm = context.getPackageManager(); - final List resolutions = - pm.queryIntentActivities(requestAccountConfirmation, 0); - if (resolutions != null && resolutions.size() > 0) { + final ResolveInfo resolution = pm.resolveActivity(requestAccountConfirmation, 0); + if (resolution != null + && resolution.activityInfo != null + && packageName.equals(resolution.activityInfo.packageName)) { + // Note that we need to check the packagename to make sure that an Activity resolver + // wasn't returned. getActivity().startActivityForResult( requestAccountConfirmation, CREDENTIAL_CONFIRM_REQUEST); return true; diff --git a/tests/robotests/src/com/android/settings/MasterClearTest.java b/tests/robotests/src/com/android/settings/MasterClearTest.java index 361bc8f319d..26cfc3a7ae0 100644 --- a/tests/robotests/src/com/android/settings/MasterClearTest.java +++ b/tests/robotests/src/com/android/settings/MasterClearTest.java @@ -19,16 +19,24 @@ package com.android.settings; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import android.accounts.Account; +import android.accounts.AccountManager; import android.app.Activity; import android.app.Fragment; import android.content.ComponentName; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.Bundle; import android.provider.Settings; import android.view.LayoutInflater; @@ -46,6 +54,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowAccountManager; import org.robolectric.shadows.ShadowActivity; @RunWith(SettingsRobolectricTestRunner.class) @@ -55,6 +64,9 @@ import org.robolectric.shadows.ShadowActivity; shadows = {ShadowUtils.class} ) public class MasterClearTest { + private static final String TEST_ACCOUNT_TYPE = "android.test.account.type"; + private static final String TEST_CONFIRMATION_PACKAGE = "android.test.confirmation.pkg"; + private static final String TEST_ACCOUNT_NAME = "test@example.com"; @Mock private MasterClear mMasterClear; @@ -62,7 +74,18 @@ public class MasterClearTest { private ScrollView mScrollView; @Mock private LinearLayout mLinearLayout; + + @Mock + private PackageManager mPackageManager; + + @Mock + private AccountManager mAccountManager; + + @Mock + private Activity mMockActivity; + private ShadowActivity mShadowActivity; + private ShadowAccountManager mShadowAccountManager; private Activity mActivity; private View mContentView; @@ -86,6 +109,7 @@ public class MasterClearTest { mMasterClear = spy(new MasterClear()); mActivity = Robolectric.setupActivity(Activity.class); mShadowActivity = shadowOf(mActivity); + // mShadowAccountManager = shadowOf(AccountManager.get(mActivity)); mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null); // Make scrollView only have one child @@ -162,9 +186,61 @@ public class MasterClearTest { @Test public void testTryShowAccountConfirmation_unsupported() { - doReturn(mActivity).when(mMasterClear).getActivity(); - /* Using the default resources, account confirmation shouldn't trigger */ - assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse(); + when(mMasterClear.getActivity()).thenReturn(mActivity); + /* Using the default resources, account confirmation shouldn't trigger */ + assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse(); + } + + @Test + public void testTryShowAccountConfirmation_no_relevant_accounts() { + when(mMasterClear.getActivity()).thenReturn(mMockActivity); + when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); + when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE); + + Account[] accounts = new Account[0]; + when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); + when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); + assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse(); + } + + @Test + public void testTryShowAccountConfirmation_unresolved() { + when(mMasterClear.getActivity()).thenReturn(mMockActivity); + when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); + when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE); + Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) }; + when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); + when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); + // The package manager should not resolve the confirmation intent targeting the non-existent + // confirmation package. + when(mMockActivity.getPackageManager()).thenReturn(mPackageManager); + assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse(); + } + + @Test + public void testTryShowAccountConfirmation_ok() { + when(mMasterClear.getActivity()).thenReturn(mMockActivity); + // Only try to show account confirmation if the appropriate resource overlays are available. + when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE); + when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE); + // Add accounts to trigger the search for a resolving intent. + Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) }; + when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager); + when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts); + // The package manager should not resolve the confirmation intent targeting the non-existent + // confirmation package. + when(mMockActivity.getPackageManager()).thenReturn(mPackageManager); + + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.packageName = TEST_CONFIRMATION_PACKAGE; + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = activityInfo; + when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo); + + // Finally mock out the startActivityForResultCall + doNothing().when(mMockActivity).startActivityForResult(any(), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST)); + + assertThat(mMasterClear.tryShowAccountConfirmation()).isTrue(); } private void initScrollView(int height, int scrollY, int childBottom) { From d506894f5d16b396936ec4eac14f91e6714ea6d4 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 19 Jan 2018 09:41:58 -0800 Subject: [PATCH 10/13] Use SoftAp API to get number of connected device Create unit test because robolectric doesn't have the new API Bug: 68058038 Test: SettingsUnitTest Change-Id: I9fa27d51c4d270b2fb92db7dfc3955e33d4a3f4a --- .../WifiTetherPreferenceController.java | 46 +++++++++++++++++- .../wifi/tether/WifiTetherSoftApManager.java | 47 +++++++++++++++++++ .../WifiTetherPreferenceControllerTest.java | 28 +++++++++-- 3 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java index fa10607b342..11f1f5977d6 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java @@ -24,6 +24,7 @@ import android.net.ConnectivityManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.provider.Settings; +import android.support.annotation.VisibleForTesting; import android.support.v7.preference.PreferenceScreen; import android.text.BidiFormatter; @@ -51,7 +52,11 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController private final WifiManager mWifiManager; private final Lifecycle mLifecycle; private WifiTetherSwitchBarController mSwitchController; - private MasterSwitchPreference mPreference; + private int mSoftApState; + @VisibleForTesting + MasterSwitchPreference mPreference; + @VisibleForTesting + WifiTetherSoftApManager mWifiTetherSoftApManager; static { WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); @@ -60,6 +65,12 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController } public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) { + this(context, lifecycle, true /* initSoftApManager */); + } + + @VisibleForTesting + WifiTetherPreferenceController(Context context, Lifecycle lifecycle, + boolean initSoftApManager) { super(context); mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -69,6 +80,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController if (lifecycle != null) { lifecycle.addObserver(this); } + if (initSoftApManager) { + initWifiTetherSoftApManager(); + } } @Override @@ -101,6 +115,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController if (mPreference != null) { mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER); clearSummaryForAirplaneMode(); + if (mWifiTetherSoftApManager != null) { + mWifiTetherSoftApManager.registerSoftApCallback(); + } } } @@ -108,9 +125,36 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController public void onStop() { if (mPreference != null) { mContext.unregisterReceiver(mReceiver); + if (mWifiTetherSoftApManager != null) { + mWifiTetherSoftApManager.unRegisterSoftApCallback(); + } } } + @VisibleForTesting + void initWifiTetherSoftApManager() { + // This manager only handles the number of connected devices, other parts are handled by + // normal BroadcastReceiver in this controller + mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager, + new WifiTetherSoftApManager.WifiTetherSoftApCallback() { + @Override + public void onStateChanged(int state, int failureReason) { + mSoftApState = state; + } + + @Override + public void onNumClientsChanged(int numClients) { + if (mPreference != null + && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) { + // Only show the number of clients when state is on + mPreference.setSummary(mContext.getResources().getQuantityString( + R.plurals.wifi_tether_connected_summary, numClients, + numClients)); + } + } + }); + } + // // Everything below is copied from WifiApEnabler // diff --git a/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java b/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java new file mode 100644 index 00000000000..77a44b08ee8 --- /dev/null +++ b/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java @@ -0,0 +1,47 @@ +package com.android.settings.wifi.tether; + +import android.net.wifi.WifiManager; +import android.os.Handler; + +/** + * Wrapper for {@link android.net.wifi.WifiManager.SoftApCallback} to pass the robo test + */ +public class WifiTetherSoftApManager { + + private WifiManager mWifiManager; + private WifiTetherSoftApCallback mWifiTetherSoftApCallback; + + private WifiManager.SoftApCallback mSoftApCallback = new WifiManager.SoftApCallback() { + @Override + public void onStateChanged(int state, int failureReason) { + mWifiTetherSoftApCallback.onStateChanged(state, failureReason); + } + + @Override + public void onNumClientsChanged(int numClients) { + mWifiTetherSoftApCallback.onNumClientsChanged(numClients); + } + }; + private Handler mHandler; + + WifiTetherSoftApManager(WifiManager wifiManager, + WifiTetherSoftApCallback wifiTetherSoftApCallback) { + mWifiManager = wifiManager; + mWifiTetherSoftApCallback = wifiTetherSoftApCallback; + mHandler = new Handler(); + } + + public void registerSoftApCallback() { + mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler); + } + + public void unRegisterSoftApCallback() { + mWifiManager.unregisterSoftApCallback(mSoftApCallback); + } + + public interface WifiTetherSoftApCallback { + void onStateChanged(int state, int failureReason); + + void onNumClientsChanged(int numClients); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java index 00d9585e4f2..dca69748a95 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java @@ -18,11 +18,15 @@ package com.android.settings.wifi.tether; import static android.arch.lifecycle.Lifecycle.Event.ON_START; import static android.arch.lifecycle.Lifecycle.Event.ON_STOP; + import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -65,6 +69,7 @@ import java.util.ArrayList; shadows = { WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class, WifiTetherPreferenceControllerTest.ShadowWifiTetherSwitchBarController.class, + WifiTetherPreferenceControllerTest.ShadowWifiTetherSoftApManager.class }) public class WifiTetherPreferenceControllerTest { @@ -94,8 +99,9 @@ public class WifiTetherPreferenceControllerTest { when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager); when(mScreen.findPreference(anyString())).thenReturn(mPreference); - when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {"1", "2"}); - mController = new WifiTetherPreferenceController(mContext, mLifecycle); + when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"}); + mController = new WifiTetherPreferenceController(mContext, mLifecycle, + false /* initSoftApManager */); } @After @@ -105,8 +111,9 @@ public class WifiTetherPreferenceControllerTest { @Test public void isAvailable_noTetherRegex_shouldReturnFalse() { - when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {}); - mController = new WifiTetherPreferenceController(mContext, mLifecycle); + when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{}); + mController = new WifiTetherPreferenceController(mContext, mLifecycle, + false /* initSoftApManager */); assertThat(mController.isAvailable()).isFalse(); } @@ -244,6 +251,19 @@ public class WifiTetherPreferenceControllerTest { } } + @Implements(WifiTetherSoftApManager.class) + public static final class ShadowWifiTetherSoftApManager { + @Implementation + public void registerSoftApCallback() { + // do nothing + } + + @Implementation + public void unRegisterSoftApCallback() { + // do nothing + } + } + @Implements(WifiTetherSwitchBarController.class) public static final class ShadowWifiTetherSwitchBarController { From b7270a5aeb57402c7c0ddb7baba314eab50e6049 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Mon, 22 Jan 2018 14:07:52 -0800 Subject: [PATCH 11/13] conditionally add FLAG_ACTIVITY_NEW_TASK when starting page Subsettings must be started from Activity context, apparently. Add a flag when it's not started from Activity context. This might not be catching all cases but should at lesat cover most of them. Change-Id: I8157ec0cb3d032bb44a0e7dec36637906fc8f1a0 Fixes: 72314527 Test: monkey --- src/com/android/settings/Utils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 1c674b68f15..f45ac5edb25 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -515,7 +515,8 @@ public final class Utils extends com.android.settingslib.Utils { Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut, int metricsCategory) { startWithFragment(context, fragmentName, args, resultTo, resultRequestCode, - titleResPackageName, titleResId, title, isShortcut, metricsCategory, 0); + titleResPackageName, titleResId, title, isShortcut, metricsCategory, + Intent.FLAG_ACTIVITY_NEW_TASK); } From 29002aacb042aef6f839215054c08a32e9b3a0f9 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 19 Jan 2018 17:32:23 -0800 Subject: [PATCH 12/13] Revamp the battery detail page for each app 1. Move preferences to new postion 2. Change background activity pref from SwitchPreference to Preference and update the controller Bug: 72227981 Test: RunSettingsRoboTests Change-Id: Ib18ac7523c24d19754c37ab8ac527095d9ced49c --- res/values/strings.xml | 7 +- res/xml/power_usage_detail.xml | 32 ++++----- .../fuelgauge/AdvancedPowerUsageDetail.java | 2 +- ...ackgroundActivityPreferenceController.java | 52 +++++++------- ...roundActivityPreferenceControllerTest.java | 70 +++++++------------ 5 files changed, 77 insertions(+), 86 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 164f6d5de29..2a14ef5c906 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4719,7 +4719,7 @@ %1$s to charge - Background activity + Restricted Allow the app to run in the background @@ -7602,6 +7602,11 @@ No + + Yes + + No + Require PIN? diff --git a/res/xml/power_usage_detail.xml b/res/xml/power_usage_detail.xml index acd63676e96..acc62b6cb8d 100644 --- a/res/xml/power_usage_detail.xml +++ b/res/xml/power_usage_detail.xml @@ -27,6 +27,22 @@ android:key="action_buttons" android:order="-9999"/> + + + + + + + + - - - - - - - - \ No newline at end of file diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 819846adea0..de027a3a199 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -374,7 +374,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements @Override public void onLimitBackgroundActivity() { - mBackgroundActivityPreferenceController.setUnchecked( + mBackgroundActivityPreferenceController.setRestricted( findPreference(mBackgroundActivityPreferenceController.getPreferenceKey())); } } diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java index 8286774226a..01e41825074 100644 --- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java @@ -43,12 +43,12 @@ import com.android.settingslib.fuelgauge.PowerWhitelistBackend; * Controller to control whether an app can run in the background */ public class BackgroundActivityPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { + implements PreferenceControllerMixin { private static final String TAG = "BgActivityPrefContr"; - private static final String KEY_BACKGROUND_ACTIVITY = "background_activity"; + @VisibleForTesting + static final String KEY_BACKGROUND_ACTIVITY = "background_activity"; - private final PackageManager mPackageManager; private final AppOpsManager mAppOpsManager; private final UserManager mUserManager; private final int mUid; @@ -70,7 +70,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo int uid, String packageName, PowerWhitelistBackend backend) { super(context); mPowerWhitelistBackend = backend; - mPackageManager = context.getPackageManager(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mDpm = new DevicePolicyManagerWrapper( (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE)); @@ -86,11 +85,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo final int mode = mAppOpsManager .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage); - // Set checked or not before we may set it disabled - if (mode != AppOpsManager.MODE_ERRORED) { - final boolean checked = whitelisted || mode != AppOpsManager.MODE_IGNORED; - ((SwitchPreference) preference).setChecked(checked); - } if (whitelisted || mode == AppOpsManager.MODE_ERRORED || Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) { preference.setEnabled(false); @@ -109,9 +103,8 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo * Called from the warning dialog, if the user decides to go ahead and disable background * activity for this package */ - public void setUnchecked(Preference preference) { + public void setRestricted(Preference preference) { mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED); - ((SwitchPreference) preference).setChecked(false); updateSummary(preference); } @@ -121,17 +114,21 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean switchOn = (Boolean) newValue; - if (!switchOn) { - final WarningDialogFragment dialogFragment = new WarningDialogFragment(); - dialogFragment.setTargetFragment(mFragment, 0); - dialogFragment.show(mFragment.getFragmentManager(), TAG); - return false; + public boolean handlePreferenceTreeClick(Preference preference) { + if (KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) { + final int mode = mAppOpsManager + .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); + final boolean restricted = mode == AppOpsManager.MODE_IGNORED; + if (!restricted) { + showDialog(); + return false; + } + mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED); + updateSummary(preference); + return true; } - mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED); - updateSummary(preference); - return true; + + return false; } @VisibleForTesting @@ -146,12 +143,19 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo if (mode == AppOpsManager.MODE_ERRORED) { preference.setSummary(R.string.background_activity_summary_disabled); } else { - final boolean checked = mode != AppOpsManager.MODE_IGNORED; - preference.setSummary(checked ? R.string.background_activity_summary_on - : R.string.background_activity_summary_off); + final boolean restricted = mode == AppOpsManager.MODE_IGNORED; + preference.setSummary(restricted ? R.string.restricted_true_label + : R.string.restricted_false_label); } } + @VisibleForTesting + void showDialog() { + final WarningDialogFragment dialogFragment = new WarningDialogFragment(); + dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */); + dialogFragment.show(mFragment.getFragmentManager(), TAG); + } + interface WarningConfirmationListener { void onLimitBackgroundActivity(); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java index 0c9e394ffdd..30fdccb23ec 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -38,6 +39,7 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.UserManager; import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; import android.widget.Button; import com.android.settings.R; @@ -92,12 +94,12 @@ public class BackgroundActivityPreferenceControllerTest { private DevicePolicyManager mDevicePolicyManager; @Mock private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper; - @Mock + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private AdvancedPowerUsageDetail mFragment; @Mock private PowerWhitelistBackend mPowerWhitelistBackend; private BackgroundActivityPreferenceController mController; - private SwitchPreference mPreference; + private Preference mPreference; private Context mShadowContext; private BatteryUtils mBatteryUtils; @@ -125,7 +127,8 @@ public class BackgroundActivityPreferenceControllerTest { mBatteryUtils = spy(new BatteryUtils(mShadowContext)); doNothing().when(mBatteryUtils).setForceAppStandby(anyInt(), anyString(), anyInt()); - mPreference = new SwitchPreference(mShadowContext); + mPreference = new Preference(mShadowContext); + mPreference.setKey(BackgroundActivityPreferenceController.KEY_BACKGROUND_ACTIVITY); mController = spy(new BackgroundActivityPreferenceController( mContext, mFragment, UID_LOW_SDK, LOW_SDK_PACKAGE, mPowerWhitelistBackend)); mController.mDpm = mDevicePolicyManagerWrapper; @@ -133,49 +136,33 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testOnPreferenceChange_TurnOnCheck_MethodInvoked() { - mController.onPreferenceChange(mPreference, true); + public void testHandlePreferenceTreeClick_restrictApp_showDialog() { + doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(anyInt(), + anyInt(), anyString()); + + mController.handlePreferenceTreeClick(mPreference); + + verify(mController).showDialog(); + } + + @Test + public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() { + doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(anyInt(), + anyInt(), anyString()); + + mController.handlePreferenceTreeClick(mPreference); verify(mBatteryUtils).setForceAppStandby(UID_LOW_SDK, LOW_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED); - assertThat(mPreference.getSummary()) - .isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on)); } @Test - public void testOnPreferenceChange_TurnOnCheckHighSDK_MethodInvoked() { - mController = new BackgroundActivityPreferenceController(mContext, mFragment, UID_HIGH_SDK, - HIGH_SDK_PACKAGE, mPowerWhitelistBackend); - mController.mBatteryUtils = mBatteryUtils; - - mController.onPreferenceChange(mPreference, true); - - verify(mBatteryUtils).setForceAppStandby(UID_HIGH_SDK, HIGH_SDK_PACKAGE, - AppOpsManager.MODE_ALLOWED); - assertThat(mPreference.getSummary()) - .isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on)); - } - - @Test - public void testUpdateState_CheckOn_SetCheckedTrue() { + public void testUpdateState_noError_setEnabled() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED); mController.updateState(mPreference); - assertThat(mPreference.isChecked()).isTrue(); - assertThat(mPreference.isEnabled()).isTrue(); - verify(mController).updateSummary(mPreference); - } - - @Test - public void testUpdateState_CheckOff_SetCheckedFalse() { - when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, - LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); assertThat(mPreference.isEnabled()).isTrue(); verify(mController).updateSummary(mPreference); } @@ -184,7 +171,6 @@ public class BackgroundActivityPreferenceControllerTest { public void testUpdateState_whitelisted() { when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(true); mController.updateState(mPreference); - assertThat(mPreference.isChecked()).isTrue(); assertThat(mPreference.isEnabled()).isFalse(); assertThat(mPreference.getSummary()).isEqualTo( mShadowContext.getText(R.string.background_activity_summary_whitelisted)); @@ -202,27 +188,23 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testUpdateSummary_modeDefault_showSummaryOn() { + public void testUpdateSummary_modeDefault_showNotRestricted() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_DEFAULT); - final CharSequence expectedSummary = mShadowContext.getText( - R.string.background_activity_summary_on); mController.updateSummary(mPreference); - assertThat(mPreference.getSummary()).isEqualTo(expectedSummary); + assertThat(mPreference.getSummary()).isEqualTo("No"); } @Test - public void testUpdateSummary_modeIgnored_showSummaryOff() { + public void testUpdateSummary_modeIgnored_showRestricted() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED); - final CharSequence expectedSummary = mShadowContext.getText( - R.string.background_activity_summary_off); mController.updateSummary(mPreference); - assertThat(mPreference.getSummary()).isEqualTo(expectedSummary); + assertThat(mPreference.getSummary()).isEqualTo("Yes"); } @Test From c8a2b040a177d4016876d232a87e107d6ef0e12c Mon Sep 17 00:00:00 2001 From: Qingxi Li Date: Thu, 11 Jan 2018 11:35:07 -0800 Subject: [PATCH 13/13] Add checkbox to let user wipe eSIM data together with FDR This CL add a check box for eSIM enabled devices to reset eSIM data during factory reset of the phone. Bug: 67500470 Test: make RunSettingsRoboTests Change-Id: I5a81d43f23ae55f8549a5b807fdf41f36c9d3acd --- res/layout/master_clear.xml | 4 ++ res/layout/reset_esim_checkbox.xml | 12 ++++-- res/values/strings.xml | 2 +- src/com/android/settings/MasterClear.java | 38 ++++++++++++++++--- .../android/settings/ResetNetworkConfirm.java | 3 +- .../wrapper/RecoverySystemWrapper.java | 5 +-- .../com/android/settings/MasterClearTest.java | 27 +++++++++++++ .../settings/ResetNetworkConfirmTest.java | 6 +-- 8 files changed, 78 insertions(+), 19 deletions(-) diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml index f81c7e81c10..d328478761a 100644 --- a/res/layout/master_clear.xml +++ b/res/layout/master_clear.xml @@ -115,6 +115,10 @@ android:text="@string/erase_external_storage_description" /> +