From d05686b43164e16fa1ccf7dd6d7a292c7dec0bf5 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 1 Oct 2021 08:15:03 +0000 Subject: [PATCH 01/21] [RESTRICT AUTOMERGE] Revert "Add SafetyNet logging" Revert submission 15220072-add_permission_wifidialog-qt-dev Reason for revert: rollback CLs to avoid compatibility risks Reverted Changes: I1535f6f2d:Add SafetyNet logging If7ca069c8:Add permission checking to WifiDialogActivity Bug: 185126813 Change-Id: I42b77ac420d9438c741b28ce33bcadf1c74b19eb --- src/com/android/settings/wifi/WifiDialogActivity.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index 53c941e9c21..a2121908894 100644 --- a/src/com/android/settings/wifi/WifiDialogActivity.java +++ b/src/com/android/settings/wifi/WifiDialogActivity.java @@ -28,7 +28,6 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.ActionListener; import android.os.Bundle; -import android.util.EventLog; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -217,7 +216,6 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo final String callingPackage = getCallingPackage(); if (callingPackage == null) { Log.d(TAG, "Failed to get the calling package, don't return the result."); - EventLog.writeEvent(0x534e4554, "185126813", -1 /* UID */, "no calling package"); return false; } @@ -234,14 +232,6 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo } Log.d(TAG, "The calling package does not have the necessary permissions for result."); - try { - EventLog.writeEvent(0x534e4554, "185126813", - getPackageManager().getPackageUid(callingPackage, 0 /* flags */), - "no permission"); - } catch (PackageManager.NameNotFoundException e) { - EventLog.writeEvent(0x534e4554, "185126813", -1 /* UID */, "no permission"); - Log.w(TAG, "Cannot find the UID, calling package: " + callingPackage, e); - } return false; } } From 153b0f03f5e1119d396d5acefb880be786212a2a Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 1 Oct 2021 08:17:59 +0000 Subject: [PATCH 02/21] [RESTRICT AUTOMERGE] Revert "Add comment to AndroidManifest" This reverts commit 4fb84a37378971a0d5236b16a19adee2b29531de. Reason for revert: rollback CLs to avoid compatibility risks Bug: 185126813 Change-Id: I495cd723917db6405ef3b540a6f01eaeaf8a45fe --- AndroidManifest.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 46f5a5362de..ff3f650f895 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2697,10 +2697,6 @@ - Date: Fri, 1 Oct 2021 08:27:32 +0000 Subject: [PATCH 03/21] [RESTRICT AUTOMERGE] Revert "[DO NOT MERGE] Add SafetyNet logging" Revert submission 15207347-add_permission_wifidialog Reason for revert: rollback CLs to avoid compatibility risks Reverted Changes: If7ca069c8:[DO NOT MERGE] Add permission checking to WifiDial... I1535f6f2d:[DO NOT MERGE] Add SafetyNet logging Bug: 185126813 Change-Id: I41721dbb34612144d00dba7885944d834bf93c7d --- src/com/android/settings/wifi/WifiDialogActivity.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index 53c941e9c21..a2121908894 100644 --- a/src/com/android/settings/wifi/WifiDialogActivity.java +++ b/src/com/android/settings/wifi/WifiDialogActivity.java @@ -28,7 +28,6 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.ActionListener; import android.os.Bundle; -import android.util.EventLog; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -217,7 +216,6 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo final String callingPackage = getCallingPackage(); if (callingPackage == null) { Log.d(TAG, "Failed to get the calling package, don't return the result."); - EventLog.writeEvent(0x534e4554, "185126813", -1 /* UID */, "no calling package"); return false; } @@ -234,14 +232,6 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo } Log.d(TAG, "The calling package does not have the necessary permissions for result."); - try { - EventLog.writeEvent(0x534e4554, "185126813", - getPackageManager().getPackageUid(callingPackage, 0 /* flags */), - "no permission"); - } catch (PackageManager.NameNotFoundException e) { - EventLog.writeEvent(0x534e4554, "185126813", -1 /* UID */, "no permission"); - Log.w(TAG, "Cannot find the UID, calling package: " + callingPackage, e); - } return false; } } From 1c93d29b73db9acada70d45387b8c76c6b91b10f Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 1 Oct 2021 08:28:44 +0000 Subject: [PATCH 04/21] [RESTRICT AUTOMERGE] Revert "Add comment to AndroidManifest" This reverts commit fa1e3870d855afd083be319008d1eb59593f3f51. Reason for revert: rollback CLs to avoid compatibility risks Bug: 185126813 Change-Id: I77a027075465b5685a857b4c8c68203ef812a172 --- AndroidManifest.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f7ca87c608a..2f53cc1e7c8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2708,10 +2708,6 @@ - Date: Fri, 1 Oct 2021 08:15:03 +0000 Subject: [PATCH 05/21] [RESTRICT AUTOMERGE] Revert "Add permission checking to WifiDialogActivity" Revert submission 15220072-add_permission_wifidialog-qt-dev Reason for revert: rollback CLs to avoid compatibility risks Reverted Changes: I1535f6f2d:Add SafetyNet logging If7ca069c8:Add permission checking to WifiDialogActivity Bug: 185126813 Change-Id: I3f75adf5bf0eea037dc0c5dfbeeb22b35b2856b6 --- .../settings/wifi/WifiDialogActivity.java | 55 ++----- .../settings/wifi/WifiDialogActivityTest.java | 147 ------------------ 2 files changed, 9 insertions(+), 193 deletions(-) diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index a2121908894..77827867630 100644 --- a/src/com/android/settings/wifi/WifiDialogActivity.java +++ b/src/com/android/settings/wifi/WifiDialogActivity.java @@ -16,13 +16,9 @@ package com.android.settings.wifi; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; - import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.NetworkInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -57,12 +53,10 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo public static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; - @VisibleForTesting - static final int RESULT_CONNECTED = RESULT_FIRST_USER; + private static final int RESULT_CONNECTED = RESULT_FIRST_USER; private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; - @VisibleForTesting - static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; + private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; private WifiDialog mDialog; @@ -162,22 +156,17 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo } } - Intent resultData = hasPermissionForResult() ? createResultData(config, accessPoint) : null; - setResult(RESULT_CONNECTED, resultData); - finish(); - } - - protected Intent createResultData(WifiConfiguration config, AccessPoint accessPoint) { - Intent result = new Intent(); + Intent resultData = new Intent(); if (accessPoint != null) { Bundle accessPointState = new Bundle(); accessPoint.saveWifiState(accessPointState); - result.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); + resultData.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); } if (config != null) { - result.putExtra(KEY_WIFI_CONFIGURATION, config); + resultData.putExtra(KEY_WIFI_CONFIGURATION, config); } - return result; + setResult(RESULT_CONNECTED, resultData); + finish(); } @Override @@ -203,35 +192,9 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo if (resultCode != RESULT_OK) { return; } - if (hasPermissionForResult()) { - setResult(RESULT_CONNECTED, data); - } else { - setResult(RESULT_CONNECTED); - } + + setResult(RESULT_CONNECTED, data); finish(); } } - - protected boolean hasPermissionForResult() { - final String callingPackage = getCallingPackage(); - if (callingPackage == null) { - Log.d(TAG, "Failed to get the calling package, don't return the result."); - return false; - } - - if (getPackageManager().checkPermission(ACCESS_COARSE_LOCATION, callingPackage) - == PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "The calling package has ACCESS_COARSE_LOCATION permission for result."); - return true; - } - - if (getPackageManager().checkPermission(ACCESS_FINE_LOCATION, callingPackage) - == PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "The calling package has ACCESS_FINE_LOCATION permission for result."); - return true; - } - - Log.d(TAG, "The calling package does not have the necessary permissions for result."); - return false; - } } diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java index f86f6e7befc..15a07802be2 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java @@ -16,32 +16,18 @@ package com.android.settings.wifi; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; - -import static com.android.settings.wifi.WifiDialogActivity.REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER; -import static com.android.settings.wifi.WifiDialogActivity.RESULT_CONNECTED; -import static com.android.settings.wifi.WifiDialogActivity.RESULT_OK; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiManager; import com.android.settings.R; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowConnectivityManager; import com.android.settings.testutils.shadow.ShadowWifiManager; import com.android.settings.wifi.dpp.WifiDppEnrolleeActivity; -import com.android.settingslib.wifi.AccessPoint; import com.google.android.setupcompat.util.WizardManagerHelper; @@ -64,30 +50,13 @@ import org.robolectric.util.ReflectionHelpers; }) public class WifiDialogActivityTest { - private static final String CALLING_PACKAGE = "calling_package"; private static final String AP1_SSID = "\"ap1\""; - - @Mock - PackageManager mPackageManager; - @Mock - WifiManager mWifiManager; - @Mock - WifiDialog mWifiDialog; - @Mock - WifiConfiguration mWifiConfiguration; - @Mock - AccessPoint mAccessPoint; - @Mock - Intent mResultData; @Mock private WifiConfigController mController; @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mWifiDialog.getController()).thenReturn(mController); - when(mController.getConfig()).thenReturn(mWifiConfiguration); - when(mController.getAccessPoint()).thenReturn(mAccessPoint); WifiConfiguration wifiConfig = new WifiConfiguration(); wifiConfig.SSID = AP1_SSID; @@ -107,29 +76,6 @@ public class WifiDialogActivityTest { assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP1_SSID); } - @Test - public void onSubmit_noPermissionForResult_setResultWithoutData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(false); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog); - - verify(activity).setResult(RESULT_CONNECTED, null); - } - - @Test - public void onSubmit_hasPermissionForResult_setResultWithData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(true); - when(activity.createResultData(any(), any())).thenReturn(mResultData); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog); - - verify(activity).setResult(RESULT_CONNECTED, mResultData); - } - @Test public void onSubmit_whenConnectForCallerIsFalse_shouldNotConnectToNetwork() { WifiDialogActivity activity = @@ -186,97 +132,4 @@ public class WifiDialogActivityTest { assertThat(controller.get().getThemeResId()). isEqualTo(R.style.LightTheme_SettingsBase_SetupWizard); } - - @Test - public void onActivityResult_noPermissionForResult_setResultWithoutData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(false); - final Intent data = new Intent(); - - activity.onActivityResult(REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER, RESULT_OK, - data); - - verify(activity).setResult(RESULT_CONNECTED); - } - - @Test - public void onActivityResult_hasPermissionForResult_setResultWithData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(true); - final Intent data = new Intent(); - - activity.onActivityResult(REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER, RESULT_OK, - data); - - verify(activity).setResult(RESULT_CONNECTED, data); - } - - @Test - public void hasPermissionForResult_noCallingPackage_returnFalse() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(null); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isFalse(); - } - - @Test - public void hasPermissionForResult_noPermission_returnFalse() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(null); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isFalse(); - } - - @Test - public void hasPermissionForResult_hasCoarseLocationPermission_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } - - @Test - public void hasPermissionForResult_hasFineLocationPermission_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } - - @Test - public void hasPermissionForResult_haveBothLocationPermissions_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } } From 18043562c1389e79e4c45a37a859401a7820bbd1 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 1 Oct 2021 08:27:32 +0000 Subject: [PATCH 06/21] [RESTRICT AUTOMERGE] Revert "[DO NOT MERGE] Add permission checking to WifiDialogActivity" Revert submission 15207347-add_permission_wifidialog Reason for revert: rollback CLs to avoid compatibility risks Reverted Changes: If7ca069c8:[DO NOT MERGE] Add permission checking to WifiDial... I1535f6f2d:[DO NOT MERGE] Add SafetyNet logging Bug: 185126813 Change-Id: Ifc3b53dc91fc8d5aa31c1a10d4eb033bbe54c47d --- .../settings/wifi/WifiDialogActivity.java | 55 ++----- .../settings/wifi/WifiDialogActivityTest.java | 147 ------------------ 2 files changed, 9 insertions(+), 193 deletions(-) diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index a2121908894..77827867630 100644 --- a/src/com/android/settings/wifi/WifiDialogActivity.java +++ b/src/com/android/settings/wifi/WifiDialogActivity.java @@ -16,13 +16,9 @@ package com.android.settings.wifi; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; - import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.NetworkInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -57,12 +53,10 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo public static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; - @VisibleForTesting - static final int RESULT_CONNECTED = RESULT_FIRST_USER; + private static final int RESULT_CONNECTED = RESULT_FIRST_USER; private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; - @VisibleForTesting - static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; + private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; private WifiDialog mDialog; @@ -162,22 +156,17 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo } } - Intent resultData = hasPermissionForResult() ? createResultData(config, accessPoint) : null; - setResult(RESULT_CONNECTED, resultData); - finish(); - } - - protected Intent createResultData(WifiConfiguration config, AccessPoint accessPoint) { - Intent result = new Intent(); + Intent resultData = new Intent(); if (accessPoint != null) { Bundle accessPointState = new Bundle(); accessPoint.saveWifiState(accessPointState); - result.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); + resultData.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); } if (config != null) { - result.putExtra(KEY_WIFI_CONFIGURATION, config); + resultData.putExtra(KEY_WIFI_CONFIGURATION, config); } - return result; + setResult(RESULT_CONNECTED, resultData); + finish(); } @Override @@ -203,35 +192,9 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo if (resultCode != RESULT_OK) { return; } - if (hasPermissionForResult()) { - setResult(RESULT_CONNECTED, data); - } else { - setResult(RESULT_CONNECTED); - } + + setResult(RESULT_CONNECTED, data); finish(); } } - - protected boolean hasPermissionForResult() { - final String callingPackage = getCallingPackage(); - if (callingPackage == null) { - Log.d(TAG, "Failed to get the calling package, don't return the result."); - return false; - } - - if (getPackageManager().checkPermission(ACCESS_COARSE_LOCATION, callingPackage) - == PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "The calling package has ACCESS_COARSE_LOCATION permission for result."); - return true; - } - - if (getPackageManager().checkPermission(ACCESS_FINE_LOCATION, callingPackage) - == PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "The calling package has ACCESS_FINE_LOCATION permission for result."); - return true; - } - - Log.d(TAG, "The calling package does not have the necessary permissions for result."); - return false; - } } diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java index 1a6de550e5f..41d1bbe4f38 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java @@ -16,31 +16,17 @@ package com.android.settings.wifi; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; - -import static com.android.settings.wifi.WifiDialogActivity.REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER; -import static com.android.settings.wifi.WifiDialogActivity.RESULT_CONNECTED; -import static com.android.settings.wifi.WifiDialogActivity.RESULT_OK; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiManager; import com.android.settings.R; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowConnectivityManager; import com.android.settings.testutils.shadow.ShadowWifiManager; -import com.android.settingslib.wifi.AccessPoint; import com.google.android.setupcompat.util.WizardManagerHelper; @@ -62,30 +48,13 @@ import org.robolectric.util.ReflectionHelpers; }) public class WifiDialogActivityTest { - private static final String CALLING_PACKAGE = "calling_package"; private static final String AP1_SSID = "\"ap1\""; - - @Mock - PackageManager mPackageManager; - @Mock - WifiManager mWifiManager; - @Mock - WifiDialog mWifiDialog; - @Mock - WifiConfiguration mWifiConfiguration; - @Mock - AccessPoint mAccessPoint; - @Mock - Intent mResultData; @Mock private WifiConfigController mController; @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mWifiDialog.getController()).thenReturn(mController); - when(mController.getConfig()).thenReturn(mWifiConfiguration); - when(mController.getAccessPoint()).thenReturn(mAccessPoint); WifiConfiguration wifiConfig = new WifiConfiguration(); wifiConfig.SSID = AP1_SSID; @@ -105,29 +74,6 @@ public class WifiDialogActivityTest { assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP1_SSID); } - @Test - public void onSubmit_noPermissionForResult_setResultWithoutData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(false); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog); - - verify(activity).setResult(RESULT_CONNECTED, null); - } - - @Test - public void onSubmit_hasPermissionForResult_setResultWithData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(true); - when(activity.createResultData(any(), any())).thenReturn(mResultData); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog); - - verify(activity).setResult(RESULT_CONNECTED, mResultData); - } - @Test public void onSubmit_whenConnectForCallerIsFalse_shouldNotConnectToNetwork() { WifiDialogActivity activity = @@ -165,97 +111,4 @@ public class WifiDialogActivityTest { assertThat(dialog.getContext().getThemeResId()) .isEqualTo(R.style.SuwAlertDialogThemeCompat_Light); } - - @Test - public void onActivityResult_noPermissionForResult_setResultWithoutData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(false); - final Intent data = new Intent(); - - activity.onActivityResult(REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER, RESULT_OK, - data); - - verify(activity).setResult(RESULT_CONNECTED); - } - - @Test - public void onActivityResult_hasPermissionForResult_setResultWithData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(true); - final Intent data = new Intent(); - - activity.onActivityResult(REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER, RESULT_OK, - data); - - verify(activity).setResult(RESULT_CONNECTED, data); - } - - @Test - public void hasPermissionForResult_noCallingPackage_returnFalse() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(null); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isFalse(); - } - - @Test - public void hasPermissionForResult_noPermission_returnFalse() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(null); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isFalse(); - } - - @Test - public void hasPermissionForResult_hasCoarseLocationPermission_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } - - @Test - public void hasPermissionForResult_hasFineLocationPermission_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } - - @Test - public void hasPermissionForResult_haveBothLocationPermissions_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } } From e92d1d1a6b9aa253fdb930284e2b86e81abd20af Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 1 Oct 2021 08:40:29 +0000 Subject: [PATCH 07/21] [RESTRICT AUTOMERGE] Revert "Add comment to AndroidManifest" This reverts commit 56a88885ccc383269f02a49fff6bb503ed35c9d8. Reason for revert: rollback CLs to avoid compatibility risks Bug: 185126813 Change-Id: Ie9d17faa536186e9787ff17b9bc7f7e30adabd1c --- AndroidManifest.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9b2cf2412a2..f02919a9af6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2964,10 +2964,6 @@ - Date: Fri, 1 Oct 2021 13:53:56 +0000 Subject: [PATCH 08/21] [RESTRICT AUTOMERGE] Revert "Add SafetyNet logging" This reverts commit 199528d46065ffe444e140023bd723786dbb5cdd. Reason for revert: rollback CLs to avoid compatibility risks Bug: 185126813 Change-Id: I5ed1782f1a35681ab6be4f74ea3156420b7dad5f --- src/com/android/settings/wifi/WifiDialogActivity.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index 4514b0d19f0..abe1205c544 100644 --- a/src/com/android/settings/wifi/WifiDialogActivity.java +++ b/src/com/android/settings/wifi/WifiDialogActivity.java @@ -33,7 +33,6 @@ import android.os.Process; import android.os.SimpleClock; import android.os.SystemClock; import android.text.TextUtils; -import android.util.EventLog; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -357,7 +356,6 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog final String callingPackage = getCallingPackage(); if (callingPackage == null) { Log.d(TAG, "Failed to get the calling package, don't return the result."); - EventLog.writeEvent(0x534e4554, "185126813", -1 /* UID */, "no calling package"); return false; } @@ -374,14 +372,6 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog } Log.d(TAG, "The calling package does not have the necessary permissions for result."); - try { - EventLog.writeEvent(0x534e4554, "185126813", - getPackageManager().getPackageUid(callingPackage, 0 /* flags */), - "no permission"); - } catch (PackageManager.NameNotFoundException e) { - EventLog.writeEvent(0x534e4554, "185126813", -1 /* UID */, "no permission"); - Log.w(TAG, "Cannot find the UID, calling package: " + callingPackage, e); - } return false; } } From ca67955d7d013975acef22cc92aca470b5e1cd0f Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 1 Oct 2021 08:59:33 +0000 Subject: [PATCH 09/21] [RESTRICT AUTOMERGE] Revert "Add permission checking to WifiDialogActivity" This reverts commit 71e728e934bec9c5d121b285e5c0089f658723d2. Reason for revert: rollback CLs to avoid compatibility risks Bug: 185126813 Change-Id: I9251eb35ecba9bcc07eb3763c47c3ad7f55897f1 --- .../settings/wifi/WifiDialogActivity.java | 60 ++---- .../settings/wifi/WifiDialogActivityTest.java | 175 ------------------ 2 files changed, 13 insertions(+), 222 deletions(-) diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index abe1205c544..877933edafe 100644 --- a/src/com/android/settings/wifi/WifiDialogActivity.java +++ b/src/com/android/settings/wifi/WifiDialogActivity.java @@ -16,12 +16,8 @@ package com.android.settings.wifi; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; - import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.NetworkInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -82,12 +78,10 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog public static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; - @VisibleForTesting - static final int RESULT_CONNECTED = RESULT_FIRST_USER; + private static final int RESULT_CONNECTED = RESULT_FIRST_USER; private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; - @VisibleForTesting - static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; + private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; // Max age of tracked WifiEntries. private static final long MAX_SCAN_AGE_MILLIS = 15_000; @@ -264,7 +258,10 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog } } - Intent resultData = hasPermissionForResult() ? createResultData(config, null) : null; + final Intent resultData = new Intent(); + if (config != null) { + resultData.putExtra(KEY_WIFI_CONFIGURATION, config); + } setResult(RESULT_CONNECTED, resultData); finish(); } @@ -292,22 +289,17 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog } } - Intent resultData = hasPermissionForResult() ? createResultData(config, accessPoint) : null; - setResult(RESULT_CONNECTED, resultData); - finish(); - } - - protected Intent createResultData(WifiConfiguration config, AccessPoint accessPoint) { - Intent result = new Intent(); + Intent resultData = new Intent(); if (accessPoint != null) { Bundle accessPointState = new Bundle(); accessPoint.saveWifiState(accessPointState); - result.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); + resultData.putExtra(KEY_ACCESS_POINT_STATE, accessPointState); } if (config != null) { - result.putExtra(KEY_WIFI_CONFIGURATION, config); + resultData.putExtra(KEY_WIFI_CONFIGURATION, config); } - return result; + setResult(RESULT_CONNECTED, resultData); + finish(); } @Override @@ -343,35 +335,9 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog if (resultCode != RESULT_OK) { return; } - if (hasPermissionForResult()) { - setResult(RESULT_CONNECTED, data); - } else { - setResult(RESULT_CONNECTED); - } + + setResult(RESULT_CONNECTED, data); finish(); } } - - protected boolean hasPermissionForResult() { - final String callingPackage = getCallingPackage(); - if (callingPackage == null) { - Log.d(TAG, "Failed to get the calling package, don't return the result."); - return false; - } - - if (getPackageManager().checkPermission(ACCESS_COARSE_LOCATION, callingPackage) - == PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "The calling package has ACCESS_COARSE_LOCATION permission for result."); - return true; - } - - if (getPackageManager().checkPermission(ACCESS_FINE_LOCATION, callingPackage) - == PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "The calling package has ACCESS_FINE_LOCATION permission for result."); - return true; - } - - Log.d(TAG, "The calling package does not have the necessary permissions for result."); - return false; - } } diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java index 685819e7839..f601e36229c 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java @@ -16,25 +16,12 @@ package com.android.settings.wifi; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; - -import static com.android.settings.wifi.WifiDialogActivity.REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER; -import static com.android.settings.wifi.WifiDialogActivity.RESULT_CONNECTED; -import static com.android.settings.wifi.WifiDialogActivity.RESULT_OK; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.content.Intent; -import android.content.pm.PackageManager; import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiManager; import androidx.lifecycle.Lifecycle.State; import androidx.test.core.app.ActivityScenario; @@ -44,7 +31,6 @@ import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowConnectivityManager; import com.android.settings.testutils.shadow.ShadowNetworkDetailsTracker; import com.android.settings.testutils.shadow.ShadowWifiManager; -import com.android.settingslib.wifi.AccessPoint; import com.google.android.setupcompat.util.WizardManagerHelper; @@ -69,25 +55,7 @@ import org.robolectric.util.ReflectionHelpers; }) public class WifiDialogActivityTest { - private static final String CALLING_PACKAGE = "calling_package"; private static final String AP1_SSID = "\"ap1\""; - - @Mock - PackageManager mPackageManager; - @Mock - WifiManager mWifiManager; - @Mock - WifiDialog mWifiDialog; - @Mock - WifiConfiguration mWifiConfiguration; - @Mock - AccessPoint mAccessPoint; - @Mock - WifiDialog2 mWifiDialog2; - @Mock - WifiConfigController2 mWifiConfiguration2; - @Mock - Intent mResultData; @Mock private WifiConfigController mController; @Mock @@ -98,10 +66,6 @@ public class WifiDialogActivityTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mWifiDialog.getController()).thenReturn(mController); - when(mController.getConfig()).thenReturn(mWifiConfiguration); - when(mController.getAccessPoint()).thenReturn(mAccessPoint); - when(mWifiDialog2.getController()).thenReturn(mWifiConfiguration2); WifiConfiguration wifiConfig = new WifiConfiguration(); wifiConfig.SSID = AP1_SSID; @@ -133,52 +97,6 @@ public class WifiDialogActivityTest { assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP1_SSID); } - @Test - public void onSubmit_noPermissionForResult_setResultWithoutData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(false); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog); - - verify(activity).setResult(RESULT_CONNECTED, null); - } - - @Test - public void onSubmit_hasPermissionForResult_setResultWithData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(true); - when(activity.createResultData(any(), any())).thenReturn(mResultData); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog); - - verify(activity).setResult(RESULT_CONNECTED, mResultData); - } - - @Test - public void onSubmit2_noPermissionForResult_setResultWithoutData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(false); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog2); - - verify(activity).setResult(RESULT_CONNECTED, null); - } - - @Test - public void onSubmit2_hasPermissionForResult_setResultWithData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(true); - when(activity.createResultData(any(), any())).thenReturn(mResultData); - when(activity.getSystemService(WifiManager.class)).thenReturn(mWifiManager); - - activity.onSubmit(mWifiDialog2); - - verify(activity).setResult(RESULT_CONNECTED, mResultData); - } - @Test @Ignore public void onSubmit2_whenConnectForCallerIsTrue_shouldConnectToNetwork() { @@ -260,97 +178,4 @@ public class WifiDialogActivityTest { assertThat(dialog.getContext().getThemeResId()) .isEqualTo(R.style.SuwAlertDialogThemeCompat_Light); } - - @Test - public void onActivityResult_noPermissionForResult_setResultWithoutData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(false); - final Intent data = new Intent(); - - activity.onActivityResult(REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER, RESULT_OK, - data); - - verify(activity).setResult(RESULT_CONNECTED); - } - - @Test - public void onActivityResult_hasPermissionForResult_setResultWithData() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.hasPermissionForResult()).thenReturn(true); - final Intent data = new Intent(); - - activity.onActivityResult(REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER, RESULT_OK, - data); - - verify(activity).setResult(RESULT_CONNECTED, data); - } - - @Test - public void hasPermissionForResult_noCallingPackage_returnFalse() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(null); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isFalse(); - } - - @Test - public void hasPermissionForResult_noPermission_returnFalse() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(null); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isFalse(); - } - - @Test - public void hasPermissionForResult_hasCoarseLocationPermission_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } - - @Test - public void hasPermissionForResult_hasFineLocationPermission_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_DENIED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } - - @Test - public void hasPermissionForResult_haveBothLocationPermissions_returnTrue() { - WifiDialogActivity activity = spy(Robolectric.setupActivity(WifiDialogActivity.class)); - when(activity.getCallingPackage()).thenReturn(CALLING_PACKAGE); - when(activity.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.checkPermission(ACCESS_COARSE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, CALLING_PACKAGE)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - - final boolean result = activity.hasPermissionForResult(); - - assertThat(result).isTrue(); - } } From dc150492e1bd608a178f90cb1b52bcecca7f7a2a Mon Sep 17 00:00:00 2001 From: Michael Wachenschwanz Date: Thu, 16 Sep 2021 23:41:34 -0700 Subject: [PATCH 10/21] Update to new method of getting display power profile constants Bug: 200226329 Test: builds and flashes Change-Id: I4abcb609a383224bba1550cfd3e50bb68f58ba72 --- .../fuelgauge/BatteryAppListPreferenceController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java index bf464a8c364..35af4053f86 100644 --- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java @@ -102,7 +102,9 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro } PowerProfile powerProfile = new PowerProfile(context); - return powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL) + // Cheap hack to try to figure out if the power_profile.xml was populated. + return powerProfile.getAveragePowerForOrdinal( + PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0) >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP; } }; From f8d1ee71bff7c59e1f162c4de32337448f5071b4 Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Tue, 5 Oct 2021 17:46:02 -0700 Subject: [PATCH 11/21] BluetoothSecurity: Add BLUETOOTH_PRIVILEGED permission for pairing dialog Bug: 194300867 Test: Build Change-Id: I5a496df50550ea7ee6986c960e28ae3e4a056b4b --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index cf43731830d..4406c9c182d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2202,6 +2202,7 @@ Date: Thu, 30 Sep 2021 10:23:17 +0800 Subject: [PATCH 12/21] [Settings] Adopt comfirm SIM deletion to euicc reset UI There's an "Erase downloaded SIMs" option within reset options UI. When reset EUICC, eSIM profile might get removed. There's a security feature "Confirm SIM deletion" need to be applied in this case. Bug: 194145231 Test: local Change-Id: I1798dfe347be7d0610a12fb79f103efece2ab240 (cherry picked from commit ebca15a8617fe1775b727cea888e13b81707d484) --- .../network/EraseEuiccDataDialogFragment.java | 27 ++++++-- .../ConfirmationSimDeletionPredicate.java | 66 +++++++++++++++++++ ...onfirmSimDeletionPreferenceController.java | 4 +- 3 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 src/com/android/settings/network/helper/ConfirmationSimDeletionPredicate.java diff --git a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java index a141f0c7bab..819d3b1fc6e 100644 --- a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java +++ b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java @@ -32,7 +32,9 @@ import androidx.fragment.app.FragmentManager; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.network.helper.ConfirmationSimDeletionPredicate; import com.android.settings.system.ResetDashboardFragment; +import com.android.settings.wifi.dpp.WifiDppUtils; public class EraseEuiccDataDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener { @@ -73,13 +75,24 @@ public class EraseEuiccDataDialogFragment extends InstrumentedDialogFragment imp } if (which == DialogInterface.BUTTON_POSITIVE) { - AsyncTask.execute(new Runnable() { - @Override - public void run() { - RecoverySystem.wipeEuiccData( - getContext(), PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); - } - }); + if (ConfirmationSimDeletionPredicate.getSingleton().test(getContext())) { + // Create a "verify it's you" verification over keyguard + // when "erase" button been pressed. + // This might protect from erasing by some automation process. + WifiDppUtils.showLockScreen(getContext(), () -> runAsyncWipe()); + } else { + runAsyncWipe(); + } } } + + private void runAsyncWipe() { + AsyncTask.execute(new Runnable() { + @Override + public void run() { + RecoverySystem.wipeEuiccData( + getContext(), PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); + } + }); + } } diff --git a/src/com/android/settings/network/helper/ConfirmationSimDeletionPredicate.java b/src/com/android/settings/network/helper/ConfirmationSimDeletionPredicate.java new file mode 100644 index 00000000000..420f6db6ee1 --- /dev/null +++ b/src/com/android/settings/network/helper/ConfirmationSimDeletionPredicate.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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.network.helper; + +import android.app.KeyguardManager; +import android.content.Context; +import android.provider.Settings; + +import com.android.settings.R; + +import java.util.function.Predicate; + +/** + * {@link Predicate} for detecting the configuration of confirm SIM deletion. + */ +public class ConfirmationSimDeletionPredicate implements Predicate { + + public static final String KEY_CONFIRM_SIM_DELETION = "confirm_sim_deletion"; + + private static final ConfirmationSimDeletionPredicate sSingleton = + new ConfirmationSimDeletionPredicate(); + + // Get singleton of this predicate + public static final ConfirmationSimDeletionPredicate getSingleton() { + return sSingleton; + } + + /** + * Get default configuration of confirm SIM deletion. + * + * @param Context context + * @return the configuration of confirm SIM deletion + */ + private static boolean getDefaultValue(Context context) { + return context.getResources() + .getBoolean(R.bool.config_sim_deletion_confirmation_default_on); + } + + /** + * Get the configuration of confirm SIM deletion. + * + * @param Context context + * @return the configuration of confirm SIM deletion + */ + public boolean test(Context context) { + final KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); + if ((keyguardManager != null) && !keyguardManager.isKeyguardSecure()) { + return false; + } + return Settings.Global.getInt(context.getContentResolver(), KEY_CONFIRM_SIM_DELETION, + getDefaultValue(context) ? 1 : 0) == 1; + } +} diff --git a/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java b/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java index bf807a19715..a7185e3f207 100644 --- a/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java +++ b/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java @@ -26,6 +26,7 @@ import androidx.preference.TwoStatePreference; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settings.network.helper.ConfirmationSimDeletionPredicate; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.overlay.FeatureFactory; import com.android.settings.wifi.dpp.WifiDppUtils; @@ -34,7 +35,8 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** Enable/disable user confirmation before deleting an eSim */ public class ConfirmSimDeletionPreferenceController extends BasePreferenceController implements Preference.OnPreferenceChangeListener{ - public static final String KEY_CONFIRM_SIM_DELETION = "confirm_sim_deletion"; + public static final String KEY_CONFIRM_SIM_DELETION = + ConfirmationSimDeletionPredicate.KEY_CONFIRM_SIM_DELETION; private boolean mConfirmationDefaultOn; private MetricsFeatureProvider mMetricsFeatureProvider; From aebca7f35bf6bf975eeeb443fa8bbeb5d477d642 Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Tue, 5 Oct 2021 17:46:02 -0700 Subject: [PATCH 13/21] BluetoothSecurity: Add BLUETOOTH_PRIVILEGED permission for pairing dialog Bug: 194300867 Test: Build Change-Id: I5a496df50550ea7ee6986c960e28ae3e4a056b4b --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index dbccf6cbe65..fd0bc5409e6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2020,6 +2020,7 @@ android:label="@string/select_webview_provider_dialog_title" /> From 65a63e76b10c56641d9f84940a77822fd5daa99a Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Tue, 5 Oct 2021 17:46:02 -0700 Subject: [PATCH 14/21] BluetoothSecurity: Add BLUETOOTH_PRIVILEGED permission for pairing dialog Bug: 194300867 Test: Build Change-Id: I5a496df50550ea7ee6986c960e28ae3e4a056b4b --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ff3f650f895..8f21b13a6b7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1980,6 +1980,7 @@ android:label="@string/select_webview_provider_dialog_title" /> Date: Tue, 5 Oct 2021 17:46:02 -0700 Subject: [PATCH 15/21] BluetoothSecurity: Add BLUETOOTH_PRIVILEGED permission for pairing dialog Bug: 194300867 Test: Build Change-Id: I5a496df50550ea7ee6986c960e28ae3e4a056b4b --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2f53cc1e7c8..a9f0a3dd7a4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2014,6 +2014,7 @@ Date: Tue, 5 Oct 2021 17:46:02 -0700 Subject: [PATCH 16/21] BluetoothSecurity: Add BLUETOOTH_PRIVILEGED permission for pairing dialog Bug: 194300867 Test: Build Change-Id: I5a496df50550ea7ee6986c960e28ae3e4a056b4b --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ab2fe05353e..cba309ad73d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2017,6 +2017,7 @@ Date: Thu, 7 Oct 2021 02:33:44 +0000 Subject: [PATCH 17/21] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Iaaafbbba20f60a2937e5947b4a90e13fb8a6d3ad --- res/values-ar/strings.xml | 2 +- res/values-as/strings.xml | 2 +- res/values-da/strings.xml | 2 +- res/values-de/strings.xml | 2 +- res/values-es/strings.xml | 10 +++++----- res/values-eu/strings.xml | 4 ++-- res/values-nb/strings.xml | 8 ++++---- res/values-or/strings.xml | 2 +- res/values-ru/strings.xml | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 97bc4518c2d..dfe4d4e1a52 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -1943,7 +1943,7 @@ "استخدام منخفض للبطارية" "‏البحث عن شبكات Wi‑Fi" "‏يمكنك السماح للتطبيقات والخدمات بالبحث عن الأجهزة المجاورة في أي وقت، حتى في حال إيقاف Wi‑Fi. ويمكن استخدام هذه البيانات مثلاً لتحسين الميزات والخدمات المستندة إلى الموقع الجغرافي." - "البحث عن بلوتوث" + "البحث عن أجهزة تتضمن بلوتوث" "يمكنك السماح للتطبيقات والخدمات بالبحث عن الأجهزة المجاورة في أي وقت، حتى في حال إيقاف البلوتوث. ويمكن استخدام هذه البيانات مثلاً لتحسين الميزات والخدمات المستندة إلى الموقع الجغرافي." "خدمات الموقع الجغرافي" "خدمات الموقع الجغرافي" diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index fa3e7f941fa..691c9cbf578 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -4966,7 +4966,7 @@ টা কেমেৰা এপ্‌ টা কেমেৰা এপ্‌ - "কেলণ্ডাৰ এপ্" + "Calendar এপ্" "সম্পৰ্কসূচীৰ এপ্" ইমেইল ক্লাইণ্ট এপসমূহ diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index c27824a4697..a4514a8bde2 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -1429,7 +1429,7 @@ "Løft enheden for at vække den" "Aktiv låseskærm" "Hvornår skal det vises" - "Væk skærmen for notifikationer" + "Væk skærmen ved notifikationer" "Når skærmen er mørk, aktiveres den ved modtagelse af nye notifikationer" "Vis altid tidspunkt og info" "Øget batteriforbrug" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index a6fc794c7e4..affd3e9136a 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -1406,7 +1406,7 @@ "Beim dunklen Design ist der Hintergrund einiger Bildschirme schwarz, damit dein Akku länger hält. Zeitpläne für das dunkle Design werden erst aktiviert, wenn das Display aus ist." "Display automatisch ausschalten" "Display wird ausgeschaltet." - "Nach %1$s ohne Aktivität" + "%1$s ohne Aktivität" "Hintergrund" "Hintergrund & Stil" "Startseite, Sperrbildschirm" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 27305b90fed..4fd9fd5984a 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -1555,8 +1555,8 @@ "Desconectar la tarjeta SD para extraerla de forma segura" "Inserta USB para activar almacenamiento" "Insertar una tarjeta SD para el montaje" - "Activar almacenamiento USB" - "Activar tarjeta SD" + "Montar almacenamiento USB" + "Montar tarjeta SD" "Borrar almacenamiento USB" @@ -1579,7 +1579,7 @@ "No queda espacio de almacenamiento" "Es posible que algunas funciones del sistema, como la sincronización, no funcionen correctamente. Prueba a eliminar o a desactivar elementos para liberar espacio (por ejemplo, aplicaciones o contenido multimedia)." "Cambiar nombre" - "Activar" + "Montar" "Expulsar" "Formatear" "Formatear como portátil" @@ -1606,13 +1606,13 @@ "^1"" ^2""" "En uso de %1$s" "%1$s se ha activado" - "Error al activar %1$s" + "Error al montar %1$s" "%1$s se ha expulsado de forma segura" "No se ha podido expulsar %1$s de forma segura" "%1$s se ha formateado" "Error al formatear %1$s" "Cambiar nombre de almacenamiento" - "Este dispositivo de almacenamiento (^1) se ha extraído de forma segura, pero sigue estando disponible. \n\nPara utilizar el dispositivo (^1), debes activarlo." + "Este dispositivo de almacenamiento (^1) se ha extraído de forma segura, pero sigue estando disponible. \n\nPara utilizar el dispositivo (^1), debes montarlo." "Este dispositivo de almacenamiento (^1) está dañado. \n\nPara utilizar el dispositivo (^1), debes configurarlo." "Después de formatear la unidad, puedes utilizar tu ^1 en otros dispositivos. \n\nTe recomendamos que hagas una copia de seguridad de los datos de tu ^1, ya que se borrarán cuando se formatee. \n\n""Copia de seguridad de fotos y de otro contenido multimedia"" \nMueve tus archivos multimedia a otro espacio de almacenamiento del dispositivo o transfiérelos a un ordenador con un cable USB. \n\n""Copia de seguridad de aplicaciones"" \nSe desinstalarán todas las aplicaciones almacenadas en tu ^1 y se borrarán sus datos. Para conservarlas, muévelas a otro espacio de almacenamiento del dispositivo." "Al extraer tu ^1, las aplicaciones almacenadas en esta unidad dejarán de funcionar y los archivos multimedia guardados en ella no estarán disponibles hasta que se vuelva a insertar."" \n\nTu ^1 se ha formateado para que solo funcione en este dispositivo." diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 5b22e3a8a6b..eddd5f213f1 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -379,8 +379,8 @@ "Konfiguratu berriro aurpegi bidez desblokeatzeko eginbidea" "Hobetu segurtasuna eta errendimendua" "Konfiguratu aurpegi bidez desblokeatzeko eginbidea" - "Ezabatu une honetako aurpegi-eredua aurpegi bidez desblokeatzeko eginbidea berriro konfiguratzeko.\n\nAurpegi-eredua betiko eta modu seguruan ezabatuko da.\n\nEzabatu ondoren, PINa, eredua edo pasahitza beharko duzu telefonoa desblokeatzeko edo aplikazioetan autentifikatzeko." - "Ezabatu une honetako aurpegi-eredua aurpegi bidez desblokeatzeko eginbidea berriro konfiguratzeko.\n\nAurpegi-eredua betiko eta modu seguruan ezabatuko da.\n\nEzabatu ondoren, hatz-marka, PINa, eredua edo pasahitza beharko duzu telefonoa desblokeatzeko edo aplikazioetan autentifikatzeko." + "Aurpegi bidez desblokeatzeko eginbidea berriro konfiguratu nahi baduzu, lehendabizi ezabatu oraingo aurpegi-eredua.\n\nAurpegi-eredua betiko eta modu seguruan ezabatuko da.\n\nEzabatu ondoren, PINa, eredua edo pasahitza beharko duzu telefonoa desblokeatzeko edo aplikazioetan autentifikatzeko." + "Aurpegi bidez desblokeatzeko eginbidea berriro konfiguratu nahi baduzu, lehendabizi ezabatu oraingo aurpegi-eredua.\n\nAurpegi-eredua betiko eta modu seguruan ezabatuko da.\n\nEzabatu ondoren, hatz-marka, PINa, eredua edo pasahitza beharko duzu telefonoa desblokeatzeko edo aplikazioetan autentifikatzeko." "Erabili hauetarako:" "Aurpegi bidez desblokeatzeko eginbidea erabiltzean" "Begiek irekita egon behar dute" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index b447dd7d1cc..995fe412eba 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -2054,8 +2054,8 @@ "Avinstaller" "Avinstaller for alle brukere" "Installer" - "Slå av" - "Slå på" + "Deaktiver" + "Aktivér" "Tøm lagring" "Avinstaller oppdateringer" "Noen aktiviteter du har valgt, åpnes som standard i denne appen." @@ -3260,8 +3260,8 @@ "Et sertifikat mangler. Prøv å redigere profilen." "System" "Bruker" - "Slå av" - "Slå på" + "Deaktiver" + "Aktivér" "Avinstaller" "Angi som pålitelig" "Vil du slå på systemets CA-sertifikat?" diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index 8074aa251f0..b44ea662b9b 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -2250,7 +2250,7 @@ "ସେଟ୍‌ ଅପ୍‌ ଇନ୍‌ପୁଟ୍‌ ପଦ୍ଧତି" "ସେଟିଂସ୍" "ସେଟିଂସ୍" - "%1$s ସେଟିଙ୍ଗ" + "%1$s ସେଟିଂସ" "ସକ୍ରିୟ ଇନ୍‌ପୁଟ୍‌ ପଦ୍ଧତି ବାଛନ୍ତୁ" "ଅନ୍‌ସ୍କ୍ରୀନ୍‌ କୀ’ବୋର୍ଡ ସେଟିଙ୍ଗ" "ଫିଜିକଲ୍ କୀ’ବୋର୍ଡ" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 2cd2475caaa..1b3d9519d8f 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -1899,7 +1899,7 @@ "Низкий расход заряда батареи" "Поиск сетей Wi‑Fi" "Разрешить приложениям и сервисам искать сети Wi-Fi, даже когда Wi-Fi отключен. Это может улучшить работу функций и сервисов, использующих данные о местоположении." - "Поиск Bluetooth-устройств" + "Поиск устройств Bluetooth" "Разрешить приложениям и сервисам искать устройства поблизости, даже когда Bluetooth отключен. Это может улучшить работу функций и сервисов, использующих данные о местоположении." "Геолокация" "Геолокация" @@ -5016,7 +5016,7 @@ "Нажмите, чтобы проверить планшет" "Нажмите, чтобы проверить устройство" "Чтобы увидеть время, уведомления и другую информацию, коснитесь экрана" - "Экстренный вызов" + "Экстренные вызовы" "Использовать функцию \"Экстренный вызов\"" "Под управлением приложения \"%1$s\"" "Быстро нажмите кнопку питания не менее пяти раз для выполнения перечисленных ниже действий" From 9037ceabd314f8d2b55144acffa780e7ba92291f Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Thu, 9 Sep 2021 16:54:01 +0800 Subject: [PATCH 18/21] Support highlightable Settings homepage menu for 2-pane 1. Add a preference group adapter to perform highlighting 2. Add a class for mapping highlighable menu keys and preference keys 3. Add an API to determine if the screen is in split mode Bug: 199017944 Test: manual, build Change-Id: I8e3fe5fb96480a31ee0f3b3afb6ad78999d3d2bc --- AndroidManifest.xml | 132 +++++++++- src/com/android/settings/Settings.java | 11 +- .../android/settings/SettingsActivity.java | 25 +- .../settings/SettingsPreferenceFragment.java | 3 +- src/com/android/settings/Utils.java | 8 + .../ActivityEmbeddingUtils.java | 8 + .../settings/dashboard/CategoryManager.java | 10 + .../dashboard/DashboardFeatureProvider.java | 4 +- .../DashboardFeatureProviderImpl.java | 24 +- .../settings/dashboard/DashboardFragment.java | 6 +- .../profileselector/ProfileSelectDialog.java | 14 +- .../settings/homepage/HighlightableMenu.java | 117 +++++++++ .../homepage/SettingsHomepageActivity.java | 54 +++- .../settings/homepage/TopLevelSettings.java | 99 ++++++++ .../search/SearchFeatureProviderImpl.java | 2 +- ...ighlightableTopLevelPreferenceAdapter.java | 238 ++++++++++++++++++ .../AccountDetailDashboardFragmentTest.java | 7 +- .../DashboardFeatureProviderImplTest.java | 104 ++++---- 18 files changed, 775 insertions(+), 91 deletions(-) create mode 100644 src/com/android/settings/homepage/HighlightableMenu.java create mode 100644 src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a34713dce6c..b26831c236d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -136,7 +136,7 @@ android:taskAffinity="com.android.settings.root" android:launchMode="singleTask" android:exported="true" - android:configChanges="keyboard|keyboardHidden|screenSize|screenLayout"> + android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout"> @@ -228,6 +228,8 @@ + @@ -257,6 +259,8 @@ + @@ -314,6 +318,8 @@ + @@ -479,6 +485,8 @@ + @@ -489,6 +497,8 @@ android:clearTaskOnLaunch="true"> + + + + + + + + @@ -618,6 +642,8 @@ android:targetActivity="Settings$TetherSettingsActivity"> + + + @@ -661,6 +691,8 @@ + + @@ -858,6 +892,8 @@ + @@ -911,6 +947,8 @@ + @@ -921,6 +959,8 @@ android:exported="true"> + + @@ -991,6 +1033,8 @@ android:value="com.android.settings.notification.zen.ZenModeEventRuleSettings" /> + + @@ -1028,6 +1074,8 @@ + + @@ -1065,6 +1115,8 @@ + @@ -1088,6 +1140,8 @@ android:value="true" /> + + @@ -1353,6 +1409,8 @@ + + @@ -1629,6 +1689,8 @@ + @@ -1644,6 +1706,8 @@ + @@ -1693,6 +1757,8 @@ + @@ -1711,6 +1777,8 @@ + @@ -1725,6 +1793,8 @@ + @@ -1739,6 +1809,8 @@ + @@ -1753,6 +1825,8 @@ + @@ -2424,6 +2498,8 @@ + + + + + + + + + + + + @@ -2816,6 +2914,8 @@ + @@ -2830,6 +2930,8 @@ + + @@ -2888,6 +2992,8 @@ + @@ -3300,6 +3406,8 @@ android:value="true" /> + + @@ -3361,6 +3471,8 @@ android:exported="true"> + + @@ -3393,6 +3507,8 @@ + + @@ -3576,6 +3694,8 @@ + @@ -3716,6 +3836,8 @@ + + + + @@ -3776,7 +3904,7 @@ android:permission="android.permission.BLUETOOTH_CONNECT"> - /> + = getMinCurrentScreenSplitWidthPx(appContext) + && dm.heightPixels >= getMinSmallestScreenSplitWidthPx(appContext); + } } diff --git a/src/com/android/settings/dashboard/CategoryManager.java b/src/com/android/settings/dashboard/CategoryManager.java index 2a82abe4859..1559a423471 100644 --- a/src/com/android/settings/dashboard/CategoryManager.java +++ b/src/com/android/settings/dashboard/CategoryManager.java @@ -17,6 +17,7 @@ package com.android.settings.dashboard; import android.content.ComponentName; import android.content.Context; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -24,6 +25,7 @@ import android.util.Pair; import androidx.annotation.VisibleForTesting; +import com.android.settings.homepage.HighlightableMenu; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.DashboardCategory; @@ -153,6 +155,14 @@ public class CategoryManager { filterDuplicateTiles(mCategoryByKeyMap); if (firstLoading) { logTiles(context); + for (Tile tile : mCategoryByKeyMap.get(CategoryKey.CATEGORY_HOMEPAGE).getTiles()) { + final String key = tile.getKey(context); + if (TextUtils.isEmpty(key)) { + Log.w(TAG, "Key hint missing for homepage tile: " + tile.getTitle(context)); + continue; + } + HighlightableMenu.addMenuKey(key); + } } } } diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java index 8c872f0b8b0..b88ecd4240b 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java @@ -47,9 +47,9 @@ public interface DashboardFeatureProvider { * Binds preference to data provided by tile and gets dynamic data observers. * * @param activity If tile contains intent to launch, it will be launched from this activity + * @param fragment The fragment that the preference will be bound to * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to * rounded icon. - * @param sourceMetricsCategory The context (source) from which an action is performed * @param pref The preference to bind data * @param tile The binding data * @param key They key for preference. If null, we will generate one from tile data @@ -58,7 +58,7 @@ public interface DashboardFeatureProvider { * @return The list of dynamic data observers */ List bindPreferenceToTileAndGetObservers(FragmentActivity activity, - boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile, + DashboardFragment fragment, boolean forceRoundedIcon, Preference pref, Tile tile, String key, int baseOrder); /** diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index 4581f6caba8..4efe03d4326 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -36,6 +36,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITL import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface.OnCancelListener; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.PackageManager; @@ -60,6 +61,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.dashboard.profileselector.ProfileSelectDialog; +import com.android.settings.homepage.TopLevelSettings; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.PrimarySwitchPreference; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; @@ -123,7 +125,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { @Override public List bindPreferenceToTileAndGetObservers(FragmentActivity activity, - boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile, + DashboardFragment fragment, boolean forceRoundedIcon, Preference pref, Tile tile, String key, int baseOrder) { if (pref == null) { return null; @@ -149,6 +151,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { bindIcon(pref, tile, forceRoundedIcon); if (tile instanceof ActivityTile) { + final int sourceMetricsCategory = fragment.getMetricsCategory(); final Bundle metadata = tile.getMetaData(); String clsName = null; String action = null; @@ -166,7 +169,17 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { intent.setAction(action); } pref.setOnPreferenceClickListener(preference -> { - launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory); + OnCancelListener listener = null; + if (fragment instanceof TopLevelSettings) { + final TopLevelSettings topLevelSettings = (TopLevelSettings) fragment; + // Highlight the tile immediately whenever it's clicked + topLevelSettings.setHighlightPreferenceKey(key); + // If the tile allows users to select profile, the pop-op dialog may be + // cancelled and then the previous highlight entry should be restored. + listener = dialog -> topLevelSettings.restorePreviousHighlight(); + } + launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory, + listener); return true; }); } @@ -198,7 +211,8 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { .putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, SettingsEnums.DASHBOARD_SUMMARY) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY); + launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY, + /* listener= */ null); } private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) { @@ -413,7 +427,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent, - int sourceMetricCategory) { + int sourceMetricCategory, OnCancelListener listener) { if (!isIntentResolvable(intent)) { Log.w(TAG, "Cannot resolve intent, skipping. " + intent); return; @@ -439,7 +453,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile, - sourceMetricCategory); + sourceMetricCategory, listener); } } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index dfd931db7e9..4317fc6ef40 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -496,15 +496,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment if (mDashboardTilePrefKeys.containsKey(key)) { // Have the key already, will rebind. final Preference preference = screen.findPreference(key); - mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), - forceRoundedIcons, getMetricsCategory(), preference, tile, key, + mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), this, + forceRoundedIcons, preference, tile, key, mPlaceholderPreferenceController.getOrder()); } else { // Don't have this key, add it. final Preference pref = createPreference(tile); final List observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), - forceRoundedIcons, getMetricsCategory(), pref, tile, key, + this, forceRoundedIcons, pref, tile, key, mPlaceholderPreferenceController.getOrder()); screen.addPreference(pref); registerDynamicDataObservers(observers); diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java index 36cdd422508..b05f23b812b 100644 --- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java +++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java @@ -19,6 +19,7 @@ package com.android.settings.dashboard.profileselector; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.os.Bundle; @@ -44,19 +45,23 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen private int mSourceMetricCategory; private Tile mSelectedTile; + private OnCancelListener mOnCancelListener; /** * Display the profile select dialog, adding the fragment to the given FragmentManager. * @param manager The FragmentManager this fragment will be added to. * @param tile The tile for this fragment. * @param sourceMetricCategory The source metric category. + * @param listener The listener listens to the dialog cancelling event. */ - public static void show(FragmentManager manager, Tile tile, int sourceMetricCategory) { + public static void show(FragmentManager manager, Tile tile, int sourceMetricCategory, + OnCancelListener listener) { final ProfileSelectDialog dialog = new ProfileSelectDialog(); final Bundle args = new Bundle(); args.putParcelable(ARG_SELECTED_TILE, tile); args.putInt(ARG_SOURCE_METRIC_CATEGORY, sourceMetricCategory); dialog.setArguments(args); + dialog.mOnCancelListener = listener; dialog.show(manager, "select_profile"); } @@ -91,6 +96,13 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen getActivity().startActivityAsUser(intent, user); } + @Override + public void onCancel(DialogInterface dialog) { + if (mOnCancelListener != null) { + mOnCancelListener.onCancel(dialog); + } + } + public static void updateUserHandlesIfNeeded(Context context, Tile tile) { final List userHandles = tile.userHandle; if (tile.userHandle == null || tile.userHandle.size() <= 1) { diff --git a/src/com/android/settings/homepage/HighlightableMenu.java b/src/com/android/settings/homepage/HighlightableMenu.java new file mode 100644 index 00000000000..6a10e66dd6a --- /dev/null +++ b/src/com/android/settings/homepage/HighlightableMenu.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 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.homepage; + +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_HIGHLIGHTABLE_MENU_KEY; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; + +import android.annotation.XmlRes; +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.settings.core.PreferenceXmlParserUtils; +import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Class for mapping highlightable menu keys and preference keys + */ +public class HighlightableMenu { + private static final String TAG = "HighlightableMenu"; + + /** + * Map from highlightable menu key to preference key. + */ + private static final Map MENU_TO_PREFERENCE_KEY_MAP; + + /** + * Map from old menu key to current key string id. + */ + private static final Map MENU_KEY_COMPAT_MAP; + + private static boolean sXmlParsed; + + static { + MENU_TO_PREFERENCE_KEY_MAP = new ArrayMap<>(); + MENU_KEY_COMPAT_MAP = new ArrayMap<>(); + + // Manual mapping for platform compatibility, e.g. + // MENU_KEY_COMPAT_MAP.put("top_level_apps_and_notifs", R.string.menu_key_apps); + } + + /** Parses the highlightable menu keys from xml */ + public static synchronized void fromXml(Context context, @XmlRes int xmlResId) { + if (sXmlParsed) { + return; + } + + Log.d(TAG, "parsing highlightable menu from xml"); + final List preferenceMetadata; + try { + preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, + MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_HIGHLIGHTABLE_MENU_KEY); + } catch (IOException | XmlPullParserException e) { + Log.e(TAG, "Failed to parse preference xml for getting highlightable menu keys", e); + return; + } + + for (Bundle metadata : preferenceMetadata) { + final String menuKey = metadata.getString(METADATA_HIGHLIGHTABLE_MENU_KEY); + if (TextUtils.isEmpty(menuKey)) { + continue; + } + final String prefKey = metadata.getString(METADATA_KEY); + if (TextUtils.isEmpty(prefKey)) { + Log.w(TAG, "Highlightable menu requires android:key but it's missing in xml: " + + menuKey); + continue; + } + MENU_TO_PREFERENCE_KEY_MAP.put(menuKey, prefKey); + } + + if (MENU_TO_PREFERENCE_KEY_MAP.isEmpty()) { + return; + } + + sXmlParsed = true; + MENU_KEY_COMPAT_MAP.forEach((compatMenuKey, keyId) -> { + final String prefKey = lookupPreferenceKey(context.getString(keyId)); + if (prefKey != null) { + MENU_TO_PREFERENCE_KEY_MAP.put(compatMenuKey, prefKey); + } + }); + } + + /** Manually adds a preference as the menu key for Injection */ + public static synchronized void addMenuKey(String key) { + Log.d(TAG, "add menu key: " + key); + MENU_TO_PREFERENCE_KEY_MAP.put(key, key); + } + + /** Looks up the preference key by a specified menu key */ + public static String lookupPreferenceKey(String menuKey) { + return MENU_TO_PREFERENCE_KEY_MAP.get(menuKey); + } +} diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index d6a8a927527..e48c82fd79e 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -16,13 +16,15 @@ package com.android.settings.homepage; +import static android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK; +import static android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI; +import static android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY; + import android.animation.LayoutTransition; import android.app.ActivityManager; -import android.app.PendingIntent; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Bundle; import android.text.TextUtils; import android.util.FeatureFlagUtils; @@ -36,16 +38,16 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.window.embedding.SplitController; import com.android.settings.R; import com.android.settings.Settings; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.accounts.AvatarViewMixin; -import com.android.settings.core.CategoryMixin; -import com.android.settings.core.FeatureFlags; import com.android.settings.activityembedding.ActivityEmbeddingRulesController; import com.android.settings.activityembedding.ActivityEmbeddingUtils; +import com.android.settings.core.CategoryMixin; +import com.android.settings.core.FeatureFlags; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; @@ -64,6 +66,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements // An alias class name of SettingsHomepageActivity. public static final String ALIAS_DEEP_LINK = "com.android.settings.DeepLinkHomepageActivity"; + private static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network; private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300; private View mHomepageView; @@ -120,7 +123,11 @@ public class SettingsHomepageActivity extends FragmentActivity implements showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content); } } - showFragment(new TopLevelSettings(), R.id.main_content); + final Fragment fragment = new TopLevelSettings(); + fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, + getHighlightMenuKey()); + showFragment(fragment, R.id.main_content); + ((FrameLayout) findViewById(R.id.main_content)) .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); @@ -132,9 +139,13 @@ public class SettingsHomepageActivity extends FragmentActivity implements protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - // When it's large screen 2-pane and Settings app is in background. Receiving a Intent - // in this Activity will not finish nor onCreate. setIntent here for this case. + // When it's large screen 2-pane and Settings app is in the background, receiving an Intent + // will not recreate this activity. Update the intent for this case. setIntent(intent); + reloadHighlightMenuKey(); + if (isFinishing()) { + return; + } // Launch the intent from deep link for large screen devices. launchDeepLinkIntentToRight(); } @@ -180,12 +191,12 @@ public class SettingsHomepageActivity extends FragmentActivity implements final Intent intent = getIntent(); if (intent == null || !TextUtils.equals(intent.getAction(), - android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { + ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { return; } final String intentUriString = intent.getStringExtra( - android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI); + EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI); if (TextUtils.isEmpty(intentUriString)) { Log.e(TAG, "No EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI to deep link"); finish(); @@ -233,6 +244,29 @@ public class SettingsHomepageActivity extends FragmentActivity implements startActivity(targetIntent); } + private String getHighlightMenuKey() { + final Intent intent = getIntent(); + if (intent != null && TextUtils.equals(intent.getAction(), + ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { + final String menuKey = intent.getStringExtra( + EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY); + if (!TextUtils.isEmpty(menuKey)) { + return menuKey; + } + } + return getString(DEFAULT_HIGHLIGHT_MENU_KEY); + } + + private void reloadHighlightMenuKey() { + final TopLevelSettings fragment = + (TopLevelSettings) getSupportFragmentManager().findFragmentById(R.id.main_content); + if (fragment != null) { + fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, + getHighlightMenuKey()); + fragment.reloadHighlightMenuKey(); + } + } + private void initHomepageContainer() { final View view = findViewById(R.id.homepage_container); // Prevent inner RecyclerView gets focus and invokes scrolling. diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java index 681ea5111c7..a819b870540 100644 --- a/src/com/android/settings/homepage/TopLevelSettings.java +++ b/src/com/android/settings/homepage/TopLevelSettings.java @@ -21,20 +21,27 @@ import static com.android.settingslib.search.SearchIndexable.MOBILE; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; +import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.support.SupportPreferenceController; +import com.android.settings.widget.HighlightableTopLevelPreferenceAdapter; import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.search.SearchIndexable; @@ -43,6 +50,11 @@ public class TopLevelSettings extends DashboardFragment implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { private static final String TAG = "TopLevelSettings"; + private static final String SAVED_HIGHLIGHTED_PREF = "highlighted_pref"; + + private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter; + + private String mHighlightedPreferenceKey; public TopLevelSettings() { final Bundle args = new Bundle(); @@ -69,6 +81,7 @@ public class TopLevelSettings extends DashboardFragment implements @Override public void onAttach(Context context) { super.onAttach(context); + HighlightableMenu.fromXml(context, getPreferenceScreenResId()); use(SupportPreferenceController.class).setActivity(getActivity()); } @@ -83,6 +96,12 @@ public class TopLevelSettings extends DashboardFragment implements return this; } + @Override + public boolean onPreferenceTreeClick(Preference preference) { + setHighlightPreferenceKey(preference.getKey()); + return super.onPreferenceTreeClick(preference); + } + @Override public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) { new SubSettingLauncher(getActivity()) @@ -96,6 +115,20 @@ public class TopLevelSettings extends DashboardFragment implements return true; } + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + if (icicle != null) { + mHighlightedPreferenceKey = icicle.getString(SAVED_HIGHLIGHTED_PREF); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_HIGHLIGHTED_PREF, mHighlightedPreferenceKey); + } + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { super.onCreatePreferences(savedInstanceState, rootKey); @@ -118,12 +151,78 @@ public class TopLevelSettings extends DashboardFragment implements } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + highlightPreferenceIfNeeded(); + } + + @Override + public void highlightPreferenceIfNeeded() { + if (mTopLevelAdapter != null) { + mTopLevelAdapter.requestHighlight(); + } + } + + /** Highlight a preference with specified key */ + public void setHighlightPreferenceKey(String prefKey) { + if (mTopLevelAdapter != null) { + mHighlightedPreferenceKey = prefKey; + mTopLevelAdapter.highlightPreference(prefKey, /* scrollNeeded= */ false); + } + } + + /** Highlight the previous preference */ + public void restorePreviousHighlight() { + if (mTopLevelAdapter != null) { + mTopLevelAdapter.restorePreviousHighlight(); + } + } + @Override protected boolean shouldForceRoundedIcon() { return getContext().getResources() .getBoolean(R.bool.config_force_rounded_icon_TopLevelSettings); } + @Override + protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { + if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext())) { + return super.onCreateAdapter(preferenceScreen); + } + + if (TextUtils.isEmpty(mHighlightedPreferenceKey)) { + mHighlightedPreferenceKey = getHighlightPrefKeyFromArguments(); + } + + Log.d(TAG, "onCreateAdapter, pref key: " + mHighlightedPreferenceKey); + mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(preferenceScreen, + getListView(), mHighlightedPreferenceKey); + return mTopLevelAdapter; + } + + void reloadHighlightMenuKey() { + if (mTopLevelAdapter == null) { + return; + } + + mHighlightedPreferenceKey = getHighlightPrefKeyFromArguments(); + Log.d(TAG, "reloadHighlightMenuKey, pref key: " + mHighlightedPreferenceKey); + mTopLevelAdapter.highlightPreference(mHighlightedPreferenceKey, /* scrollNeeded= */ true); + } + + private String getHighlightPrefKeyFromArguments() { + final Bundle arguments = getArguments(); + final String menuKey = arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY); + final String prefKey = HighlightableMenu.lookupPreferenceKey(menuKey); + if (TextUtils.isEmpty(prefKey)) { + Log.e(TAG, "Invalid highlight menu key: " + menuKey); + } else { + Log.d(TAG, "Menu key: " + menuKey); + } + return prefKey; + } + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.top_level_settings) { diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java index 508d37d7e6a..6f909709058 100644 --- a/src/com/android/settings/search/SearchFeatureProviderImpl.java +++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java @@ -50,7 +50,7 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider { if (isSettingsPackage || isAllowlistedPackage) { return; } - throw new SecurityException("Search result intents must be called with from a " + throw new SecurityException("Search result intents must be called with from an " + "allowlisted package."); } diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java new file mode 100644 index 00000000000..eeaad214a21 --- /dev/null +++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2021 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.widget; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceGroupAdapter; +import androidx.preference.PreferenceViewHolder; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.Utils; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; + +/** + * Adapter for highlighting top level preferences + */ +public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapter { + + private static final String TAG = "HighlightableTopLevelAdapter"; + + static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L; + + @VisibleForTesting + final int mHighlightColor; + final int mTitleColorNormal; + final int mTitleColorHighlight; + final int mSummaryColorNormal; + final int mSummaryColorHighlight; + final int mIconColorNormal; + final int mIconColorHighlight; + + private final Context mContext; + private final RecyclerView mRecyclerView; + private final int mNormalBackgroundRes; + private String mHighlightKey; + private String mPreviousHighlightKey; + private int mHighlightPosition = RecyclerView.NO_POSITION; + private boolean mHighlightNeeded; + private boolean mScrolled; + + public HighlightableTopLevelPreferenceAdapter(PreferenceGroup preferenceGroup, + RecyclerView recyclerView, String key) { + super(preferenceGroup); + mRecyclerView = recyclerView; + mHighlightKey = key; + mContext = preferenceGroup.getContext(); + final TypedValue outValue = new TypedValue(); + mContext.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true /* resolveRefs */); + mNormalBackgroundRes = outValue.resourceId; + mHighlightColor = Utils.getColorAttrDefaultColor(mContext, + com.android.internal.R.attr.colorAccentSecondaryVariant); + mTitleColorNormal = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimary); + mTitleColorHighlight = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimaryInverse); + mSummaryColorNormal = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorSecondary); + mSummaryColorHighlight = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorSecondaryInverse); + mIconColorNormal = Utils.getHomepageIconColor(mContext); + mIconColorHighlight = Utils.getHomepageIconColorHighlight(mContext); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder, int position) { + super.onBindViewHolder(holder, position); + updateBackground(holder, position); + } + + @VisibleForTesting + void updateBackground(PreferenceViewHolder holder, int position) { + if (!isHighlightNeeded()) { + removeHighlightBackground(holder); + return; + } + + if (position == mHighlightPosition + && mHighlightKey != null + && TextUtils.equals(mHighlightKey, getItem(position).getKey())) { + // This position should be highlighted. + addHighlightBackground(holder); + } else { + removeHighlightBackground(holder); + } + } + + /** + * A function can highlight a specific setting in recycler view. + */ + public void requestHighlight() { + if (mRecyclerView == null || TextUtils.isEmpty(mHighlightKey)) { + return; + } + + if (TextUtils.isEmpty(mHighlightKey)) { + // De-highlight previous preference. + final int previousPosition = mHighlightPosition; + mHighlightPosition = RecyclerView.NO_POSITION; + mScrolled = true; + if (previousPosition >= 0) { + notifyItemChanged(previousPosition); + } + return; + } + + final int position = getPreferenceAdapterPosition(mHighlightKey); + if (position < 0) { + return; + } + + final boolean highlightNeeded = isHighlightNeeded(); + if (highlightNeeded) { + scrollToPositionIfNeeded(position); + } + + // Turn on/off highlight when screen split mode is changed. + if (highlightNeeded != mHighlightNeeded) { + Log.d(TAG, "Highlight change needed: " + highlightNeeded); + mHighlightNeeded = highlightNeeded; + mHighlightPosition = position; + notifyItemChanged(position); + return; + } + + if (position == mHighlightPosition) { + return; + } + + final int previousPosition = mHighlightPosition; + mHighlightPosition = position; + Log.d(TAG, "Request highlight position " + position); + Log.d(TAG, "Is highlight needed: " + highlightNeeded); + if (!highlightNeeded) { + return; + } + + // Highlight preference. + notifyItemChanged(position); + + // De-highlight previous preference. + if (previousPosition >= 0) { + notifyItemChanged(previousPosition); + } + } + + /** + * A function that highlights a setting by specifying a preference key. Usually used whenever a + * preference is clicked. + */ + public void highlightPreference(String key, boolean scrollNeeded) { + mPreviousHighlightKey = mHighlightKey; + mHighlightKey = key; + mScrolled = !scrollNeeded; + requestHighlight(); + } + + /** + * A function that restores the previous highlighted setting. + */ + public void restorePreviousHighlight() { + mHighlightKey = mPreviousHighlightKey; + requestHighlight(); + } + + private void scrollToPositionIfNeeded(int position) { + if (mScrolled || position < 0) { + return; + } + + // Only when the recyclerView is loaded, it can be scrolled + final View view = mRecyclerView.getChildAt(position); + if (view == null) { + mRecyclerView.postDelayed(() -> scrollToPositionIfNeeded(position), + DELAY_HIGHLIGHT_DURATION_MILLIS); + return; + } + + mScrolled = true; + Log.d(TAG, "Scroll to position " + position); + // Scroll to the top to reset the position. + mRecyclerView.nestedScrollBy(0, -mRecyclerView.getHeight()); + + final int scrollY = view.getTop(); + if (scrollY > 0) { + mRecyclerView.nestedScrollBy(0, scrollY); + } + } + + private void addHighlightBackground(PreferenceViewHolder holder) { + final View v = holder.itemView; + v.setBackgroundColor(mHighlightColor); + ((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorHighlight); + ((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorHighlight); + final Drawable drawable = ((ImageView) v.findViewById(android.R.id.icon)).getDrawable(); + if (drawable != null) { + drawable.setTint(mIconColorHighlight); + } + } + + private void removeHighlightBackground(PreferenceViewHolder holder) { + final View v = holder.itemView; + v.setBackgroundResource(mNormalBackgroundRes); + ((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorNormal); + ((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorNormal); + final Drawable drawable = ((ImageView) v.findViewById(android.R.id.icon)).getDrawable(); + if (drawable != null) { + drawable.setTint(mIconColorNormal); + } + } + + private boolean isHighlightNeeded() { + return ActivityEmbeddingUtils.isTwoPaneResolution(mContext); + } +} diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java index a827284a088..cccca9c9ea5 100644 --- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java @@ -43,7 +43,6 @@ import android.os.UserManager; import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; -import com.android.internal.logging.nano.MetricsProto; import com.android.settings.dashboard.DashboardFeatureProviderImpl; import com.android.settings.testutils.shadow.ShadowAccountManager; import com.android.settings.testutils.shadow.ShadowUserManager; @@ -151,9 +150,9 @@ public class AccountDetailDashboardFragmentTest { final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class); final Preference preference = new Preference(mContext); - dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity, - false /* forceRoundedIcon */, MetricsProto.MetricsEvent.DASHBOARD_SUMMARY, - preference, tile, null /* key */, Preference.DEFAULT_ORDER); + dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity, mFragment, + false /* forceRoundedIcon */, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); preference.performClick(); diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java index 5d6fdf98f1b..e7c99c873b1 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java @@ -114,6 +114,7 @@ public class DashboardFeatureProviderImplTest { private Bundle mSwitchMetaData; private DashboardFeatureProviderImpl mImpl; private boolean mForceRoundedIcon; + private DashboardFragment mFragment; @Before public void setUp() { @@ -144,6 +145,7 @@ public class DashboardFeatureProviderImplTest { .thenReturn(new ResolveInfo()); mFeatureFactory = FakeFeatureFactory.setupForTest(); mImpl = new DashboardFeatureProviderImpl(mContext); + mFragment = new TestFragment(); } @Test @@ -159,8 +161,8 @@ public class DashboardFeatureProviderImplTest { doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) .when(tile).getIcon(any(Context.class)); mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); assertThat(preference.getSummary()) @@ -180,8 +182,8 @@ public class DashboardFeatureProviderImplTest { doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) .when(tile).getIcon(any(Context.class)); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, preference, tile, - null /* key*/, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key*/, + Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); assertThat(preference.getSummary()) @@ -198,8 +200,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getFragment()).isNull(); assertThat(preference.getOnPreferenceClickListener()).isNotNull(); @@ -214,8 +216,8 @@ public class DashboardFeatureProviderImplTest { tile.userHandle.add(mock(UserHandle.class)); tile.userHandle.add(mock(UserHandle.class)); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mActivity).getSupportFragmentManager(); @@ -231,8 +233,8 @@ public class DashboardFeatureProviderImplTest { when(mActivity.getSystemService(Context.USER_SERVICE)) .thenReturn(mUserManager); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( @@ -250,8 +252,8 @@ public class DashboardFeatureProviderImplTest { tile.userHandle = new ArrayList<>(); tile.userHandle.add(mock(UserHandle.class)); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( any(Intent.class), @@ -263,8 +265,8 @@ public class DashboardFeatureProviderImplTest { @Test public void bindPreference_nullPreference_shouldIgnore() { final Tile tile = mock(Tile.class); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, null, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + null /* keys */, tile, "123", Preference.DEFAULT_ORDER); verifyZeroInteractions(tile); } @@ -273,8 +275,8 @@ public class DashboardFeatureProviderImplTest { public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() { final Preference preference = new Preference(RuntimeEnvironment.application); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isNotNull(); @@ -288,9 +290,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); assertThat(preference.getSummary()).isNull(); } @@ -304,8 +305,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, uriString); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, - null /*key */, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); @@ -320,8 +321,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, uriString); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, - null /*key */, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); @@ -336,9 +337,8 @@ public class DashboardFeatureProviderImplTest { final Bundle bundle = new Bundle(); bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, false); ShadowTileUtils.setResultBundle(bundle); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); preference.callChangeListener(false); @@ -358,9 +358,8 @@ public class DashboardFeatureProviderImplTest { final Bundle bundle = new Bundle(); bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, true); ShadowTileUtils.setResultBundle(bundle); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); preference.callChangeListener(true); @@ -378,8 +377,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, mSwitchMetaData); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, - null /*key */, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); ShadowTileUtils.setProviderChecked(false); observers.get(0).onDataChanged(); @@ -397,9 +396,8 @@ public class DashboardFeatureProviderImplTest { final Preference preference = new Preference(RuntimeEnvironment.application); mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /* key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); } @@ -483,8 +481,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", baseOrder); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", baseOrder); assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder); } @@ -496,8 +494,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(testOrder); } @@ -508,8 +506,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello"); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); } @@ -522,8 +520,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); tile.userHandle = null; - mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.performClick(); ShadowActivity shadowActivity = Shadows.shadowOf(activity); @@ -546,8 +544,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); tile.userHandle = null; - mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.performClick(); final ShadowActivity.IntentForResult launchIntent = @@ -669,4 +667,22 @@ public class DashboardFeatureProviderImplTest { assertThat(argument.getValue().getIdentifier()).isEqualTo(0); verify(mActivity, never()).getSupportFragmentManager(); } + + private static class TestFragment extends DashboardFragment { + + @Override + public int getMetricsCategory() { + return MetricsEvent.SETTINGS_GESTURES; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.gestures; + } + + @Override + protected String getLogTag() { + return "TestFragment"; + } + } } From 3648d672019c4d9d72bdaf57a5a9f91011190943 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Fri, 8 Oct 2021 16:07:46 +0800 Subject: [PATCH 19/21] Change DeepLinkHomepageActivity enable state for ACTION_USER_INITIALIZE Each user has different components enable state, should update enable state of DeepLinkHomepageActivity for Intent#ACTION_USER_INITIALIZE. Bug: 202364984 Test: manual Switch user -> long click Launcher -> Wallpaper & style See if it shows wallpaper app's Activity. Change-Id: If50de0f017f5cd606ba0cbf88d41eeabf3f47506 --- src/com/android/settings/SettingsInitialize.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/com/android/settings/SettingsInitialize.java b/src/com/android/settings/SettingsInitialize.java index e527ae131df..500ce779ef5 100644 --- a/src/com/android/settings/SettingsInitialize.java +++ b/src/com/android/settings/SettingsInitialize.java @@ -34,7 +34,6 @@ import android.content.pm.ShortcutManager; import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; -import android.text.TextUtils; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -150,10 +149,6 @@ public class SettingsInitialize extends BroadcastReceiver { } private void enableTwoPaneDeepLinkActivityIfNecessary(PackageManager pm, Intent intent) { - if (!TextUtils.equals(intent.getAction(), Intent.ACTION_PRE_BOOT_COMPLETED)) { - return; - } - final ComponentName deepLinkHome = new ComponentName(Utils.SETTINGS_PACKAGE_NAME, SettingsHomepageActivity.ALIAS_DEEP_LINK); final int enableState = SplitController.getInstance().isSplitSupported() From 28cdf73cf73c56b05327b822946955627c2dfe13 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Mon, 27 Sep 2021 23:06:02 +0800 Subject: [PATCH 20/21] [Large screen] Set alwaysExpand for SearchActivity. Set alwaysExpand and SearchActivity will show in full screen. Bug: 196923591 Test: manual Settings -> Search settings Change-Id: I4174753bce15232342fa69d0845225a95d757782 --- .../ActivityEmbeddingRulesController.java | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java index e1360fd4996..e9dcea19471 100644 --- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java +++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java @@ -17,6 +17,7 @@ package com.android.settings.activityembedding; import android.app.Activity; +import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,6 +26,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.window.embedding.ActivityFilter; +import androidx.window.embedding.ActivityRule; import androidx.window.embedding.SplitController; import androidx.window.embedding.SplitPairFilter; import androidx.window.embedding.SplitPairRule; @@ -34,6 +36,7 @@ import com.android.settings.Settings; import com.android.settings.SubSettings; import com.android.settings.Utils; import com.android.settings.homepage.SettingsHomepageActivity; +import com.android.settings.overlay.FeatureFactory; import java.util.HashSet; import java.util.Set; @@ -70,6 +73,8 @@ public class ActivityEmbeddingRulesController { null /* secondaryIntentAction */, true /* finishPrimaryWithSecondary */, true /* finishSecondaryWithPrimary */); + // Set rules here to show full screen for specified Activity. + registerAlwaysExpandRule(); } /** Register a SplitPairRule for 2-pane. */ @@ -85,21 +90,31 @@ public class ActivityEmbeddingRulesController { SplitController.getInstance().registerRule(new SplitPairRule(filters, finishPrimaryWithSecondary, - finishSecondaryWithPrimary, true /* clearTop */, + finishSecondaryWithPrimary, + true /* clearTop */, ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(context), ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(context), ActivityEmbeddingUtils.SPLIT_RATIO, LayoutDirection.LOCALE)); } + private void registerAlwaysExpandRule() { + final Set activityFilters = new HashSet<>(); + + final Intent searchIntent = FeatureFactory.getFactory(mContext).getSearchFeatureProvider() + .buildSearchIntent(mContext, SettingsEnums.SETTINGS_HOMEPAGE); + addActivityFilter(activityFilters, searchIntent); + + mSplitController.registerRule(new ActivityRule(activityFilters, true /* alwaysExpand */)); + } + private void registerHomepagePlaceholderRule() { final Set activityFilters = new HashSet<>(); - activityFilters.add(new ActivityFilter(getComponentName(SettingsHomepageActivity.class), - null /* intentAction */)); - activityFilters.add(new ActivityFilter(getComponentName(Settings.class), - null /* intentAction */)); - activityFilters.add(new ActivityFilter(new ComponentName(Utils.SETTINGS_PACKAGE_NAME, - SettingsHomepageActivity.ALIAS_DEEP_LINK), null /* intentAction */)); + addActivityFilter(activityFilters, SettingsHomepageActivity.class); + addActivityFilter(activityFilters, Settings.class); + addActivityFilter(activityFilters, new ComponentName(Utils.SETTINGS_PACKAGE_NAME, + SettingsHomepageActivity.ALIAS_DEEP_LINK)); + final Intent intent = new Intent(); intent.setComponent(getComponentName(Settings.NetworkDashboardActivity.class)); final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule( @@ -113,6 +128,22 @@ public class ActivityEmbeddingRulesController { mSplitController.registerRule(placeholderRule); } + private void addActivityFilter(Set activityFilters, + Class activityClass) { + activityFilters.add(new ActivityFilter(getComponentName(activityClass), + null /* intentAction */)); + } + + private void addActivityFilter(Set activityFilters, Intent intent) { + activityFilters.add(new ActivityFilter(new ComponentName("*" /* pkg */, "*" /* cls */), + intent.getAction())); + } + + private void addActivityFilter(Set activityFilters, + ComponentName componentName) { + activityFilters.add(new ActivityFilter(componentName, null /* intentAction */)); + } + @NonNull private ComponentName getComponentName(Class activityClass) { return new ComponentName(mContext.getPackageName(), activityClass.getName()); From 096c090b805acbed1ea5a45f584ff5a24bd2fa3b Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Fri, 8 Oct 2021 14:29:06 +0000 Subject: [PATCH 21/21] Revert "Support highlightable Settings homepage menu for 2-pane" This reverts commit 9037ceabd314f8d2b55144acffa780e7ba92291f. Reason for revert: Presumed cause of b/202510128 Change-Id: I0daaab95d9bc57229c0c8c916cbe3ada7aafb6fa --- AndroidManifest.xml | 132 +--------- src/com/android/settings/Settings.java | 11 +- .../android/settings/SettingsActivity.java | 25 +- .../settings/SettingsPreferenceFragment.java | 3 +- src/com/android/settings/Utils.java | 8 - .../ActivityEmbeddingUtils.java | 8 - .../settings/dashboard/CategoryManager.java | 10 - .../dashboard/DashboardFeatureProvider.java | 4 +- .../DashboardFeatureProviderImpl.java | 24 +- .../settings/dashboard/DashboardFragment.java | 6 +- .../profileselector/ProfileSelectDialog.java | 14 +- .../settings/homepage/HighlightableMenu.java | 117 --------- .../homepage/SettingsHomepageActivity.java | 54 +--- .../settings/homepage/TopLevelSettings.java | 99 -------- .../search/SearchFeatureProviderImpl.java | 2 +- ...ighlightableTopLevelPreferenceAdapter.java | 238 ------------------ .../AccountDetailDashboardFragmentTest.java | 7 +- .../DashboardFeatureProviderImplTest.java | 104 ++++---- 18 files changed, 91 insertions(+), 775 deletions(-) delete mode 100644 src/com/android/settings/homepage/HighlightableMenu.java delete mode 100644 src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b26831c236d..a34713dce6c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -136,7 +136,7 @@ android:taskAffinity="com.android.settings.root" android:launchMode="singleTask" android:exported="true" - android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout"> + android:configChanges="keyboard|keyboardHidden|screenSize|screenLayout"> @@ -228,8 +228,6 @@ - @@ -259,8 +257,6 @@ - @@ -318,8 +314,6 @@ - @@ -485,8 +479,6 @@ - @@ -497,8 +489,6 @@ android:clearTaskOnLaunch="true"> - - - - - - - - @@ -642,8 +618,6 @@ android:targetActivity="Settings$TetherSettingsActivity"> - - - @@ -691,8 +661,6 @@ - - @@ -892,8 +858,6 @@ - @@ -947,8 +911,6 @@ - @@ -959,8 +921,6 @@ android:exported="true"> - - @@ -1033,8 +991,6 @@ android:value="com.android.settings.notification.zen.ZenModeEventRuleSettings" /> - - @@ -1074,8 +1028,6 @@ - - @@ -1115,8 +1065,6 @@ - @@ -1140,8 +1088,6 @@ android:value="true" /> - - @@ -1409,8 +1353,6 @@ - - @@ -1689,8 +1629,6 @@ - @@ -1706,8 +1644,6 @@ - @@ -1757,8 +1693,6 @@ - @@ -1777,8 +1711,6 @@ - @@ -1793,8 +1725,6 @@ - @@ -1809,8 +1739,6 @@ - @@ -1825,8 +1753,6 @@ - @@ -2498,8 +2424,6 @@ - - - - - - - - - - - - @@ -2914,8 +2816,6 @@ - @@ -2930,8 +2830,6 @@ - - @@ -2992,8 +2888,6 @@ - @@ -3406,8 +3300,6 @@ android:value="true" /> - - @@ -3471,8 +3361,6 @@ android:exported="true"> - - @@ -3507,8 +3393,6 @@ - - @@ -3694,8 +3576,6 @@ - @@ -3836,8 +3716,6 @@ - - - - @@ -3904,7 +3776,7 @@ android:permission="android.permission.BLUETOOTH_CONNECT"> - + /> = getMinCurrentScreenSplitWidthPx(appContext) - && dm.heightPixels >= getMinSmallestScreenSplitWidthPx(appContext); - } } diff --git a/src/com/android/settings/dashboard/CategoryManager.java b/src/com/android/settings/dashboard/CategoryManager.java index 1559a423471..2a82abe4859 100644 --- a/src/com/android/settings/dashboard/CategoryManager.java +++ b/src/com/android/settings/dashboard/CategoryManager.java @@ -17,7 +17,6 @@ package com.android.settings.dashboard; import android.content.ComponentName; import android.content.Context; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -25,7 +24,6 @@ import android.util.Pair; import androidx.annotation.VisibleForTesting; -import com.android.settings.homepage.HighlightableMenu; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.DashboardCategory; @@ -155,14 +153,6 @@ public class CategoryManager { filterDuplicateTiles(mCategoryByKeyMap); if (firstLoading) { logTiles(context); - for (Tile tile : mCategoryByKeyMap.get(CategoryKey.CATEGORY_HOMEPAGE).getTiles()) { - final String key = tile.getKey(context); - if (TextUtils.isEmpty(key)) { - Log.w(TAG, "Key hint missing for homepage tile: " + tile.getTitle(context)); - continue; - } - HighlightableMenu.addMenuKey(key); - } } } } diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java index b88ecd4240b..8c872f0b8b0 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java @@ -47,9 +47,9 @@ public interface DashboardFeatureProvider { * Binds preference to data provided by tile and gets dynamic data observers. * * @param activity If tile contains intent to launch, it will be launched from this activity - * @param fragment The fragment that the preference will be bound to * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to * rounded icon. + * @param sourceMetricsCategory The context (source) from which an action is performed * @param pref The preference to bind data * @param tile The binding data * @param key They key for preference. If null, we will generate one from tile data @@ -58,7 +58,7 @@ public interface DashboardFeatureProvider { * @return The list of dynamic data observers */ List bindPreferenceToTileAndGetObservers(FragmentActivity activity, - DashboardFragment fragment, boolean forceRoundedIcon, Preference pref, Tile tile, + boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder); /** diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index 4efe03d4326..4581f6caba8 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -36,7 +36,6 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITL import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface.OnCancelListener; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.PackageManager; @@ -61,7 +60,6 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.dashboard.profileselector.ProfileSelectDialog; -import com.android.settings.homepage.TopLevelSettings; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.PrimarySwitchPreference; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; @@ -125,7 +123,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { @Override public List bindPreferenceToTileAndGetObservers(FragmentActivity activity, - DashboardFragment fragment, boolean forceRoundedIcon, Preference pref, Tile tile, + boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder) { if (pref == null) { return null; @@ -151,7 +149,6 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { bindIcon(pref, tile, forceRoundedIcon); if (tile instanceof ActivityTile) { - final int sourceMetricsCategory = fragment.getMetricsCategory(); final Bundle metadata = tile.getMetaData(); String clsName = null; String action = null; @@ -169,17 +166,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { intent.setAction(action); } pref.setOnPreferenceClickListener(preference -> { - OnCancelListener listener = null; - if (fragment instanceof TopLevelSettings) { - final TopLevelSettings topLevelSettings = (TopLevelSettings) fragment; - // Highlight the tile immediately whenever it's clicked - topLevelSettings.setHighlightPreferenceKey(key); - // If the tile allows users to select profile, the pop-op dialog may be - // cancelled and then the previous highlight entry should be restored. - listener = dialog -> topLevelSettings.restorePreviousHighlight(); - } - launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory, - listener); + launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory); return true; }); } @@ -211,8 +198,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { .putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, SettingsEnums.DASHBOARD_SUMMARY) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY, - /* listener= */ null); + launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY); } private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) { @@ -427,7 +413,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent, - int sourceMetricCategory, OnCancelListener listener) { + int sourceMetricCategory) { if (!isIntentResolvable(intent)) { Log.w(TAG, "Cannot resolve intent, skipping. " + intent); return; @@ -453,7 +439,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile, - sourceMetricCategory, listener); + sourceMetricCategory); } } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index 4317fc6ef40..dfd931db7e9 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -496,15 +496,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment if (mDashboardTilePrefKeys.containsKey(key)) { // Have the key already, will rebind. final Preference preference = screen.findPreference(key); - mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), this, - forceRoundedIcons, preference, tile, key, + mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), + forceRoundedIcons, getMetricsCategory(), preference, tile, key, mPlaceholderPreferenceController.getOrder()); } else { // Don't have this key, add it. final Preference pref = createPreference(tile); final List observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), - this, forceRoundedIcons, pref, tile, key, + forceRoundedIcons, getMetricsCategory(), pref, tile, key, mPlaceholderPreferenceController.getOrder()); screen.addPreference(pref); registerDynamicDataObservers(observers); diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java index b05f23b812b..36cdd422508 100644 --- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java +++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java @@ -19,7 +19,6 @@ package com.android.settings.dashboard.profileselector; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.os.Bundle; @@ -45,23 +44,19 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen private int mSourceMetricCategory; private Tile mSelectedTile; - private OnCancelListener mOnCancelListener; /** * Display the profile select dialog, adding the fragment to the given FragmentManager. * @param manager The FragmentManager this fragment will be added to. * @param tile The tile for this fragment. * @param sourceMetricCategory The source metric category. - * @param listener The listener listens to the dialog cancelling event. */ - public static void show(FragmentManager manager, Tile tile, int sourceMetricCategory, - OnCancelListener listener) { + public static void show(FragmentManager manager, Tile tile, int sourceMetricCategory) { final ProfileSelectDialog dialog = new ProfileSelectDialog(); final Bundle args = new Bundle(); args.putParcelable(ARG_SELECTED_TILE, tile); args.putInt(ARG_SOURCE_METRIC_CATEGORY, sourceMetricCategory); dialog.setArguments(args); - dialog.mOnCancelListener = listener; dialog.show(manager, "select_profile"); } @@ -96,13 +91,6 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen getActivity().startActivityAsUser(intent, user); } - @Override - public void onCancel(DialogInterface dialog) { - if (mOnCancelListener != null) { - mOnCancelListener.onCancel(dialog); - } - } - public static void updateUserHandlesIfNeeded(Context context, Tile tile) { final List userHandles = tile.userHandle; if (tile.userHandle == null || tile.userHandle.size() <= 1) { diff --git a/src/com/android/settings/homepage/HighlightableMenu.java b/src/com/android/settings/homepage/HighlightableMenu.java deleted file mode 100644 index 6a10e66dd6a..00000000000 --- a/src/com/android/settings/homepage/HighlightableMenu.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2021 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.homepage; - -import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_HIGHLIGHTABLE_MENU_KEY; -import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; - -import android.annotation.XmlRes; -import android.content.Context; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.settings.core.PreferenceXmlParserUtils; -import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; - -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -/** - * Class for mapping highlightable menu keys and preference keys - */ -public class HighlightableMenu { - private static final String TAG = "HighlightableMenu"; - - /** - * Map from highlightable menu key to preference key. - */ - private static final Map MENU_TO_PREFERENCE_KEY_MAP; - - /** - * Map from old menu key to current key string id. - */ - private static final Map MENU_KEY_COMPAT_MAP; - - private static boolean sXmlParsed; - - static { - MENU_TO_PREFERENCE_KEY_MAP = new ArrayMap<>(); - MENU_KEY_COMPAT_MAP = new ArrayMap<>(); - - // Manual mapping for platform compatibility, e.g. - // MENU_KEY_COMPAT_MAP.put("top_level_apps_and_notifs", R.string.menu_key_apps); - } - - /** Parses the highlightable menu keys from xml */ - public static synchronized void fromXml(Context context, @XmlRes int xmlResId) { - if (sXmlParsed) { - return; - } - - Log.d(TAG, "parsing highlightable menu from xml"); - final List preferenceMetadata; - try { - preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, - MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_HIGHLIGHTABLE_MENU_KEY); - } catch (IOException | XmlPullParserException e) { - Log.e(TAG, "Failed to parse preference xml for getting highlightable menu keys", e); - return; - } - - for (Bundle metadata : preferenceMetadata) { - final String menuKey = metadata.getString(METADATA_HIGHLIGHTABLE_MENU_KEY); - if (TextUtils.isEmpty(menuKey)) { - continue; - } - final String prefKey = metadata.getString(METADATA_KEY); - if (TextUtils.isEmpty(prefKey)) { - Log.w(TAG, "Highlightable menu requires android:key but it's missing in xml: " - + menuKey); - continue; - } - MENU_TO_PREFERENCE_KEY_MAP.put(menuKey, prefKey); - } - - if (MENU_TO_PREFERENCE_KEY_MAP.isEmpty()) { - return; - } - - sXmlParsed = true; - MENU_KEY_COMPAT_MAP.forEach((compatMenuKey, keyId) -> { - final String prefKey = lookupPreferenceKey(context.getString(keyId)); - if (prefKey != null) { - MENU_TO_PREFERENCE_KEY_MAP.put(compatMenuKey, prefKey); - } - }); - } - - /** Manually adds a preference as the menu key for Injection */ - public static synchronized void addMenuKey(String key) { - Log.d(TAG, "add menu key: " + key); - MENU_TO_PREFERENCE_KEY_MAP.put(key, key); - } - - /** Looks up the preference key by a specified menu key */ - public static String lookupPreferenceKey(String menuKey) { - return MENU_TO_PREFERENCE_KEY_MAP.get(menuKey); - } -} diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index e48c82fd79e..d6a8a927527 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -16,15 +16,13 @@ package com.android.settings.homepage; -import static android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK; -import static android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI; -import static android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY; - import android.animation.LayoutTransition; import android.app.ActivityManager; +import android.app.PendingIntent; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; import android.text.TextUtils; import android.util.FeatureFlagUtils; @@ -38,16 +36,16 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.window.embedding.SplitController; import com.android.settings.R; import com.android.settings.Settings; -import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.accounts.AvatarViewMixin; -import com.android.settings.activityembedding.ActivityEmbeddingRulesController; -import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.core.CategoryMixin; import com.android.settings.core.FeatureFlags; +import com.android.settings.activityembedding.ActivityEmbeddingRulesController; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; @@ -66,7 +64,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements // An alias class name of SettingsHomepageActivity. public static final String ALIAS_DEEP_LINK = "com.android.settings.DeepLinkHomepageActivity"; - private static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network; private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300; private View mHomepageView; @@ -123,11 +120,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content); } } - final Fragment fragment = new TopLevelSettings(); - fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, - getHighlightMenuKey()); - showFragment(fragment, R.id.main_content); - + showFragment(new TopLevelSettings(), R.id.main_content); ((FrameLayout) findViewById(R.id.main_content)) .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); @@ -139,13 +132,9 @@ public class SettingsHomepageActivity extends FragmentActivity implements protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - // When it's large screen 2-pane and Settings app is in the background, receiving an Intent - // will not recreate this activity. Update the intent for this case. + // When it's large screen 2-pane and Settings app is in background. Receiving a Intent + // in this Activity will not finish nor onCreate. setIntent here for this case. setIntent(intent); - reloadHighlightMenuKey(); - if (isFinishing()) { - return; - } // Launch the intent from deep link for large screen devices. launchDeepLinkIntentToRight(); } @@ -191,12 +180,12 @@ public class SettingsHomepageActivity extends FragmentActivity implements final Intent intent = getIntent(); if (intent == null || !TextUtils.equals(intent.getAction(), - ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { + android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { return; } final String intentUriString = intent.getStringExtra( - EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI); + android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI); if (TextUtils.isEmpty(intentUriString)) { Log.e(TAG, "No EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI to deep link"); finish(); @@ -244,29 +233,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements startActivity(targetIntent); } - private String getHighlightMenuKey() { - final Intent intent = getIntent(); - if (intent != null && TextUtils.equals(intent.getAction(), - ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { - final String menuKey = intent.getStringExtra( - EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY); - if (!TextUtils.isEmpty(menuKey)) { - return menuKey; - } - } - return getString(DEFAULT_HIGHLIGHT_MENU_KEY); - } - - private void reloadHighlightMenuKey() { - final TopLevelSettings fragment = - (TopLevelSettings) getSupportFragmentManager().findFragmentById(R.id.main_content); - if (fragment != null) { - fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, - getHighlightMenuKey()); - fragment.reloadHighlightMenuKey(); - } - } - private void initHomepageContainer() { final View view = findViewById(R.id.homepage_container); // Prevent inner RecyclerView gets focus and invokes scrolling. diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java index a819b870540..681ea5111c7 100644 --- a/src/com/android/settings/homepage/TopLevelSettings.java +++ b/src/com/android/settings/homepage/TopLevelSettings.java @@ -21,27 +21,20 @@ import static com.android.settingslib.search.SearchIndexable.MOBILE; import android.app.settings.SettingsEnums; import android.content.Context; -import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; -import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; -import com.android.settings.SettingsActivity; import com.android.settings.Utils; -import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.support.SupportPreferenceController; -import com.android.settings.widget.HighlightableTopLevelPreferenceAdapter; import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.search.SearchIndexable; @@ -50,11 +43,6 @@ public class TopLevelSettings extends DashboardFragment implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { private static final String TAG = "TopLevelSettings"; - private static final String SAVED_HIGHLIGHTED_PREF = "highlighted_pref"; - - private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter; - - private String mHighlightedPreferenceKey; public TopLevelSettings() { final Bundle args = new Bundle(); @@ -81,7 +69,6 @@ public class TopLevelSettings extends DashboardFragment implements @Override public void onAttach(Context context) { super.onAttach(context); - HighlightableMenu.fromXml(context, getPreferenceScreenResId()); use(SupportPreferenceController.class).setActivity(getActivity()); } @@ -96,12 +83,6 @@ public class TopLevelSettings extends DashboardFragment implements return this; } - @Override - public boolean onPreferenceTreeClick(Preference preference) { - setHighlightPreferenceKey(preference.getKey()); - return super.onPreferenceTreeClick(preference); - } - @Override public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) { new SubSettingLauncher(getActivity()) @@ -115,20 +96,6 @@ public class TopLevelSettings extends DashboardFragment implements return true; } - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - if (icicle != null) { - mHighlightedPreferenceKey = icicle.getString(SAVED_HIGHLIGHTED_PREF); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(SAVED_HIGHLIGHTED_PREF, mHighlightedPreferenceKey); - } - @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { super.onCreatePreferences(savedInstanceState, rootKey); @@ -151,78 +118,12 @@ public class TopLevelSettings extends DashboardFragment implements } } - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - highlightPreferenceIfNeeded(); - } - - @Override - public void highlightPreferenceIfNeeded() { - if (mTopLevelAdapter != null) { - mTopLevelAdapter.requestHighlight(); - } - } - - /** Highlight a preference with specified key */ - public void setHighlightPreferenceKey(String prefKey) { - if (mTopLevelAdapter != null) { - mHighlightedPreferenceKey = prefKey; - mTopLevelAdapter.highlightPreference(prefKey, /* scrollNeeded= */ false); - } - } - - /** Highlight the previous preference */ - public void restorePreviousHighlight() { - if (mTopLevelAdapter != null) { - mTopLevelAdapter.restorePreviousHighlight(); - } - } - @Override protected boolean shouldForceRoundedIcon() { return getContext().getResources() .getBoolean(R.bool.config_force_rounded_icon_TopLevelSettings); } - @Override - protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { - if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext())) { - return super.onCreateAdapter(preferenceScreen); - } - - if (TextUtils.isEmpty(mHighlightedPreferenceKey)) { - mHighlightedPreferenceKey = getHighlightPrefKeyFromArguments(); - } - - Log.d(TAG, "onCreateAdapter, pref key: " + mHighlightedPreferenceKey); - mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(preferenceScreen, - getListView(), mHighlightedPreferenceKey); - return mTopLevelAdapter; - } - - void reloadHighlightMenuKey() { - if (mTopLevelAdapter == null) { - return; - } - - mHighlightedPreferenceKey = getHighlightPrefKeyFromArguments(); - Log.d(TAG, "reloadHighlightMenuKey, pref key: " + mHighlightedPreferenceKey); - mTopLevelAdapter.highlightPreference(mHighlightedPreferenceKey, /* scrollNeeded= */ true); - } - - private String getHighlightPrefKeyFromArguments() { - final Bundle arguments = getArguments(); - final String menuKey = arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY); - final String prefKey = HighlightableMenu.lookupPreferenceKey(menuKey); - if (TextUtils.isEmpty(prefKey)) { - Log.e(TAG, "Invalid highlight menu key: " + menuKey); - } else { - Log.d(TAG, "Menu key: " + menuKey); - } - return prefKey; - } - public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.top_level_settings) { diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java index 6f909709058..508d37d7e6a 100644 --- a/src/com/android/settings/search/SearchFeatureProviderImpl.java +++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java @@ -50,7 +50,7 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider { if (isSettingsPackage || isAllowlistedPackage) { return; } - throw new SecurityException("Search result intents must be called with from an " + throw new SecurityException("Search result intents must be called with from a " + "allowlisted package."); } diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java deleted file mode 100644 index eeaad214a21..00000000000 --- a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2021 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.widget; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.util.Log; -import android.util.TypedValue; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.PreferenceGroup; -import androidx.preference.PreferenceGroupAdapter; -import androidx.preference.PreferenceViewHolder; -import androidx.recyclerview.widget.RecyclerView; - -import com.android.settings.Utils; -import com.android.settings.activityembedding.ActivityEmbeddingUtils; - -/** - * Adapter for highlighting top level preferences - */ -public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapter { - - private static final String TAG = "HighlightableTopLevelAdapter"; - - static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L; - - @VisibleForTesting - final int mHighlightColor; - final int mTitleColorNormal; - final int mTitleColorHighlight; - final int mSummaryColorNormal; - final int mSummaryColorHighlight; - final int mIconColorNormal; - final int mIconColorHighlight; - - private final Context mContext; - private final RecyclerView mRecyclerView; - private final int mNormalBackgroundRes; - private String mHighlightKey; - private String mPreviousHighlightKey; - private int mHighlightPosition = RecyclerView.NO_POSITION; - private boolean mHighlightNeeded; - private boolean mScrolled; - - public HighlightableTopLevelPreferenceAdapter(PreferenceGroup preferenceGroup, - RecyclerView recyclerView, String key) { - super(preferenceGroup); - mRecyclerView = recyclerView; - mHighlightKey = key; - mContext = preferenceGroup.getContext(); - final TypedValue outValue = new TypedValue(); - mContext.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, - outValue, true /* resolveRefs */); - mNormalBackgroundRes = outValue.resourceId; - mHighlightColor = Utils.getColorAttrDefaultColor(mContext, - com.android.internal.R.attr.colorAccentSecondaryVariant); - mTitleColorNormal = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.textColorPrimary); - mTitleColorHighlight = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.textColorPrimaryInverse); - mSummaryColorNormal = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.textColorSecondary); - mSummaryColorHighlight = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.textColorSecondaryInverse); - mIconColorNormal = Utils.getHomepageIconColor(mContext); - mIconColorHighlight = Utils.getHomepageIconColorHighlight(mContext); - } - - @Override - public void onBindViewHolder(PreferenceViewHolder holder, int position) { - super.onBindViewHolder(holder, position); - updateBackground(holder, position); - } - - @VisibleForTesting - void updateBackground(PreferenceViewHolder holder, int position) { - if (!isHighlightNeeded()) { - removeHighlightBackground(holder); - return; - } - - if (position == mHighlightPosition - && mHighlightKey != null - && TextUtils.equals(mHighlightKey, getItem(position).getKey())) { - // This position should be highlighted. - addHighlightBackground(holder); - } else { - removeHighlightBackground(holder); - } - } - - /** - * A function can highlight a specific setting in recycler view. - */ - public void requestHighlight() { - if (mRecyclerView == null || TextUtils.isEmpty(mHighlightKey)) { - return; - } - - if (TextUtils.isEmpty(mHighlightKey)) { - // De-highlight previous preference. - final int previousPosition = mHighlightPosition; - mHighlightPosition = RecyclerView.NO_POSITION; - mScrolled = true; - if (previousPosition >= 0) { - notifyItemChanged(previousPosition); - } - return; - } - - final int position = getPreferenceAdapterPosition(mHighlightKey); - if (position < 0) { - return; - } - - final boolean highlightNeeded = isHighlightNeeded(); - if (highlightNeeded) { - scrollToPositionIfNeeded(position); - } - - // Turn on/off highlight when screen split mode is changed. - if (highlightNeeded != mHighlightNeeded) { - Log.d(TAG, "Highlight change needed: " + highlightNeeded); - mHighlightNeeded = highlightNeeded; - mHighlightPosition = position; - notifyItemChanged(position); - return; - } - - if (position == mHighlightPosition) { - return; - } - - final int previousPosition = mHighlightPosition; - mHighlightPosition = position; - Log.d(TAG, "Request highlight position " + position); - Log.d(TAG, "Is highlight needed: " + highlightNeeded); - if (!highlightNeeded) { - return; - } - - // Highlight preference. - notifyItemChanged(position); - - // De-highlight previous preference. - if (previousPosition >= 0) { - notifyItemChanged(previousPosition); - } - } - - /** - * A function that highlights a setting by specifying a preference key. Usually used whenever a - * preference is clicked. - */ - public void highlightPreference(String key, boolean scrollNeeded) { - mPreviousHighlightKey = mHighlightKey; - mHighlightKey = key; - mScrolled = !scrollNeeded; - requestHighlight(); - } - - /** - * A function that restores the previous highlighted setting. - */ - public void restorePreviousHighlight() { - mHighlightKey = mPreviousHighlightKey; - requestHighlight(); - } - - private void scrollToPositionIfNeeded(int position) { - if (mScrolled || position < 0) { - return; - } - - // Only when the recyclerView is loaded, it can be scrolled - final View view = mRecyclerView.getChildAt(position); - if (view == null) { - mRecyclerView.postDelayed(() -> scrollToPositionIfNeeded(position), - DELAY_HIGHLIGHT_DURATION_MILLIS); - return; - } - - mScrolled = true; - Log.d(TAG, "Scroll to position " + position); - // Scroll to the top to reset the position. - mRecyclerView.nestedScrollBy(0, -mRecyclerView.getHeight()); - - final int scrollY = view.getTop(); - if (scrollY > 0) { - mRecyclerView.nestedScrollBy(0, scrollY); - } - } - - private void addHighlightBackground(PreferenceViewHolder holder) { - final View v = holder.itemView; - v.setBackgroundColor(mHighlightColor); - ((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorHighlight); - ((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorHighlight); - final Drawable drawable = ((ImageView) v.findViewById(android.R.id.icon)).getDrawable(); - if (drawable != null) { - drawable.setTint(mIconColorHighlight); - } - } - - private void removeHighlightBackground(PreferenceViewHolder holder) { - final View v = holder.itemView; - v.setBackgroundResource(mNormalBackgroundRes); - ((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorNormal); - ((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorNormal); - final Drawable drawable = ((ImageView) v.findViewById(android.R.id.icon)).getDrawable(); - if (drawable != null) { - drawable.setTint(mIconColorNormal); - } - } - - private boolean isHighlightNeeded() { - return ActivityEmbeddingUtils.isTwoPaneResolution(mContext); - } -} diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java index cccca9c9ea5..a827284a088 100644 --- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java @@ -43,6 +43,7 @@ import android.os.UserManager; import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; +import com.android.internal.logging.nano.MetricsProto; import com.android.settings.dashboard.DashboardFeatureProviderImpl; import com.android.settings.testutils.shadow.ShadowAccountManager; import com.android.settings.testutils.shadow.ShadowUserManager; @@ -150,9 +151,9 @@ public class AccountDetailDashboardFragmentTest { final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class); final Preference preference = new Preference(mContext); - dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity, mFragment, - false /* forceRoundedIcon */, preference, tile, null /* key */, - Preference.DEFAULT_ORDER); + dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity, + false /* forceRoundedIcon */, MetricsProto.MetricsEvent.DASHBOARD_SUMMARY, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); preference.performClick(); diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java index e7c99c873b1..5d6fdf98f1b 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java @@ -114,7 +114,6 @@ public class DashboardFeatureProviderImplTest { private Bundle mSwitchMetaData; private DashboardFeatureProviderImpl mImpl; private boolean mForceRoundedIcon; - private DashboardFragment mFragment; @Before public void setUp() { @@ -145,7 +144,6 @@ public class DashboardFeatureProviderImplTest { .thenReturn(new ResolveInfo()); mFeatureFactory = FakeFeatureFactory.setupForTest(); mImpl = new DashboardFeatureProviderImpl(mContext); - mFragment = new TestFragment(); } @Test @@ -161,8 +159,8 @@ public class DashboardFeatureProviderImplTest { doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) .when(tile).getIcon(any(Context.class)); mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); assertThat(preference.getSummary()) @@ -182,8 +180,8 @@ public class DashboardFeatureProviderImplTest { doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) .when(tile).getIcon(any(Context.class)); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key*/, - Preference.DEFAULT_ORDER); + mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, preference, tile, + null /* key*/, Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); assertThat(preference.getSummary()) @@ -200,8 +198,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getFragment()).isNull(); assertThat(preference.getOnPreferenceClickListener()).isNotNull(); @@ -216,8 +214,8 @@ public class DashboardFeatureProviderImplTest { tile.userHandle.add(mock(UserHandle.class)); tile.userHandle.add(mock(UserHandle.class)); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mActivity).getSupportFragmentManager(); @@ -233,8 +231,8 @@ public class DashboardFeatureProviderImplTest { when(mActivity.getSystemService(Context.USER_SERVICE)) .thenReturn(mUserManager); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( @@ -252,8 +250,8 @@ public class DashboardFeatureProviderImplTest { tile.userHandle = new ArrayList<>(); tile.userHandle.add(mock(UserHandle.class)); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( any(Intent.class), @@ -265,8 +263,8 @@ public class DashboardFeatureProviderImplTest { @Test public void bindPreference_nullPreference_shouldIgnore() { final Tile tile = mock(Tile.class); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - null /* keys */, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, null, tile, "123", Preference.DEFAULT_ORDER); verifyZeroInteractions(tile); } @@ -275,8 +273,8 @@ public class DashboardFeatureProviderImplTest { public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() { final Preference preference = new Preference(RuntimeEnvironment.application); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, null /* key */, + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isNotNull(); @@ -290,8 +288,9 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, null /* key */, Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, + Preference.DEFAULT_ORDER); assertThat(preference.getSummary()).isNull(); } @@ -305,8 +304,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, uriString); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, - Preference.DEFAULT_ORDER); + mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, + null /*key */, Preference.DEFAULT_ORDER); assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); @@ -321,8 +320,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, uriString); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, - Preference.DEFAULT_ORDER); + mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, + null /*key */, Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); @@ -337,8 +336,9 @@ public class DashboardFeatureProviderImplTest { final Bundle bundle = new Bundle(); bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, false); ShadowTileUtils.setResultBundle(bundle); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, null /* key */, Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, + Preference.DEFAULT_ORDER); preference.callChangeListener(false); @@ -358,8 +358,9 @@ public class DashboardFeatureProviderImplTest { final Bundle bundle = new Bundle(); bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, true); ShadowTileUtils.setResultBundle(bundle); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, null /* key */, Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, + Preference.DEFAULT_ORDER); preference.callChangeListener(true); @@ -377,8 +378,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, mSwitchMetaData); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, - Preference.DEFAULT_ORDER); + mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, + null /*key */, Preference.DEFAULT_ORDER); ShadowTileUtils.setProviderChecked(false); observers.get(0).onDataChanged(); @@ -396,8 +397,9 @@ public class DashboardFeatureProviderImplTest { final Preference preference = new Preference(RuntimeEnvironment.application); mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, null /* key */, Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); } @@ -481,8 +483,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", baseOrder); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", baseOrder); assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder); } @@ -494,8 +496,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(testOrder); } @@ -506,8 +508,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello"); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, + MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); } @@ -520,8 +522,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); tile.userHandle = null; - mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon, + MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); preference.performClick(); ShadowActivity shadowActivity = Shadows.shadowOf(activity); @@ -544,8 +546,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); tile.userHandle = null; - mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, - preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon, + MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); preference.performClick(); final ShadowActivity.IntentForResult launchIntent = @@ -667,22 +669,4 @@ public class DashboardFeatureProviderImplTest { assertThat(argument.getValue().getIdentifier()).isEqualTo(0); verify(mActivity, never()).getSupportFragmentManager(); } - - private static class TestFragment extends DashboardFragment { - - @Override - public int getMetricsCategory() { - return MetricsEvent.SETTINGS_GESTURES; - } - - @Override - protected int getPreferenceScreenResId() { - return R.xml.gestures; - } - - @Override - protected String getLogTag() { - return "TestFragment"; - } - } }