From 43f9adea620edad452210ce56712d16ce93bbb8b Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Mon, 1 Jun 2020 10:31:46 +0100 Subject: [PATCH 1/3] Improved UX when no debuggable apps are available. In AppPicker, add an extra to exclude the "Nothing" sentinel. This is used when selecting a debug app, and makes sense in that context. However it does not makes sense when choosing an app to modify compatibilty options for, so exclude it. Also update the AppPicker to return a new result code in the case that there are no apps available which match the criteria given. This result code can only be used when the new extra is defined which keep this change low risk. In PlatformCompatDashboard, use the new extra and handle the new result code, showing a dialog to explain that only debuggable apps are shown in user builds. This also results in the "Nothing" item being shown at the top of the app list. Bug: 157633308 Test: Manual Change-Id: Ifb055dd7c030cda42556bca8a9d7e87606f0ff72 --- res/values/strings.xml | 4 ++++ .../android/settings/development/AppPicker.java | 14 +++++++++++--- .../compat/PlatformCompatDashboard.java | 12 +++++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 366254f2bc7..38676a12f4e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -11267,6 +11267,10 @@ Default disabled changes Enabled for targetSdkVersion > %d + + No apps available + + App compatibility changes can only be modified for debuggable apps. Install a debuggable app and try again. diff --git a/src/com/android/settings/development/AppPicker.java b/src/com/android/settings/development/AppPicker.java index d819bc23fec..7854de22c93 100644 --- a/src/com/android/settings/development/AppPicker.java +++ b/src/com/android/settings/development/AppPicker.java @@ -46,10 +46,14 @@ public class AppPicker extends ListActivity { = "com.android.settings.extra.REQUESTIING_PERMISSION"; public static final String EXTRA_DEBUGGABLE = "com.android.settings.extra.DEBUGGABLE"; public static final String EXTRA_NON_SYSTEM = "com.android.settings.extra.NON_SYSTEM"; + public static final String EXTRA_INCLUDE_NOTHING = "com.android.settings.extra.INCLUDE_NOTHING"; + + public static final int RESULT_NO_MATCHING_APPS = -2; private String mPermissionName; private boolean mDebuggableOnly; private boolean mNonSystemOnly; + private boolean mIncludeNothing; @Override protected void onCreate(Bundle icicle) { @@ -58,9 +62,11 @@ public class AppPicker extends ListActivity { mPermissionName = getIntent().getStringExtra(EXTRA_REQUESTIING_PERMISSION); mDebuggableOnly = getIntent().getBooleanExtra(EXTRA_DEBUGGABLE, false); mNonSystemOnly = getIntent().getBooleanExtra(EXTRA_NON_SYSTEM, false); + mIncludeNothing = getIntent().getBooleanExtra(EXTRA_INCLUDE_NOTHING, true); mAdapter = new AppListAdapter(this); if (mAdapter.getCount() <= 0) { + setResult(RESULT_NO_MATCHING_APPS); finish(); } else { setListAdapter(mAdapter); @@ -140,9 +146,11 @@ public class AppPicker extends ListActivity { mPackageInfoList.add(info); } Collections.sort(mPackageInfoList, sDisplayNameComparator); - MyApplicationInfo info = new MyApplicationInfo(); - info.label = context.getText(R.string.no_application); - mPackageInfoList.add(0, info); + if (mIncludeNothing) { + MyApplicationInfo info = new MyApplicationInfo(); + info.label = context.getText(R.string.no_application); + mPackageInfoList.add(0, info); + } addAll(mPackageInfoList); } diff --git a/src/com/android/settings/development/compat/PlatformCompatDashboard.java b/src/com/android/settings/development/compat/PlatformCompatDashboard.java index 2f2c7509851..fe64948e905 100644 --- a/src/com/android/settings/development/compat/PlatformCompatDashboard.java +++ b/src/com/android/settings/development/compat/PlatformCompatDashboard.java @@ -20,6 +20,7 @@ import static com.android.internal.compat.OverrideAllowedState.ALLOWED; import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_COMPAT_CHANGE_APP; import android.app.Activity; +import android.app.AlertDialog; import android.app.settings.SettingsEnums; import android.compat.Compatibility.ChangeConfig; import android.content.Context; @@ -124,6 +125,14 @@ public class PlatformCompatDashboard extends DashboardFragment { } catch (PackageManager.NameNotFoundException e) { startAppPicker(); } + } else if (resultCode == AppPicker.RESULT_NO_MATCHING_APPS) { + new AlertDialog.Builder(getContext()) + .setTitle(R.string.platform_compat_dialog_title_no_apps) + .setMessage(R.string.platform_compat_dialog_text_no_apps) + .setPositiveButton(R.string.okay, (dialog, which) -> finish()) + .setOnDismissListener(dialog -> finish()) + .setCancelable(false) + .show(); } return; } @@ -254,7 +263,8 @@ public class PlatformCompatDashboard extends DashboardFragment { } private void startAppPicker() { - final Intent intent = new Intent(getContext(), AppPicker.class); + final Intent intent = new Intent(getContext(), AppPicker.class) + .putExtra(AppPicker.EXTRA_INCLUDE_NOTHING, false); // If build is neither userdebug nor eng, only include debuggable apps final boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); if (!debuggableBuild) { From 13f3e8f04b89f0e99235f135ed5f4c746a7a52cb Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Tue, 9 Jun 2020 23:38:41 -0400 Subject: [PATCH 2/3] [DO NOT MERGE] Add toggle for media resumption Adds a toggle under Settings > Sound > Media to toggle media resumption Bug: 154039093 Test: manual Test: atest SettingsProviderTest Test: make -j40 RunSettingsRoboTests ROBOTEST_FILTER="MediaControls" Change-Id: I803c57031bc3252cab35c06ea124d1c22a3c3aeb --- AndroidManifest.xml | 11 ++ res/values/strings.xml | 15 +++ res/xml/media_controls_settings.xml | 31 +++++ res/xml/sound_settings.xml | 8 ++ src/com/android/settings/Settings.java | 5 + .../core/gateway/SettingsGateway.java | 4 +- ...diaControlsParentPreferenceController.java | 53 +++++++++ .../MediaControlsPreferenceController.java | 57 +++++++++ .../settings/sound/MediaControlsSettings.java | 51 ++++++++ ...ontrolsParentPreferenceControllerTest.java | 104 +++++++++++++++++ ...MediaControlsPreferenceControllerTest.java | 110 ++++++++++++++++++ .../sound/MediaControlsSettingsTest.java | 50 ++++++++ 12 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 res/xml/media_controls_settings.xml create mode 100644 src/com/android/settings/sound/MediaControlsParentPreferenceController.java create mode 100644 src/com/android/settings/sound/MediaControlsPreferenceController.java create mode 100644 src/com/android/settings/sound/MediaControlsSettings.java create mode 100644 tests/robotests/src/com/android/settings/sound/MediaControlsParentPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/sound/MediaControlsSettingsTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4ce8f362198..2f53cc1e7c8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3393,6 +3393,17 @@ android:value="true" /> + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 847335750f8..c23abe9d417 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12125,4 +12125,19 @@ Allow screen overlays on Settings Allow apps that can display over other apps to overlay Settings screens + + + Media + + Hide player when the media session has ended + + The player allows you to resume a session from the expanded Quick Settings panel. + + Hide player + + Show player + + No players available + + media diff --git a/res/xml/media_controls_settings.xml b/res/xml/media_controls_settings.xml new file mode 100644 index 00000000000..3f0483f028e --- /dev/null +++ b/res/xml/media_controls_settings.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml index eca5f1ea09e..fc5c3e222a2 100644 --- a/res/xml/sound_settings.xml +++ b/res/xml/sound_settings.xml @@ -110,6 +110,14 @@ settings:allowDividerAbove="true" settings:controller="com.android.settings.notification.zen.ZenModePreferenceController"/> + + 0; + } +} diff --git a/src/com/android/settings/sound/MediaControlsSettings.java b/src/com/android/settings/sound/MediaControlsSettings.java new file mode 100644 index 00000000000..f914b5d6241 --- /dev/null +++ b/src/com/android/settings/sound/MediaControlsSettings.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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.sound; + +import android.app.settings.SettingsEnums; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +/** + * Media control settings located in the sound menu + */ +@SearchIndexable +public class MediaControlsSettings extends DashboardFragment { + + private static final String TAG = "MediaControlsSettings"; + + @Override + protected int getPreferenceScreenResId() { + return R.xml.media_controls_settings; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.MEDIA_CONTROLS_SETTINGS; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.media_controls_settings); +} diff --git a/tests/robotests/src/com/android/settings/sound/MediaControlsParentPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaControlsParentPreferenceControllerTest.java new file mode 100644 index 00000000000..a1a3f127d8f --- /dev/null +++ b/tests/robotests/src/com/android/settings/sound/MediaControlsParentPreferenceControllerTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2020 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.sound; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import com.android.settings.R; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class MediaControlsParentPreferenceControllerTest { + + private static final String KEY = "media_controls_summary"; + + private Context mContext; + private int mOriginalQs; + private int mOriginalResume; + private ContentResolver mContentResolver; + private MediaControlsParentPreferenceController mController; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + mContentResolver = mContext.getContentResolver(); + mOriginalQs = Settings.Global.getInt(mContentResolver, + Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + mOriginalResume = Settings.Secure.getInt(mContentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, 1); + mController = new MediaControlsParentPreferenceController(mContext, KEY); + } + + @After + public void tearDown() { + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, + mOriginalQs); + Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, + mOriginalResume); + } + + @Test + public void getAvailability_flagNotEnabled_returnUnsupportedOnDevice() { + // When the qs_media flag is not enabled + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 0); + + // Then the media resume option should not appear + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void getAvailability_flagEnabled_returnAvailable() { + // When the qs_media flag is enabled + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + + // Then the media resume option should appear + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void getSummary_isOn_showPlayer() { + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1); + + assertThat(mController.getSummary()) + .isEqualTo(mContext.getString(R.string.media_controls_show_player)); + } + + @Test + public void getSummary_isOff_hidePlayer() { + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0); + + assertThat(mController.getSummary()) + .isEqualTo(mContext.getString(R.string.media_controls_hide_player)); + } +} diff --git a/tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java new file mode 100644 index 00000000000..ff35ebf341c --- /dev/null +++ b/tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 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.sound; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class MediaControlsPreferenceControllerTest { + + private static final String KEY = "media_controls_resume_switch"; + + private Context mContext; + private int mOriginalQs; + private int mOriginalResume; + private ContentResolver mContentResolver; + private MediaControlsPreferenceController mController; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + mContentResolver = mContext.getContentResolver(); + mOriginalQs = Settings.Global.getInt(mContentResolver, + Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + mOriginalResume = Settings.Secure.getInt(mContentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, 1); + mController = new MediaControlsPreferenceController(mContext, KEY); + } + + @After + public void tearDown() { + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, + mOriginalQs); + Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, + mOriginalResume); + } + + @Test + public void getAvailability_flagNotEnabled_returnUnsupportedOnDevice() { + // When the qs_media flag is not enabled + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 0); + + // Then the media resume option should not appear + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void getAvailability_flagEnabled_returnAvailable() { + // When the qs_media flag is enabled + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + + // Then the media resume option should appear + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void setChecked_enable_shouldTurnOff() { + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1); + + assertThat(mController.isChecked()).isFalse(); + + mController.setChecked(true); + + assertThat(Settings.Secure.getInt(mContentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, -1)).isEqualTo(0); + } + + @Test + public void setChecked_disable_shouldTurnOn() { + Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); + Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0); + + assertThat(mController.isChecked()).isTrue(); + + mController.setChecked(false); + + assertThat(Settings.Secure.getInt(mContentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, -1)).isEqualTo(1); + } +} diff --git a/tests/robotests/src/com/android/settings/sound/MediaControlsSettingsTest.java b/tests/robotests/src/com/android/settings/sound/MediaControlsSettingsTest.java new file mode 100644 index 00000000000..a27cbaf36f5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/sound/MediaControlsSettingsTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 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.sound; + +import static com.google.common.truth.Truth.assertThat; + +import android.provider.SearchIndexableResource; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class MediaControlsSettingsTest { + + private MediaControlsSettings mSettings; + + @Before + public void setUp() { + mSettings = new MediaControlsSettings(); + } + + @Test + public void testSearchIndexProvider_shouldIndexResource() { + final List indexRes = + MediaControlsSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex( + RuntimeEnvironment.application, true /* enabled */); + + assertThat(indexRes).isNotNull(); + assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId()); + } +} From 80ac68ca91664e14100d61a0e691066e5335eeaf Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Thu, 18 Jun 2020 17:12:58 +0800 Subject: [PATCH 3/3] According to the enrolled status to show/hide face unlock results In current design, we only check the hardware support for face unlock to show/hide the face unlock results in Settings Search. However the face settings page is not launchable when the user doesn't enroll the face unlock. It will cause user confused that face unlock results is no response when they click them. Therefore, it's more making sense to add enrolled status checking to index the face unlock results. Test: manual and robotests Fixes: 157954564 Change-Id: I5dd36e15fe48d537ee499c73cc172fc913b39554 --- .../settings/biometrics/face/FaceSettings.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java index c1ee545fea3..a493ae01dc9 100644 --- a/src/com/android/settings/biometrics/face/FaceSettings.java +++ b/src/com/android/settings/biometrics/face/FaceSettings.java @@ -323,16 +323,18 @@ public class FaceSettings extends DashboardFragment { @Override protected boolean isPageSearchEnabled(Context context) { - return isAvailable(context); + if (isAvailable(context)) { + return hasEnrolledBiometrics(context); + } + + return false; } @Override public List getNonIndexableKeys(Context context) { final List keys = super.getNonIndexableKeys(context); if (isAvailable(context)) { - final FaceManager faceManager = context.getSystemService(FaceManager.class); - final boolean hasEnrolled = faceManager.hasEnrolledTemplates( - UserHandle.myUserId()); + final boolean hasEnrolled = hasEnrolledBiometrics(context); keys.add(hasEnrolled ? PREF_KEY_ENROLL_FACE_UNLOCK : PREF_KEY_DELETE_FACE_DATA); } @@ -353,5 +355,13 @@ public class FaceSettings extends DashboardFragment { } return isAttentionSupported; } + + private boolean hasEnrolledBiometrics(Context context) { + final FaceManager faceManager = Utils.getFaceManagerOrNull(context); + if (faceManager != null) { + return faceManager.hasEnrolledTemplates(UserHandle.myUserId()); + } + return false; + } }; }