From 4d67f4c8471a468e39e104fd94e0295876fded2f Mon Sep 17 00:00:00 2001 From: ykhung Date: Mon, 30 Aug 2021 17:07:53 +0800 Subject: [PATCH 01/12] Hide Camera background running time in the battery usage screen hide camera background running time in the battery usage screen to avoid users confuse about it, since it will standby in the backgound to sspeed up launching time, not always running in the background Bug: 196744910 Test: make SettingsRoboTests Change-Id: Ia391d661bca44435fc5a68b1c86e5ca32dd0fcfe Merged-In: Ia391d661bca44435fc5a68b1c86e5ca32dd0fcfe (cherry picked from commit 4e3f6921644889c0ea524c44d1052f73aefe515e) --- .../settings/fuelgauge/ConvertUtils.java | 15 ++++++- .../fuelgauge/PowerUsageFeatureProvider.java | 6 +++ .../PowerUsageFeatureProviderImpl.java | 7 ++++ .../settings/fuelgauge/ConvertUtilsTest.java | 41 +++++++++++++++++-- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/fuelgauge/ConvertUtils.java b/src/com/android/settings/fuelgauge/ConvertUtils.java index 001e9e912a4..da846318b9b 100644 --- a/src/com/android/settings/fuelgauge/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/ConvertUtils.java @@ -24,6 +24,8 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.settings.overlay.FeatureFactory; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.text.SimpleDateFormat; @@ -288,7 +290,7 @@ public final class ConvertUtils { } insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap); if (purgeLowPercentageAndFakeData) { - purgeLowPercentageAndFakeData(resultMap); + purgeLowPercentageAndFakeData(context, resultMap); } return resultMap; } @@ -327,7 +329,12 @@ public final class ConvertUtils { // Removes low percentage data and fake usage data, which will be zero value. private static void purgeLowPercentageAndFakeData( + final Context context, final Map> indexedUsageMap) { + final List backgroundUsageTimeHideList = + FeatureFactory.getFactory(context) + .getPowerUsageFeatureProvider(context) + .getHideBackgroundUsageTimeList(context); for (List entries : indexedUsageMap.values()) { final Iterator iterator = entries.iterator(); while (iterator.hasNext()) { @@ -336,6 +343,12 @@ public final class ConvertUtils { || FAKE_PACKAGE_NAME.equals(entry.getPackageName())) { iterator.remove(); } + final String packageName = entry.getPackageName(); + if (packageName != null + && !backgroundUsageTimeHideList.isEmpty() + && backgroundUsageTimeHideList.contains(packageName)) { + entry.mBackgroundUsageTimeInMs = 0; + } } } } diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index 61abe6b1fbf..eff538fa644 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -23,6 +23,7 @@ import android.util.SparseIntArray; import com.android.internal.os.BatterySipper; import com.android.settingslib.fuelgauge.Estimate; +import java.util.List; import java.util.Map; /** @@ -141,4 +142,9 @@ public interface PowerUsageFeatureProvider { * Returns battery history data with corresponding timestamp key. */ Map> getBatteryHistory(Context context); + + /** + * Returns {@link List} for hidding applications background usage time. + */ + List getHideBackgroundUsageTimeList(Context context); } diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index 1dcdab08be5..7f1bef0ae23 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -26,6 +26,8 @@ import com.android.internal.os.BatterySipper; import com.android.internal.util.ArrayUtils; import com.android.settingslib.fuelgauge.Estimate; +import java.util.ArrayList; +import java.util.List; import java.util.Map; public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider { @@ -167,4 +169,9 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider public Map> getBatteryHistory(Context context) { return null; } + + @Override + public List getHideBackgroundUsageTimeList(Context context) { + return new ArrayList<>(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java index 0b1a1e31642..1b9e05b63b6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java @@ -50,8 +50,11 @@ import java.util.TimeZone; public final class ConvertUtilsTest { private Context mContext; - @Mock private BatteryUsageStats mBatteryUsageStats; - @Mock private BatteryEntry mockBatteryEntry; + + @Mock + private BatteryUsageStats mBatteryUsageStats; + @Mock + private BatteryEntry mockBatteryEntry; private FakeFeatureFactory mFeatureFactory; private PowerUsageFeatureProvider mPowerUsageFeatureProvider; @@ -301,7 +304,7 @@ public final class ConvertUtilsTest { final Map> purgedResultMap = ConvertUtils.getIndexedUsageMap( mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap, - /*purgeLowPercentageAndFakeData=*/ true); + /*purgeLowPercentageAndFakeData=*/ true); assertThat(purgedResultMap).hasSize(2); final List entryList = purgedResultMap.get(0); @@ -382,6 +385,38 @@ public final class ConvertUtilsTest { assertThat(ConvertUtils.sLocaleForHour).isEqualTo(new Locale("en_US")); } + @Test + public void testGetIndexedUsageMap_hideBackgroundUsageTime_returnsExpectedResult() { + final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L}; + final Map> batteryHistoryMap = new HashMap<>(); + final BatteryHistEntry fakeEntry = createBatteryHistEntry( + ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", 0, 0L, 0L, 0L); + // Adds the index = 0 data. + Map entryMap = new HashMap<>(); + entryMap.put(fakeEntry.getKey(), fakeEntry); + batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap); + // Adds the index = 1 data. + entryMap = new HashMap<>(); + entryMap.put(fakeEntry.getKey(), fakeEntry); + batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[1]), entryMap); + // Adds the index = 2 data. + entryMap = new HashMap<>(); + final BatteryHistEntry entry = createBatteryHistEntry( + "package3", "label3", 500, 5L, 3600000L, 7200000L); + entryMap.put(entry.getKey(), entry); + batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap); + when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeList(mContext)) + .thenReturn(Arrays.asList((CharSequence) "package3")); + + final Map> purgedResultMap = + ConvertUtils.getIndexedUsageMap( + mContext, /*timeSlotSize=*/ 1, batteryHistoryKeys, batteryHistoryMap, + /*purgeLowPercentageAndFakeData=*/ true); + + final BatteryDiffEntry resultEntry = purgedResultMap.get(0).get(0); + assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(0); + } + @Test public void getLocale_nullContext_returnDefaultLocale() { assertThat(ConvertUtils.getLocale(/*context=*/ null)) From 5549bf8cd03bcd5e71dbcf825301f5cbe0224d61 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Thu, 14 Oct 2021 15:06:33 +0000 Subject: [PATCH 02/12] Rephrase dialog message of clear storage dialog for security concern Bug: 193890833 Test: visual Change-Id: I8f0b066de710169ee8b922c44b6519ca21b9c7ef (cherry picked from commit 0c359da620498d536d81cb97d5ae48048201c226) --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index c4609fb1f5c..133b98392be 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4813,7 +4813,7 @@ Delete app data? - All this app\u2019s data will be deleted permanently. This includes all files, settings, accounts, databases, etc. + This app\u2019s data will be permanently deleted. This includes files, settings, databases, and other app data. OK From 9a0c28d911046e7cbfdfc8cce1fab94394588c51 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Thu, 14 Oct 2021 15:06:33 +0000 Subject: [PATCH 03/12] Rephrase dialog message of clear storage dialog for security concern Bug: 193890833 Test: visual Change-Id: I8f0b066de710169ee8b922c44b6519ca21b9c7ef (cherry picked from commit 0c359da620498d536d81cb97d5ae48048201c226) --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 802a28462e6..a31f66bd66d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4808,7 +4808,7 @@ Delete app data? - All this app\u2019s data will be deleted permanently. This includes all files, settings, accounts, databases, etc. + This app\u2019s data will be permanently deleted. This includes files, settings, databases, and other app data. OK From 881d7ec5ab1fd185ce050082045156947b09bbcb Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Thu, 14 Oct 2021 15:06:33 +0000 Subject: [PATCH 04/12] Rephrase dialog message of clear storage dialog for security concern Bug: 193890833 Test: visual Change-Id: I8f0b066de710169ee8b922c44b6519ca21b9c7ef (cherry picked from commit 0c359da620498d536d81cb97d5ae48048201c226) --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 811a44c7c56..8ea573817a3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4479,7 +4479,7 @@ Delete app data? - All this app\u2019s data will be deleted permanently. This includes all files, settings, accounts, databases, etc. + This app\u2019s data will be permanently deleted. This includes files, settings, databases, and other app data. OK From 6024ea553a5f662f4693bd203aedbbcce049a15e Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Thu, 14 Oct 2021 15:06:33 +0000 Subject: [PATCH 05/12] Rephrase dialog message of clear storage dialog for security concern Bug: 193890833 Test: visual Change-Id: I8f0b066de710169ee8b922c44b6519ca21b9c7ef (cherry picked from commit 0c359da620498d536d81cb97d5ae48048201c226) --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index d6d780f71d0..205fe306841 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4465,7 +4465,7 @@ Delete app data? - All this app\u2019s data will be deleted permanently. This includes all files, settings, accounts, databases, etc. + This app\u2019s data will be permanently deleted. This includes files, settings, databases, and other app data. OK From 7d22136fb086da3edff0664d8fe5f5e0b70fd77d Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Thu, 14 Oct 2021 15:06:33 +0000 Subject: [PATCH 06/12] Rephrase dialog message of clear storage dialog for security concern Bug: 193890833 Test: visual Change-Id: I8f0b066de710169ee8b922c44b6519ca21b9c7ef (cherry picked from commit 0c359da620498d536d81cb97d5ae48048201c226) --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index a8a2d73d163..7174438ba7a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4395,7 +4395,7 @@ Delete app data? - All this app\u2019s data will be deleted permanently. This includes all files, settings, accounts, databases, etc. + This app\u2019s data will be permanently deleted. This includes files, settings, databases, and other app data. OK From 7fd0ece6f4af76dfe693c73e0b3d4ecf1b6fbaac Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Thu, 14 Oct 2021 15:06:33 +0000 Subject: [PATCH 07/12] Rephrase dialog message of clear storage dialog for security concern Bug: 193890833 Test: visual Change-Id: I8f0b066de710169ee8b922c44b6519ca21b9c7ef (cherry picked from commit 0c359da620498d536d81cb97d5ae48048201c226) --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 64cbf901b0d..03e9a18066b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4047,7 +4047,7 @@ Delete app data? - All this app\u2019s data will be deleted permanently. This includes all files, settings, accounts, databases, etc. + This app\u2019s data will be permanently deleted. This includes files, settings, databases, and other app data. OK From e53d70babc944ca889078d4a9156b80bd1fe900b Mon Sep 17 00:00:00 2001 From: tom hsu Date: Tue, 2 Nov 2021 20:19:23 +0800 Subject: [PATCH 08/12] [Settings] Avoid crash from rotation screen. Bug: b/200822579 Test: Local test Change-Id: Ie25b2ab4284d47abdd5db23676d05b6d547b2a73 --- AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 8a69e04ff4b..d792cdc96ea 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -233,7 +233,8 @@ + android:launchMode="singleTask" + android:configChanges="orientation|screenSize|keyboardHidden"> From 7fb4cbf0053f84051b963f070b3449b0377771d0 Mon Sep 17 00:00:00 2001 From: "Wesley.CW Wang" Date: Tue, 2 Nov 2021 17:21:06 +0800 Subject: [PATCH 09/12] Adjust optimize page logging timing - Log the optimize mode only when leave this page and mode changed - Update mOptimizationMode at onResume to make sure the state is sync with framework Bug: 195306545 Test: make SettingsRoboTests Change-Id: Iab116220cd7d2b1bdb1c170c4b47016c763bf4fe --- .../fuelgauge/AdvancedPowerUsageDetail.java | 59 +++++++++++-------- .../AdvancedPowerUsageDetailTest.java | 39 +++++++++--- 2 files changed, 68 insertions(+), 30 deletions(-) diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 4ad4de74de5..6b024ed705c 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -270,6 +270,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements initHeader(); if (mEnableTriState) { + mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); initPreferenceForTriState(getContext()); final String packageName = mBatteryOptimizeUtils.getPackageName(); FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider() @@ -286,8 +287,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements public void onPause() { super.onPause(); if (mEnableTriState) { - Log.d(TAG, "Leave with mode: " + getSelectedPreference()); - mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference()); + final int selectedPreference = getSelectedPreference(); + + logMetricCategory(selectedPreference); + mBatteryOptimizeUtils.setAppUsageState(selectedPreference); + Log.d(TAG, "Leave with mode: " + selectedPreference); } } @@ -459,32 +463,42 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements updatePreferenceState(mUnrestrictedPreference, selectedKey); updatePreferenceState(mOptimizePreference, selectedKey); updatePreferenceState(mRestrictedPreference, selectedKey); - - // Logs metric. - int metricCategory = 0; - if (selectedKey.equals(mUnrestrictedPreference.getKey())) { - metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED; - } else if (selectedKey.equals(mOptimizePreference.getKey())) { - metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED; - } else if (selectedKey.equals(mRestrictedPreference.getKey())) { - metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_RESTRICTED; - } - if (metricCategory != 0) { - FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider() - .action( - getContext(), - metricCategory, - new Pair(ConvertUtils.METRIC_KEY_PACKAGE, - mBatteryOptimizeUtils.getPackageName()), - new Pair(ConvertUtils.METRIC_KEY_BATTERY_USAGE, - getArguments().getString(EXTRA_POWER_USAGE_PERCENT))); - } } private void updatePreferenceState(RadioButtonPreference preference, String selectedKey) { preference.setChecked(selectedKey.equals(preference.getKey())); } + private void logMetricCategory(int selectedKey) { + if (selectedKey == mOptimizationMode) { + return; + } + + int metricCategory = 0; + switch (selectedKey) { + case BatteryOptimizeUtils.MODE_UNRESTRICTED: + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED; + break; + case BatteryOptimizeUtils.MODE_OPTIMIZED: + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED; + break; + case BatteryOptimizeUtils.MODE_RESTRICTED: + metricCategory = SettingsEnums.ACTION_APP_BATTERY_USAGE_RESTRICTED; + break; + } + + if (metricCategory != 0) { + FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider() + .action( + getContext(), + metricCategory, + new Pair(ConvertUtils.METRIC_KEY_PACKAGE, + mBatteryOptimizeUtils.getPackageName()), + new Pair(ConvertUtils.METRIC_KEY_BATTERY_USAGE, + getArguments().getString(EXTRA_POWER_USAGE_PERCENT))); + } + } + private void onCreateForTriState(String packageName) { mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED); mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED); @@ -496,7 +510,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements mBatteryOptimizeUtils = new BatteryOptimizeUtils( getContext(), getArguments().getInt(EXTRA_UID), packageName); - mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); } private int getSelectedPreference() { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 3501726bc1a..7e975a08e25 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -792,14 +792,39 @@ public class AdvancedPowerUsageDetailTest { assertThat(mOptimizePreference.isChecked()).isTrue(); assertThat(mRestrictedPreference.isChecked()).isFalse(); assertThat(mUnrestrictedPreference.isChecked()).isFalse(); + } + + @Test + public void testOnPause_optimizationModeChanged_logPreference() { + final int mode = BatteryOptimizeUtils.MODE_RESTRICTED; + mFragment.mOptimizationMode = mode; + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode); + mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); + + mFragment.onRadioButtonClicked(mOptimizePreference); + mFragment.onPause(); + verify(mMetricsFeatureProvider) - .action( - mContext, - SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED, - (Pair[]) new Pair[] { - new Pair(ConvertUtils.METRIC_KEY_PACKAGE, null), - new Pair(ConvertUtils.METRIC_KEY_BATTERY_USAGE, "app label") - }); + .action( + mContext, + SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED, + (Pair[]) new Pair[] { + new Pair(ConvertUtils.METRIC_KEY_PACKAGE, null), + new Pair(ConvertUtils.METRIC_KEY_BATTERY_USAGE, "app label") + }); + } + + @Test + public void testOnPause_optimizationModeIsNotChanged_notInvokeLogging() { + final int mode = BatteryOptimizeUtils.MODE_OPTIMIZED; + mFragment.mOptimizationMode = mode; + when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode); + mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); + + mFragment.onRadioButtonClicked(mOptimizePreference); + mFragment.onPause(); + + verifyZeroInteractions(mMetricsFeatureProvider); } @Test From 9fc0f18181b5afe942a80b567a0375bdc5a16bc5 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Wed, 3 Nov 2021 12:18:39 +0800 Subject: [PATCH 10/12] Define a constant Sliceable#NO_RES for no resource cases Fix: 204733076 Test: build Change-Id: Ifa3b9db0e915a3ec056ad68a9a1862811594d423 --- .../BiometricSettingsAppPreferenceController.java | 2 +- ...etricSettingsKeyguardPreferenceController.java | 2 +- .../face/FaceSettingsPreferenceController.java | 2 +- ...tionTimeZoneDetectionPreferenceController.java | 2 +- ...EmergencyGestureSoundPreferenceController.java | 2 +- .../settings/flashlight/FlashlightSlice.java | 2 +- .../TelephonyTogglePreferenceController.java | 2 +- .../BubbleNotificationPreferenceController.java | 2 +- src/com/android/settings/slices/Sliceable.java | 15 +++++++++++---- .../network/ProviderModelSliceHelperTest.java | 2 +- .../slices/SpecialCaseSliceManagerTest.java | 2 +- 11 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/com/android/settings/biometrics/combination/BiometricSettingsAppPreferenceController.java b/src/com/android/settings/biometrics/combination/BiometricSettingsAppPreferenceController.java index 6351621d2e1..a46ae7a728c 100644 --- a/src/com/android/settings/biometrics/combination/BiometricSettingsAppPreferenceController.java +++ b/src/com/android/settings/biometrics/combination/BiometricSettingsAppPreferenceController.java @@ -95,6 +95,6 @@ public class BiometricSettingsAppPreferenceController extends TogglePreferenceCo @Override public int getSliceHighlightMenuRes() { // not needed since it's not sliceable - return 0; + return NO_RES; } } diff --git a/src/com/android/settings/biometrics/combination/BiometricSettingsKeyguardPreferenceController.java b/src/com/android/settings/biometrics/combination/BiometricSettingsKeyguardPreferenceController.java index 4101963cf49..2d2255805cc 100644 --- a/src/com/android/settings/biometrics/combination/BiometricSettingsKeyguardPreferenceController.java +++ b/src/com/android/settings/biometrics/combination/BiometricSettingsKeyguardPreferenceController.java @@ -77,6 +77,6 @@ public class BiometricSettingsKeyguardPreferenceController extends TogglePrefere @Override public int getSliceHighlightMenuRes() { // not needed since it's not sliceable - return 0; + return NO_RES; } } diff --git a/src/com/android/settings/biometrics/face/FaceSettingsPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsPreferenceController.java index a8bff6b4197..bda078f2711 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsPreferenceController.java @@ -55,6 +55,6 @@ public abstract class FaceSettingsPreferenceController extends TogglePreferenceC @Override public int getSliceHighlightMenuRes() { // not needed since it's not sliceable - return 0; + return NO_RES; } } diff --git a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java index 2bab3e97a86..93e6d0a9644 100644 --- a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java +++ b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java @@ -120,7 +120,7 @@ public class LocationTimeZoneDetectionPreferenceController @Override public int getSliceHighlightMenuRes() { // not needed since it's not sliceable - return 0; + return NO_RES; } @Override diff --git a/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java b/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java index 3d6695b1a37..52694af537c 100644 --- a/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java +++ b/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java @@ -55,7 +55,7 @@ public class EmergencyGestureSoundPreferenceController extends TogglePreferenceC @Override public int getSliceHighlightMenuRes() { // not needed since it's not sliceable - return 0; + return NO_RES; } @Override diff --git a/src/com/android/settings/flashlight/FlashlightSlice.java b/src/com/android/settings/flashlight/FlashlightSlice.java index f18d7122cf7..eaf059aa673 100644 --- a/src/com/android/settings/flashlight/FlashlightSlice.java +++ b/src/com/android/settings/flashlight/FlashlightSlice.java @@ -120,7 +120,7 @@ public class FlashlightSlice implements CustomSliceable { @Override public int getSliceHighlightMenuRes() { // no landing page in Settings - return 0; + return NO_RES; } private static String getCameraId(Context context) throws CameraAccessException { diff --git a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java index 46f279db260..e3609cb214b 100644 --- a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java +++ b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java @@ -68,7 +68,7 @@ public abstract class TelephonyTogglePreferenceController extends TogglePreferen @Override public int getSliceHighlightMenuRes() { // not needed since it's not sliceable - return 0; + return NO_RES; } /** diff --git a/src/com/android/settings/notification/BubbleNotificationPreferenceController.java b/src/com/android/settings/notification/BubbleNotificationPreferenceController.java index 9e3566498b0..eeb9924f50e 100644 --- a/src/com/android/settings/notification/BubbleNotificationPreferenceController.java +++ b/src/com/android/settings/notification/BubbleNotificationPreferenceController.java @@ -91,7 +91,7 @@ public class BubbleNotificationPreferenceController extends @Override public int getSliceHighlightMenuRes() { // not needed since it's not sliceable - return 0; + return NO_RES; } @Override diff --git a/src/com/android/settings/slices/Sliceable.java b/src/com/android/settings/slices/Sliceable.java index 31555df0022..406cb3aeb73 100644 --- a/src/com/android/settings/slices/Sliceable.java +++ b/src/com/android/settings/slices/Sliceable.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.net.Uri; import android.widget.Toast; +import androidx.annotation.StringRes; import androidx.slice.Slice; import com.android.settings.R; @@ -135,10 +136,16 @@ public interface Sliceable { } /** - * @return a resource ID that indicates which menu entry should be highlighted in multi-pane - * mode. + * Used to mark a {@link Sliceable} that has no highlight menu string resource. */ - default int getSliceHighlightMenuRes() { - return 0; + int NO_RES = 0; + + /** + * @return a string resource declared in res/values/menu_keys.xml that indicates which menu + * entry should be highlighted in two-pane mode, or {@link #NO_RES} representing highlighting is + * not applicable. + */ + @StringRes default int getSliceHighlightMenuRes() { + return NO_RES; } } diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java index df6a38b20a1..8687e5ada64 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java @@ -305,7 +305,7 @@ public class ProviderModelSliceHelperTest { @Override public int getSliceHighlightMenuRes() { - return 0; + return NO_RES; } } diff --git a/tests/unit/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java b/tests/unit/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java index 490b7bd62c6..38347e91ca1 100644 --- a/tests/unit/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java +++ b/tests/unit/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java @@ -146,7 +146,7 @@ public class SpecialCaseSliceManagerTest { @Override public int getSliceHighlightMenuRes() { - return 0; + return NO_RES; } } } From 9b6eac28f30ccf73548e2281456c860fbe3eb8d2 Mon Sep 17 00:00:00 2001 From: "Wesley.CW Wang" Date: Wed, 3 Nov 2021 12:38:10 +0800 Subject: [PATCH 11/12] Move optimization mode backup timing into onPause - Move to onPause to avoid some corner case (like kill apps from recent app won't trigger onDestroy) Bug: 195306545 Test: make SettingsRoboTests Change-Id: I89445a7138c30e6c869ce1c5c9a3f818997d99a7 --- .../settings/fuelgauge/AdvancedPowerUsageDetail.java | 10 ++-------- .../fuelgauge/AdvancedPowerUsageDetailTest.java | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 6b024ed705c..eefeb9e9ae2 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -289,22 +289,16 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements if (mEnableTriState) { final int selectedPreference = getSelectedPreference(); + notifyBackupManager(); logMetricCategory(selectedPreference); mBatteryOptimizeUtils.setAppUsageState(selectedPreference); Log.d(TAG, "Leave with mode: " + selectedPreference); } } - @Override - public void onDestroy() { - super.onDestroy(); - notifyBackupManager(); - } - @VisibleForTesting void notifyBackupManager() { - if (mEnableTriState - && mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { + if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { final BackupManager backupManager = mBackupManager != null ? mBackupManager : new BackupManager(getContext()); backupManager.dataChanged(); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 7e975a08e25..db002cb047f 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -856,7 +856,7 @@ public class AdvancedPowerUsageDetailTest { .thenReturn(BatteryOptimizeUtils.MODE_UNRESTRICTED); mFragment.mEnableTriState = false; - mFragment.notifyBackupManager(); + mFragment.onPause(); verifyZeroInteractions(mBackupManager); } From e427cc6752fac18cdc026754a3da392ea5fc6259 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Mon, 25 Oct 2021 14:11:16 +0800 Subject: [PATCH 12/12] Search experience improvement for large screen - Support fragment and direct link in SearchResultTrampoline - Start activity for SI case and start deep link trampoline for others - Disable menu highlight whenever the search bar is clicked - Don't overwrite SettingsApplication's homepage activity in SliceDeepLinkHomepageActivity - Scroll to highlighted menu entry after homepage is loaded to prevent UI overlapping Bug: 201724410 Test: manual, robotest build pass Change-Id: I5115d17d829e85036000da2e80f0e5b0598c733f --- .../android/settings/SettingsActivity.java | 37 ++++---- .../homepage/SettingsHomepageActivity.java | 39 +++++++- .../SliceDeepLinkHomepageActivity.java | 5 ++ .../settings/homepage/TopLevelSettings.java | 14 ++- .../search/SearchFeatureProvider.java | 25 ++++-- .../search/SearchResultTrampoline.java | 88 +++++++++++++++---- .../SettingsSearchIndexablesProvider.java | 1 - ...ighlightableTopLevelPreferenceAdapter.java | 17 ++-- .../search/SearchFeatureProviderImplTest.java | 7 +- 9 files changed, 176 insertions(+), 57 deletions(-) diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index df30d8b546c..9c81895e976 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -255,7 +255,8 @@ public class SettingsActivity extends SettingsBaseActivity // Should happen before any call to getIntent() getMetaData(); final Intent intent = getIntent(); - if (launchHomepageForTwoPaneDeepLink(intent)) { + if (shouldShowTwoPaneDeepLink(intent)) { + launchHomepageForTwoPaneDeepLink(intent); finishAndRemoveTask(); return; } @@ -368,16 +369,13 @@ public class SettingsActivity extends SettingsBaseActivity intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false); } - /** Returns true if the Activity is started by a deep link intent for large screen devices. */ - private boolean launchHomepageForTwoPaneDeepLink(Intent intent) { - if (!shouldShowTwoPaneDeepLink(intent)) { - return false; - } - + /** + * Returns the deep link trampoline intent for large screen devices. + */ + public static Intent getTrampolineIntent(Intent intent, String highlightMenuKey) { final Intent detailIntent = new Intent(intent); // It's a deep link intent, SettingsHomepageActivity will set SplitPairRule and start it. final Intent trampolineIntent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY); - trampolineIntent.replaceExtras(detailIntent); // Relay detail intent data to prevent failure of Intent#ParseUri. @@ -391,22 +389,27 @@ public class SettingsActivity extends SettingsBaseActivity trampolineIntent.putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI, detailIntent.toUri(Intent.URI_INTENT_SCHEME)); - if (detailIntent.getBooleanExtra(EXTRA_IS_FROM_SLICE, false)) { - trampolineIntent.setClass(this, SliceDeepLinkHomepageActivity.class); + trampolineIntent.putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY, + highlightMenuKey); + trampolineIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + return trampolineIntent; + } + + private void launchHomepageForTwoPaneDeepLink(Intent intent) { + final Intent trampolineIntent; + if (intent.getBooleanExtra(EXTRA_IS_FROM_SLICE, false)) { // Get menu key for slice deep link case. - final String highlightMenuKey = detailIntent.getStringExtra( + final String highlightMenuKey = intent.getStringExtra( EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY); if (!TextUtils.isEmpty(highlightMenuKey)) { mHighlightMenuKey = highlightMenuKey; } + trampolineIntent = getTrampolineIntent(intent, mHighlightMenuKey); + trampolineIntent.setClass(this, SliceDeepLinkHomepageActivity.class); + } else { + trampolineIntent = getTrampolineIntent(intent, mHighlightMenuKey); } - - trampolineIntent.putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY, - mHighlightMenuKey); - trampolineIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); startActivity(trampolineIntent); - - return true; } private boolean shouldShowTwoPaneDeepLink(Intent intent) { diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index 2bbc11e2b95..ae8c61e3b30 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; +import android.util.ArraySet; import android.util.FeatureFlagUtils; import android.util.Log; import android.view.View; @@ -54,6 +55,7 @@ import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; import java.net.URISyntaxException; +import java.util.Set; /** Settings homepage activity */ public class SettingsHomepageActivity extends FragmentActivity implements @@ -79,10 +81,27 @@ public class SettingsHomepageActivity extends FragmentActivity implements private View mHomepageView; private View mSuggestionView; private CategoryMixin mCategoryMixin; + private Set mLoadedListeners; - @Override - public CategoryMixin getCategoryMixin() { - return mCategoryMixin; + /** A listener receiving homepage loaded events. */ + public interface HomepageLoadedListener { + /** Called when the homepage is loaded. */ + void onHomepageLoaded(); + } + + /** + * Try to register a {@link HomepageLoadedListener}. If homepage is already loaded, the + * listener will not be notified. + * + * @return Whether the listener should be registered. + */ + public boolean registerHomepageLoadedListenerIfNeeded(HomepageLoadedListener listener) { + if (mHomepageView == null) { + return false; + } else { + mLoadedListeners.add(listener); + return true; + } } /** @@ -97,17 +116,25 @@ public class SettingsHomepageActivity extends FragmentActivity implements mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE); mHomepageView.setVisibility(View.VISIBLE); mHomepageView = null; + mLoadedListeners.forEach(listener -> listener.onHomepageLoaded()); + mLoadedListeners.clear(); + } + + @Override + public CategoryMixin getCategoryMixin() { + return mCategoryMixin; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ((SettingsApplication) getApplication()).setHomeActivity(this); + setHomeActivity(); setContentView(R.layout.settings_homepage_container); final View appBar = findViewById(R.id.app_bar_container); appBar.setMinimumHeight(getSearchBoxHeight()); initHomepageContainer(); + mLoadedListeners = new ArraySet<>(); final Toolbar toolbar = findViewById(R.id.search_action_bar); FeatureFactory.getFactory(this).getSearchFeatureProvider() @@ -158,6 +185,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements launchDeepLinkIntentToRight(); } + protected void setHomeActivity() { + ((SettingsApplication) getApplication()).setHomeActivity(this); + } + private void showSuggestionFragment() { final Class fragment = FeatureFactory.getFactory(this) .getSuggestionFeatureProvider(this).getContextualSuggestionFragment(); diff --git a/src/com/android/settings/homepage/SliceDeepLinkHomepageActivity.java b/src/com/android/settings/homepage/SliceDeepLinkHomepageActivity.java index 2f836127e21..61e946d3bd8 100644 --- a/src/com/android/settings/homepage/SliceDeepLinkHomepageActivity.java +++ b/src/com/android/settings/homepage/SliceDeepLinkHomepageActivity.java @@ -20,6 +20,11 @@ import android.content.ComponentName; /** Activity for Slices to launch Settings deep link page */ public class SliceDeepLinkHomepageActivity extends SettingsHomepageActivity { + @Override + protected void setHomeActivity() { + // do not overwrite homepage activity in SettingsApplication + } + @Override protected ComponentName getDeepLinkComponent() { return new ComponentName(getApplicationContext(), getClass()); diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java index d3bfa02ae27..e9c7ef89668 100644 --- a/src/com/android/settings/homepage/TopLevelSettings.java +++ b/src/com/android/settings/homepage/TopLevelSettings.java @@ -101,7 +101,7 @@ public class TopLevelSettings extends DashboardFragment implements public boolean onPreferenceTreeClick(Preference preference) { // Register SplitPairRule for SubSettings. ActivityEmbeddingRulesController.registerSubSettingsPairRuleIfNeeded(getContext(), - true /* clearTop*/); + true /* clearTop */); setHighlightPreferenceKey(preference.getKey()); return super.onPreferenceTreeClick(preference); @@ -184,6 +184,15 @@ public class TopLevelSettings extends DashboardFragment implements } } + /** Disable highlight on the menu entry */ + public void disableMenuHighlight() { + if (mTopLevelAdapter == null) { + return; + } + mHighlightedPreferenceKey = null; + mTopLevelAdapter.highlightPreference(mHighlightedPreferenceKey, /* scrollNeeded= */ false); + } + @Override protected boolean shouldForceRoundedIcon() { return getContext().getResources() @@ -202,7 +211,8 @@ public class TopLevelSettings extends DashboardFragment implements Log.d(TAG, "onCreateAdapter, pref key: " + mHighlightedPreferenceKey); mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter( - getActivity(), preferenceScreen, getListView(), mHighlightedPreferenceKey); + (SettingsHomepageActivity) getActivity(), preferenceScreen, getListView(), + mHighlightedPreferenceKey); return mTopLevelAdapter; } diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java index 05fd2aea4de..9b081e18925 100644 --- a/src/com/android/settings/search/SearchFeatureProvider.java +++ b/src/com/android/settings/search/SearchFeatureProvider.java @@ -19,7 +19,6 @@ package com.android.settings.search; import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO; import android.annotation.NonNull; -import android.app.Activity; import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Context; @@ -30,8 +29,12 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toolbar; +import androidx.fragment.app.FragmentActivity; + import com.android.settings.R; import com.android.settings.Utils; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; +import com.android.settings.homepage.TopLevelSettings; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.search.SearchIndexableResources; @@ -66,7 +69,7 @@ public interface SearchFeatureProvider { /** * Initializes the search toolbar. */ - default void initSearchToolbar(Activity activity, Toolbar toolbar, int pageId) { + default void initSearchToolbar(FragmentActivity activity, Toolbar toolbar, int pageId) { if (activity == null || toolbar == null) { return; } @@ -91,7 +94,8 @@ public interface SearchFeatureProvider { toolbar.setOnClickListener(tb -> { final Context context = activity.getApplicationContext(); - final Intent intent = buildSearchIntent(context, pageId); + final Intent intent = buildSearchIntent(context, pageId) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (activity.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { @@ -103,8 +107,19 @@ public interface SearchFeatureProvider { FeatureFactory.getFactory(context).getMetricsFeatureProvider() .logSettingsTileClick(KEY_HOMEPAGE_SEARCH_BAR, pageId); - final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle(); - activity.startActivity(intent, bundle); + + if (ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) { + final TopLevelSettings fragment = (TopLevelSettings) activity + .getSupportFragmentManager().findFragmentById(R.id.main_content); + if (fragment != null) { + fragment.disableMenuHighlight(); + } + activity.startActivity(intent); + } else { + final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity) + .toBundle(); + activity.startActivity(intent, bundle); + } }); } diff --git a/src/com/android/settings/search/SearchResultTrampoline.java b/src/com/android/settings/search/SearchResultTrampoline.java index 3414efeda72..d20a2ea860d 100644 --- a/src/com/android/settings/search/SearchResultTrampoline.java +++ b/src/com/android/settings/search/SearchResultTrampoline.java @@ -20,52 +20,102 @@ import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENT import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB; import android.app.Activity; +import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; import com.android.settings.SettingsActivity; import com.android.settings.SubSettings; import com.android.settings.activityembedding.ActivityEmbeddingRulesController; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.overlay.FeatureFactory; +import java.net.URISyntaxException; + /** * A trampoline activity that launches setting result page. */ public class SearchResultTrampoline extends Activity { + private static final String TAG = "SearchResultTrampoline"; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + final ComponentName callingActivity = getCallingActivity(); // First make sure caller has privilege to launch a search result page. FeatureFactory.getFactory(this) .getSearchFeatureProvider() - .verifyLaunchSearchResultPageCaller(this, getCallingActivity()); + .verifyLaunchSearchResultPageCaller(this, callingActivity); // Didn't crash, proceed and launch the result as a subsetting. - final Intent intent = getIntent(); + Intent intent = getIntent(); + final String highlightMenuKey = intent.getStringExtra( + Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY); - // Hack to take EXTRA_FRAGMENT_ARG_KEY from intent and set into - // EXTRA_SHOW_FRAGMENT_ARGUMENTS. This is necessary because intent could be from external - // caller and args may not persisted. - final String settingKey = intent.getStringExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY); - final int tab = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TAB, 0); - final Bundle args = new Bundle(); - args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, settingKey); - args.putInt(EXTRA_SHOW_FRAGMENT_TAB, tab); - intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); + final String fragment = intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT); + if (!TextUtils.isEmpty(fragment)) { + // Hack to take EXTRA_FRAGMENT_ARG_KEY from intent and set into + // EXTRA_SHOW_FRAGMENT_ARGUMENTS. This is necessary because intent could be from + // external caller and args may not persisted. + final String settingKey = intent.getStringExtra( + SettingsActivity.EXTRA_FRAGMENT_ARG_KEY); + final int tab = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TAB, 0); + final Bundle args = new Bundle(); + args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, settingKey); + args.putInt(EXTRA_SHOW_FRAGMENT_TAB, tab); + intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); - // Register SplirPairRule for SubSettings, set clearTop false to prevent unexpected back - // navigation behavior. - ActivityEmbeddingRulesController.registerSubSettingsPairRuleIfNeeded(this /* context */, - false /* clearTop*/); + // Reroute request to SubSetting. + intent.setClass(this /* context */, SubSettings.class); + } else { + // Direct link case + final String intentUriString = intent.getStringExtra( + Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI); + if (TextUtils.isEmpty(intentUriString)) { + Log.e(TAG, "No EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI for deep link"); + finish(); + return; + } - // Reroute request to SubSetting. - intent.setClass(this /* context */, SubSettings.class) - .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); - startActivity(intent); + try { + intent = Intent.parseUri(intentUriString, Intent.URI_INTENT_SCHEME); + } catch (URISyntaxException e) { + Log.e(TAG, "Failed to parse deep link intent: " + e); + finish(); + return; + } + } + + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + + if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) { + startActivity(intent); + } else if (isFromSettingsIntelligence(callingActivity)) { + // Register SplitPairRule for SubSettings, set clearTop false to prevent unexpected back + // navigation behavior. + ActivityEmbeddingRulesController.registerSubSettingsPairRuleIfNeeded(this, + false /* clearTop */); + // TODO: pass menu key to homepage + intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } else { + // Two-pane case + intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(SettingsActivity.getTrampolineIntent(intent, highlightMenuKey)); + } // Done. finish(); } + private boolean isFromSettingsIntelligence(ComponentName callingActivity) { + return callingActivity != null && TextUtils.equals( + callingActivity.getPackageName(), + FeatureFactory.getFactory(this).getSearchFeatureProvider() + .getSettingsIntelligencePkgName(this)); + } } diff --git a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java index feb9510bff9..d6635a197c2 100644 --- a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java +++ b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java @@ -365,7 +365,6 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider { // The classname and intent information comes from the PreIndexData // This will be more clear when provider conversion is done at PreIndex time. raw.className = bundle.getTargetClass().getName(); - } rawList.addAll(providerRaws); } diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java index 19a91f65d16..bf92bbdf679 100644 --- a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java +++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java @@ -16,7 +16,6 @@ package com.android.settings.widget; -import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; import android.text.TextUtils; @@ -34,6 +33,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.Utils; import com.android.settings.activityembedding.ActivityEmbeddingUtils; +import com.android.settings.homepage.SettingsHomepageActivity; /** * Adapter for highlighting top level preferences @@ -54,7 +54,7 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt final int mIconColorHighlight; private final Context mContext; - private final Activity mActivity; + private final SettingsHomepageActivity mHomepageActivity; private final RecyclerView mRecyclerView; private final int mNormalBackgroundRes; private String mHighlightKey; @@ -63,13 +63,13 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt private boolean mHighlightNeeded; private boolean mScrolled; - public HighlightableTopLevelPreferenceAdapter(Activity activity, + public HighlightableTopLevelPreferenceAdapter(SettingsHomepageActivity homepageActivity, PreferenceGroup preferenceGroup, RecyclerView recyclerView, String key) { super(preferenceGroup); mRecyclerView = recyclerView; mHighlightKey = key; mContext = preferenceGroup.getContext(); - mActivity = activity; + mHomepageActivity = homepageActivity; final TypedValue outValue = new TypedValue(); mContext.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true /* resolveRefs */); @@ -115,7 +115,7 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt * A function can highlight a specific setting in recycler view. */ public void requestHighlight() { - if (mRecyclerView == null || TextUtils.isEmpty(mHighlightKey)) { + if (mRecyclerView == null) { return; } @@ -194,6 +194,11 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt return; } + if (mHomepageActivity.registerHomepageLoadedListenerIfNeeded( + () -> scrollToPositionIfNeeded(position))) { + return; + } + // Only when the recyclerView is loaded, it can be scrolled final View view = mRecyclerView.getChildAt(position); if (view == null) { @@ -236,6 +241,6 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt } private boolean isHighlightNeeded() { - return ActivityEmbeddingUtils.isTwoPaneResolution(mActivity); + return ActivityEmbeddingUtils.isTwoPaneResolution(mHomepageActivity); } } diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java index 444a8137889..5de57b6c95e 100644 --- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java @@ -19,7 +19,6 @@ package com.android.settings.search; import static com.google.common.truth.Truth.assertThat; -import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Intent; @@ -29,6 +28,8 @@ import android.net.Uri; import android.provider.Settings; import android.widget.Toolbar; +import androidx.fragment.app.FragmentActivity; + import com.android.settings.R; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowUtils; @@ -46,13 +47,13 @@ import org.robolectric.shadows.ShadowPackageManager; public class SearchFeatureProviderImplTest { private SearchFeatureProviderImpl mProvider; - private Activity mActivity; + private FragmentActivity mActivity; private ShadowPackageManager mPackageManager; @Before public void setUp() { FakeFeatureFactory.setupForTest(); - mActivity = Robolectric.setupActivity(Activity.class); + mActivity = Robolectric.setupActivity(FragmentActivity.class); mProvider = new SearchFeatureProviderImpl(); mPackageManager = Shadows.shadowOf(mActivity.getPackageManager()); Settings.Global.putInt(mActivity.getContentResolver(),