From 453534d2101a65fc1bda393faff911fa717a58d8 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Tue, 19 Jun 2018 15:01:47 +0530 Subject: [PATCH 01/19] Bluetooth: PBAP not disconnected on disabling contact sharing Precondition: Contact Sharing is checked and there is PBAP Connection. Usecase: 1) Establish PBAP Connection. 2) Disable "Contact Sharing" button. 3) Check if PBAP is Disconnected or not. Issue: PBAP Profile Disconnection is not triggered. Root Cause: Change in Setting menu UI by removal of "Disable Profile" dialog box (like Android O) has resulted in not invoking PBAP Disconnect call from Setting App. Fix: Handle Disconnection for PBAP from API disableProfile() at settings/bluetooth/BluetoothDetailsProfilesController.java like other profiles. Test: Issue is not seen as per above usecase. Bug: 110515410 Change-Id: Ibd18fb836e10e79e99e6b04127ae181134201cef --- .../bluetooth/BluetoothDetailsProfilesController.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java index b0ed05681d6..ddb61790f21 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java @@ -148,15 +148,12 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll */ private void disableProfile(LocalBluetoothProfile profile, BluetoothDevice device, SwitchPreference profilePref) { - if (profile instanceof PbapServerProfile) { - mCachedDevice.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED); - // We don't need to do the additional steps below for this profile. - return; - } mCachedDevice.disconnect(profile); profile.setPreferred(device, false); if (profile instanceof MapProfile) { mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED); + } else if (profile instanceof PbapServerProfile) { + mCachedDevice.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED); } } From f722e59a51f219427f1d646fefcfd8d4306328dd Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 7 Jun 2018 11:33:48 -0700 Subject: [PATCH 02/19] Fix memory leaks in Settings Bug: 80507279 Test: inspected hprof before and after fix Change-Id: I6ea2925695deb6261263649e858484e1667ec522 Merged-In: I6ea2925695deb6261263649e858484e1667ec522 --- .../settings/fuelgauge/BatteryUtils.java | 8 ++--- .../settings/overlay/FeatureFactoryImpl.java | 32 +++++++++++-------- ...roundActivityPreferenceControllerTest.java | 6 ++-- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index c62af8fc34c..2dc3ed2852c 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -24,8 +24,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.BatteryStats; -import android.os.Bundle; import android.os.Build; +import android.os.Bundle; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; @@ -35,7 +35,6 @@ import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; -import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import android.util.SparseLongArray; @@ -48,7 +47,6 @@ import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.batterytip.AnomalyInfo; import com.android.settings.fuelgauge.batterytip.StatsManagerConfig; import com.android.settings.overlay.FeatureFactory; - import com.android.settingslib.fuelgauge.PowerWhitelistBackend; import com.android.settingslib.utils.PowerUtil; @@ -93,14 +91,14 @@ public class BatteryUtils { public static BatteryUtils getInstance(Context context) { if (sInstance == null || sInstance.isDataCorrupted()) { - sInstance = new BatteryUtils(context); + sInstance = new BatteryUtils(context.getApplicationContext()); } return sInstance; } @VisibleForTesting BatteryUtils(Context context) { - mContext = context.getApplicationContext(); + mContext = context; mPackageManager = context.getPackageManager(); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mPowerUsageFeatureProvider = FeatureFactory.getFactory( diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index d058b3b3ac5..0f7d58c531c 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -94,7 +94,8 @@ public class FeatureFactoryImpl extends FeatureFactory { @Override public PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context) { if (mPowerUsageFeatureProvider == null) { - mPowerUsageFeatureProvider = new PowerUsageFeatureProviderImpl(context); + mPowerUsageFeatureProvider = new PowerUsageFeatureProviderImpl( + context.getApplicationContext()); } return mPowerUsageFeatureProvider; } @@ -102,7 +103,8 @@ public class FeatureFactoryImpl extends FeatureFactory { @Override public DashboardFeatureProvider getDashboardFeatureProvider(Context context) { if (mDashboardFeatureProvider == null) { - mDashboardFeatureProvider = new DashboardFeatureProviderImpl(context); + mDashboardFeatureProvider = new DashboardFeatureProviderImpl( + context.getApplicationContext()); } return mDashboardFeatureProvider; } @@ -118,10 +120,11 @@ public class FeatureFactoryImpl extends FeatureFactory { @Override public ApplicationFeatureProvider getApplicationFeatureProvider(Context context) { if (mApplicationFeatureProvider == null) { - mApplicationFeatureProvider = new ApplicationFeatureProviderImpl(context, - new PackageManagerWrapper(context.getPackageManager()), + final Context appContext = context.getApplicationContext(); + mApplicationFeatureProvider = new ApplicationFeatureProviderImpl(appContext, + new PackageManagerWrapper(appContext.getPackageManager()), AppGlobals.getPackageManager(), - (DevicePolicyManager) context + (DevicePolicyManager) appContext .getSystemService(Context.DEVICE_POLICY_SERVICE)); } return mApplicationFeatureProvider; @@ -138,12 +141,14 @@ public class FeatureFactoryImpl extends FeatureFactory { @Override public EnterprisePrivacyFeatureProvider getEnterprisePrivacyFeatureProvider(Context context) { if (mEnterprisePrivacyFeatureProvider == null) { - mEnterprisePrivacyFeatureProvider = new EnterprisePrivacyFeatureProviderImpl(context, - (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE), - new PackageManagerWrapper(context.getPackageManager()), - UserManager.get(context), - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), - context.getResources()); + final Context appContext = context.getApplicationContext(); + mEnterprisePrivacyFeatureProvider = new EnterprisePrivacyFeatureProviderImpl(appContext, + (DevicePolicyManager) appContext + .getSystemService(Context.DEVICE_POLICY_SERVICE), + new PackageManagerWrapper(appContext.getPackageManager()), + UserManager.get(appContext), + (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE), + appContext.getResources()); } return mEnterprisePrivacyFeatureProvider; } @@ -172,7 +177,8 @@ public class FeatureFactoryImpl extends FeatureFactory { @Override public SuggestionFeatureProvider getSuggestionFeatureProvider(Context context) { if (mSuggestionFeatureProvider == null) { - mSuggestionFeatureProvider = new SuggestionFeatureProviderImpl(context); + mSuggestionFeatureProvider = new SuggestionFeatureProviderImpl( + context.getApplicationContext()); } return mSuggestionFeatureProvider; } @@ -180,7 +186,7 @@ public class FeatureFactoryImpl extends FeatureFactory { @Override public UserFeatureProvider getUserFeatureProvider(Context context) { if (mUserFeatureProvider == null) { - mUserFeatureProvider = new UserFeatureProviderImpl(context); + mUserFeatureProvider = new UserFeatureProviderImpl(context.getApplicationContext()); } return mUserFeatureProvider; } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java index 389b7144136..8bbb334cc1a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java @@ -17,6 +17,7 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doNothing; @@ -89,6 +90,7 @@ public class BackgroundActivityPreferenceControllerTest { mShadowContext = RuntimeEnvironment.application; FakeFeatureFactory.setupForTest(); + when(mContext.getApplicationContext()).thenReturn(mContext); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); @@ -118,7 +120,7 @@ public class BackgroundActivityPreferenceControllerTest { @Test public void testHandlePreferenceTreeClick_restrictApp_showDialog() { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) - .checkOpNoThrow(anyInt(), anyInt(), anyString()); + .checkOpNoThrow(anyInt(), anyInt(), anyString()); mController.handlePreferenceTreeClick(mPreference); @@ -128,7 +130,7 @@ public class BackgroundActivityPreferenceControllerTest { @Test public void testHandlePreferenceTreeClick_unRestrictApp_showDialog() { doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager) - .checkOpNoThrow(anyInt(), anyInt(), anyString()); + .checkOpNoThrow(anyInt(), anyInt(), anyString()); mController.handlePreferenceTreeClick(mPreference); From 1dfb9421b40605d55c3a6eca613c22215cf731ea Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Mon, 13 Aug 2018 10:39:52 -0700 Subject: [PATCH 03/19] Add 'Reach to check' to lock screen settings Also added to Gestures page to make sure we're following the same structure for all gestures. Change-Id: Iaa9e53e870165801decfa427441d01ee69bcce6d Note: using dummy video. dependency on b/112442049 Bug: 111414690 Test: make RunSettingsRoboTests ROBOTEST_FILTER=ReachGesturePreferenceControllerTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=ReachGestureSettings --- res/values/strings.xml | 14 +++ res/xml/reach_gesture_settings.xml | 37 +++++++ .../PickupGesturePreferenceController.java | 4 +- .../ReachGesturePreferenceController.java | 96 ++++++++++++++++ .../gestures/ReachGestureSettings.java | 81 ++++++++++++++ .../gestures/PickupGestureSettingsTest.java | 2 +- .../ReachGesturePreferenceControllerTest.java | 104 ++++++++++++++++++ .../gestures/ReachGestureSettingsTest.java | 51 +++++++++ 8 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 res/xml/reach_gesture_settings.xml create mode 100644 src/com/android/settings/gestures/ReachGesturePreferenceController.java create mode 100644 src/com/android/settings/gestures/ReachGestureSettings.java create mode 100644 tests/robotests/src/com/android/settings/gestures/ReachGesturePreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/gestures/ReachGestureSettingsTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 0c3732bd4b0..2f29a865d40 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9584,6 +9584,20 @@ To check time, notifications, and other info, pick up your device. + + Reach to check phone + + Reach to check tablet + + Reach to check device + + + To check time, notifications, and other info, reach for your phone. + + To check time, notifications, and other info, reach for your tablet. + + To check time, notifications, and other info, reach for your device. + Swipe fingerprint for notifications diff --git a/res/xml/reach_gesture_settings.xml b/res/xml/reach_gesture_settings.xml new file mode 100644 index 00000000000..1acc3640516 --- /dev/null +++ b/res/xml/reach_gesture_settings.xml @@ -0,0 +1,37 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/com/android/settings/gestures/PickupGesturePreferenceController.java b/src/com/android/settings/gestures/PickupGesturePreferenceController.java index 7460183bda0..399c047c451 100644 --- a/src/com/android/settings/gestures/PickupGesturePreferenceController.java +++ b/src/com/android/settings/gestures/PickupGesturePreferenceController.java @@ -31,8 +31,8 @@ import androidx.annotation.VisibleForTesting; public class PickupGesturePreferenceController extends GesturePreferenceController { - private final int ON = 1; - private final int OFF = 0; + private static final int ON = 1; + private static final int OFF = 0; private static final String PREF_KEY_VIDEO = "gesture_pick_up_video"; private final String mPickUpPrefKey; diff --git a/src/com/android/settings/gestures/ReachGesturePreferenceController.java b/src/com/android/settings/gestures/ReachGesturePreferenceController.java new file mode 100644 index 00000000000..e22dae42e18 --- /dev/null +++ b/src/com/android/settings/gestures/ReachGesturePreferenceController.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.gestures; + +import static android.provider.Settings.Secure.DOZE_REACH_GESTURE; + +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; + +import com.android.internal.hardware.AmbientDisplayConfiguration; + +public class ReachGesturePreferenceController extends GesturePreferenceController { + + private static final int ON = 1; + private static final int OFF = 0; + + private static final String PREF_KEY_VIDEO = "gesture_reach_video"; + private final String mReachUpPrefKey; + + private AmbientDisplayConfiguration mAmbientConfig; + @UserIdInt + private final int mUserId; + + public ReachGesturePreferenceController(Context context, String key) { + super(context, key); + mUserId = UserHandle.myUserId(); + mReachUpPrefKey = key; + } + + public ReachGesturePreferenceController setConfig(AmbientDisplayConfiguration config) { + mAmbientConfig = config; + return this; + } + + @Override + public int getAvailabilityStatus() { + // No hardware support for Reach Gesture + if (!getAmbientConfig().reachGestureAvailable()) { + return UNSUPPORTED_ON_DEVICE; + } + + return AVAILABLE; + } + + @Override + public boolean isSliceable() { + return TextUtils.equals(getPreferenceKey(), "gesture_reach"); + } + + @Override + protected String getVideoPrefKey() { + return PREF_KEY_VIDEO; + } + + @Override + public boolean isChecked() { + return getAmbientConfig().reachGestureEnabled(mUserId); + } + + @Override + public String getPreferenceKey() { + return mReachUpPrefKey; + } + + @Override + public boolean setChecked(boolean isChecked) { + return Settings.Secure.putInt(mContext.getContentResolver(), DOZE_REACH_GESTURE, + isChecked ? ON : OFF); + } + + private AmbientDisplayConfiguration getAmbientConfig() { + if (mAmbientConfig == null) { + mAmbientConfig = new AmbientDisplayConfiguration(mContext); + } + + return mAmbientConfig; + } +} diff --git a/src/com/android/settings/gestures/ReachGestureSettings.java b/src/com/android/settings/gestures/ReachGestureSettings.java new file mode 100644 index 00000000000..3df9fcfdeae --- /dev/null +++ b/src/com/android/settings/gestures/ReachGestureSettings.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.gestures; + +import android.content.Context; +import android.content.SharedPreferences; +import android.provider.SearchIndexableResource; + +import com.android.internal.hardware.AmbientDisplayConfiguration; +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +import java.util.Arrays; +import java.util.List; + +@SearchIndexable +public class ReachGestureSettings extends DashboardFragment { + + private static final String TAG = "ReachGestureSettings"; + + public static final String PREF_KEY_SUGGESTION_COMPLETE = + "pref_reach_gesture_suggestion_complete"; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + SuggestionFeatureProvider suggestionFeatureProvider = FeatureFactory.getFactory(context) + .getSuggestionFeatureProvider(context); + SharedPreferences prefs = suggestionFeatureProvider.getSharedPrefs(context); + prefs.edit().putBoolean(PREF_KEY_SUGGESTION_COMPLETE, true).apply(); + + use(ReachGesturePreferenceController.class) + .setConfig(new AmbientDisplayConfiguration(context)); + } + + @Override + public int getMetricsCategory() { + return MetricsProto.MetricsEvent.SETTINGS_GESTURE_REACH; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.reach_gesture_settings; + } + + public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.reach_gesture_settings; + return Arrays.asList(sir); + } + }; + +} diff --git a/tests/robotests/src/com/android/settings/gestures/PickupGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/PickupGestureSettingsTest.java index d78880b4b78..72865cfd4e2 100644 --- a/tests/robotests/src/com/android/settings/gestures/PickupGestureSettingsTest.java +++ b/tests/robotests/src/com/android/settings/gestures/PickupGestureSettingsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/robotests/src/com/android/settings/gestures/ReachGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/ReachGesturePreferenceControllerTest.java new file mode 100644 index 00000000000..9e24dab6261 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/ReachGesturePreferenceControllerTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.gestures; + +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.Matchers.anyInt; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.SharedPreferences; + +import com.android.internal.hardware.AmbientDisplayConfiguration; +import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class ReachGesturePreferenceControllerTest { + + private static final String KEY_REACH = "gesture_reach"; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + @Mock + private AmbientDisplayConfiguration mAmbientDisplayConfiguration; + + private ReachGesturePreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mController = new ReachGesturePreferenceController(mContext, KEY_REACH); + mController.setConfig(mAmbientDisplayConfiguration); + } + + @Test + public void testIsChecked_configIsSet_shouldReturnTrue() { + // Set the setting to be enabled. + when(mAmbientDisplayConfiguration.reachGestureEnabled(anyInt())).thenReturn(true); + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void testIsChecked_configIsNotSet_shouldReturnFalse() { + // Set the setting to be disabled. + when(mAmbientDisplayConfiguration.reachGestureEnabled(anyInt())).thenReturn(false); + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void getAvailabilityStatus_gestureNotSupported_UNSUPPORTED_ON_DEVICE() { + when(mAmbientDisplayConfiguration.reachGestureAvailable()).thenReturn(false); + final int availabilityStatus = mController.getAvailabilityStatus(); + + assertThat(availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void getAvailabilityStatus_gestureSupported_AVAILABLE() { + when(mAmbientDisplayConfiguration.reachGestureAvailable()).thenReturn(true); + final int availabilityStatus = mController.getAvailabilityStatus(); + + assertThat(availabilityStatus).isEqualTo(AVAILABLE); + } + + @Test + public void isSliceableCorrectKey_returnsTrue() { + final ReachGesturePreferenceController controller = + new ReachGesturePreferenceController(mContext, "gesture_reach"); + assertThat(controller.isSliceable()).isTrue(); + } + + @Test + public void isSliceableIncorrectKey_returnsFalse() { + final ReachGesturePreferenceController controller = + new ReachGesturePreferenceController(mContext, "bad_key"); + assertThat(controller.isSliceable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/gestures/ReachGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/ReachGestureSettingsTest.java new file mode 100644 index 00000000000..9371c71f968 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/ReachGestureSettingsTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.gestures; + +import static com.google.common.truth.Truth.assertThat; + +import android.provider.SearchIndexableResource; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class ReachGestureSettingsTest { + + private ReachGestureSettings mSettings; + + @Before + public void setUp() { + mSettings = new ReachGestureSettings(); + } + + @Test + public void testSearchIndexProvider_shouldIndexResource() { + final List indexRes = + ReachGestureSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex( + RuntimeEnvironment.application, true /* enabled */); + + assertThat(indexRes).isNotNull(); + assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId()); + } +} From f5c0145912e292e628d594510e34f973ff9e0d4c Mon Sep 17 00:00:00 2001 From: timhypeng Date: Tue, 31 Jul 2018 15:15:10 +0800 Subject: [PATCH 04/19] Remove LocalBluetoothAdapter from CachedBluetoothDeviceManager::addDevice() Bug: 111815935 Test: make -j50 RunSettingsRoboTests Change-Id: I5294439853ce35bccd883ed2aa21b33802d76cb1 --- .../settings/bluetooth/BluetoothPermissionActivity.java | 3 +-- .../android/settings/bluetooth/BluetoothPermissionRequest.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java index 74a5f246fce..5f17e5f2ae6 100644 --- a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java +++ b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java @@ -207,8 +207,7 @@ public class BluetoothPermissionActivity extends AlertActivity implements bluetoothManager.getCachedDeviceManager(); CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice); if (cachedDevice == null) { - cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(), - mDevice); + cachedDevice = cachedDeviceManager.addDevice(mDevice); } always = cachedDevice.checkAndIncreaseMessageRejectionCount(); } diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java index bbf3fffc6c7..c452957bc14 100644 --- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java +++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java @@ -231,8 +231,7 @@ public final class BluetoothPermissionRequest extends BroadcastReceiver { bluetoothManager.getCachedDeviceManager(); CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice); if (cachedDevice == null) { - cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(), - mDevice); + cachedDevice = cachedDeviceManager.addDevice(mDevice); } String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY; From 6409cf5c94cc1feb72dc078e84e66362fbecd6d5 Mon Sep 17 00:00:00 2001 From: Matthew Fritze Date: Mon, 30 Jul 2018 14:12:50 -0700 Subject: [PATCH 05/19] Hide SettingsSlice provider Explicitly set the Settings SliceBroadcastReceiver to be non-exported and remove the intent-filter. Add a second provider: SliceRelayReceiver to receive broadcasts from SysUI to alert Settings to potential changes to bound Settings Slices. The new receiver is exported, but only notifies changes to Settings, and doesn't make any changes itself. Change-Id: I422c0b07a61efa8996e9fdfa398eee84bbc1796f Merged-In: I80d070f7636614135ebe4f57a16f12a3eb6dee81 Fixes: 111330641 Test: boot, robolectric, Slicebrowser --- AndroidManifest.xml | 13 ++-- .../slices/SettingsSliceProvider.java | 2 +- .../slices/SliceBroadcastReceiver.java | 7 -- .../settings/slices/SliceRelayReceiver.java | 39 ++++++++++ .../slices/SliceBroadcastReceiverTest.java | 8 ++- .../slices/SliceRelayReceiverTest.java | 72 +++++++++++++++++++ 6 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 src/com/android/settings/slices/SliceRelayReceiver.java create mode 100644 tests/robotests/src/com/android/settings/slices/SliceRelayReceiverTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fb74bf96f09..2c5f8465114 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3314,10 +3314,15 @@ - - - + android:name=".slices.SliceBroadcastReceiver" + android:exported="false"> + + + + Formatting ^1\u2026 - Don\u2019t remove the ^1 while it\u2019s being formatting. + Don\u2019t remove the ^1 while it\u2019s being formatted. Move data to new storage From 520befff0184aa7e2ac0436b820e1f0ce7f86580 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Wed, 15 Aug 2018 17:02:11 -0700 Subject: [PATCH 09/19] Add colors to whitelist export ANDROID_LINT_JARS=$(gettop)/prebuilts/checkcolor/checkcolor.jar lint --check HardCodedColor --xml color-check-baseline.xml . Bug: 112005540 Test: builds Change-Id: Ibac23dda42e797fc01141cbe85d232ba5ea63139 --- color-check-baseline.xml | 102 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/color-check-baseline.xml b/color-check-baseline.xml index c2b8ed516ce..d72aaa8109a 100644 --- a/color-check-baseline.xml +++ b/color-check-baseline.xml @@ -1,6 +1,18 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -2557,7 +2649,7 @@ errorLine2=" ^"> @@ -2573,7 +2665,7 @@ errorLine2=" ^"> @@ -2589,7 +2681,7 @@ errorLine2=" ^"> @@ -2605,7 +2697,7 @@ errorLine2=" ^"> From 273c6d391883ba87daf79dae56cacf1ab05fbe40 Mon Sep 17 00:00:00 2001 From: timhypeng Date: Mon, 13 Aug 2018 17:38:36 +0800 Subject: [PATCH 10/19] Remove test-purpose constructor from ConnectedBluetoothDeviceUpdater - replace mock object with ShadowCachedBluetoothDeviceManager to test CachedBluetoothDevice - rename cachedDevices to mCachedDevices Bug: 111848213 Test: make -j50 RunSettingsRoboTests Change-Id: Ib024a3e9c3af745b1ab0be36361165a547cfa756 --- .../ConnectedBluetoothDeviceUpdater.java | 11 ----- .../ConnectedBluetoothDeviceUpdaterTest.java | 33 +++++++------- .../ShadowCachedBluetoothDeviceManager.java | 44 +++++++++++++++++++ 3 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/testutils/shadow/ShadowCachedBluetoothDeviceManager.java diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java index 259a403625b..b66b286b0fd 100644 --- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java @@ -24,9 +24,7 @@ import android.util.Log; import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.LocalBluetoothManager; -import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; /** @@ -45,15 +43,6 @@ public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater { mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); } - @VisibleForTesting - ConnectedBluetoothDeviceUpdater(DashboardFragment fragment, - DevicePreferenceCallback devicePreferenceCallback, - LocalBluetoothManager localBluetoothManager) { - super(fragment, devicePreferenceCallback, localBluetoothManager); - mAudioManager = (AudioManager) fragment.getContext(). - getSystemService(Context.AUDIO_SERVICE); - } - @Override public void onAudioModeChanged() { forceUpdate(); diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java index ece71d73d9c..6faea9a2334 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java @@ -34,9 +34,8 @@ import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowAudioManager; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; -import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.junit.Before; import org.junit.Test; @@ -51,7 +50,8 @@ import java.util.ArrayList; import java.util.Collection; @RunWith(SettingsRobolectricTestRunner.class) -@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class}) +@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class, + ShadowCachedBluetoothDeviceManager.class}) public class ConnectedBluetoothDeviceUpdaterTest { @Mock private DashboardFragment mDashboardFragment; @@ -61,16 +61,13 @@ public class ConnectedBluetoothDeviceUpdaterTest { private CachedBluetoothDevice mCachedBluetoothDevice; @Mock private BluetoothDevice mBluetoothDevice; - @Mock - private LocalBluetoothManager mLocalManager; - @Mock - private CachedBluetoothDeviceManager mCachedDeviceManager; private Context mContext; private ConnectedBluetoothDeviceUpdater mBluetoothDeviceUpdater; - private Collection cachedDevices; + private Collection mCachedDevices; private ShadowAudioManager mShadowAudioManager; private ShadowBluetoothAdapter mShadowBluetoothAdapter; + private ShadowCachedBluetoothDeviceManager mShadowCachedBluetoothDeviceManager; @Before public void setUp() { @@ -80,16 +77,16 @@ public class ConnectedBluetoothDeviceUpdaterTest { mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); mShadowBluetoothAdapter.setEnabled(true); mContext = RuntimeEnvironment.application; + mShadowCachedBluetoothDeviceManager = Shadow.extract( + Utils.getLocalBtManager(mContext).getCachedDeviceManager()); doReturn(mContext).when(mDashboardFragment).getContext(); - cachedDevices = + mCachedDevices = new ArrayList(new ArrayList()); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); - when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); - when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices); - - mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mDashboardFragment, - mDevicePreferenceCallback, mLocalManager)); + mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices); + mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext, + mDashboardFragment, mDevicePreferenceCallback)); mBluetoothDeviceUpdater.setPrefContext(mContext); doNothing().when(mBluetoothDeviceUpdater).addPreference(any()); doNothing().when(mBluetoothDeviceUpdater).removePreference(any()); @@ -101,7 +98,7 @@ public class ConnectedBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isHfpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); @@ -114,7 +111,7 @@ public class ConnectedBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isHfpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); @@ -127,7 +124,7 @@ public class ConnectedBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isA2dpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); @@ -140,7 +137,7 @@ public class ConnectedBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isA2dpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowCachedBluetoothDeviceManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowCachedBluetoothDeviceManager.java new file mode 100644 index 00000000000..ee04c4f1d38 --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowCachedBluetoothDeviceManager.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.testutils.shadow; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import java.util.Collection; + +/** + * Shadow class for {@link CachedBluetoothDeviceManager} to allow tests to manages the set of + * remote Bluetooth devices. + */ +@Implements(CachedBluetoothDeviceManager.class) +public class ShadowCachedBluetoothDeviceManager { + + private Collection mCachedDevices; + + public void setCachedDevicesCopy(Collection cachedDevices) { + mCachedDevices = cachedDevices; + } + + @Implementation + public synchronized Collection getCachedDevicesCopy() { + return mCachedDevices; + } +} \ No newline at end of file From 47a6dbcd9403be105d00ba17b37a1fe9a1a48438 Mon Sep 17 00:00:00 2001 From: timhypeng Date: Tue, 14 Aug 2018 10:30:50 +0800 Subject: [PATCH 11/19] Remove test-purpose constructor from AvailableMediaBluetoothDeviceUpdater - replace mock object with ShadowCachedBluetoothDeviceManager to test CachedBluetoothDevice - rename cachedDevices to mCachedDevices Bug: 111848213 Test: make -j50 RunSettingsRoboTests Change-Id: I3028a6fe06c39c48e7cee33976bdfcab2c8b73c8 --- .../AvailableMediaBluetoothDeviceUpdater.java | 11 ----- ...ilableMediaBluetoothDeviceUpdaterTest.java | 40 ++++++++----------- 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java index 2b9c2cb5565..c2e6f2f8d89 100644 --- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java @@ -23,9 +23,7 @@ import android.util.Log; import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.LocalBluetoothManager; -import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; /** @@ -45,15 +43,6 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); } - @VisibleForTesting - AvailableMediaBluetoothDeviceUpdater(DashboardFragment fragment, - DevicePreferenceCallback devicePreferenceCallback, - LocalBluetoothManager localBluetoothManager) { - super(fragment, devicePreferenceCallback, localBluetoothManager); - mAudioManager = (AudioManager) fragment.getContext(). - getSystemService(Context.AUDIO_SERVICE); - } - @Override public void onAudioModeChanged() { forceUpdate(); diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java index e676cf4b041..14d9ca79d01 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java @@ -33,10 +33,9 @@ import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowAudioManager; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HeadsetProfile; -import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import org.junit.Before; @@ -52,7 +51,8 @@ import java.util.ArrayList; import java.util.Collection; @RunWith(SettingsRobolectricTestRunner.class) -@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class}) +@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class, + ShadowCachedBluetoothDeviceManager.class}) public class AvailableMediaBluetoothDeviceUpdaterTest { @Mock private DashboardFragment mDashboardFragment; @@ -62,21 +62,14 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { private CachedBluetoothDevice mCachedBluetoothDevice; @Mock private BluetoothDevice mBluetoothDevice; - @Mock - private LocalBluetoothManager mLocalManager; - @Mock - private LocalBluetoothProfileManager mLocalBluetoothProfileManager; - @Mock - private HeadsetProfile mHeadsetProfile; - @Mock - private CachedBluetoothDeviceManager mCachedDeviceManager; private Context mContext; private AvailableMediaBluetoothDeviceUpdater mBluetoothDeviceUpdater; - private Collection cachedDevices; + private Collection mCachedDevices; private ShadowAudioManager mShadowAudioManager; private BluetoothDevicePreference mPreference; private ShadowBluetoothAdapter mShadowBluetoothAdapter; + private ShadowCachedBluetoothDeviceManager mShadowCachedBluetoothDeviceManager; @Before public void setUp() { @@ -86,18 +79,17 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); mShadowBluetoothAdapter.setEnabled(true); mContext = RuntimeEnvironment.application; - doReturn(mContext).when(mDashboardFragment).getContext(); - cachedDevices = + mShadowCachedBluetoothDeviceManager = Shadow.extract( + Utils.getLocalBtManager(mContext).getCachedDeviceManager()); + mCachedDevices = new ArrayList(new ArrayList()); + mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices); + doReturn(mContext).when(mDashboardFragment).getContext(); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); - when(mLocalManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); - when(mLocalBluetoothProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile); - when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); - when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices); - mBluetoothDeviceUpdater = spy(new AvailableMediaBluetoothDeviceUpdater(mDashboardFragment, - mDevicePreferenceCallback, mLocalManager)); + mBluetoothDeviceUpdater = spy(new AvailableMediaBluetoothDeviceUpdater(mContext, + mDashboardFragment, mDevicePreferenceCallback)); mBluetoothDeviceUpdater.setPrefContext(mContext); mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false); doNothing().when(mBluetoothDeviceUpdater).addPreference(any()); @@ -110,7 +102,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isHfpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); @@ -123,7 +115,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isHfpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); @@ -136,7 +128,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isA2dpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); @@ -149,7 +141,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { when(mBluetoothDeviceUpdater. isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); when(mCachedBluetoothDevice.isA2dpDevice()).thenReturn(true); - cachedDevices.add(mCachedBluetoothDevice); + mCachedDevices.add(mCachedBluetoothDevice); mBluetoothDeviceUpdater.onAudioModeChanged(); From f43a1f185db25408dd4a0f169731557da9265694 Mon Sep 17 00:00:00 2001 From: timhypeng Date: Tue, 14 Aug 2018 18:45:18 +0800 Subject: [PATCH 12/19] Remove test-purpose constructor from SavedBluetoothDeviceUpdater - replace mock object with ShadowBluetoothAdapter Bug: 111848213 Test: make -j50 RunSettingsRoboTests Change-Id: Iff69ed511b23846078925609bb603ae414956ea3 --- .../bluetooth/SavedBluetoothDeviceUpdater.java | 10 ---------- .../bluetooth/SavedBluetoothDeviceUpdaterTest.java | 14 +++++--------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java index 059a920b3d2..50de166ba65 100644 --- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java @@ -16,16 +16,13 @@ package com.android.settings.bluetooth; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.content.Context; import android.util.Log; import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.LocalBluetoothManager; -import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; /** @@ -41,13 +38,6 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater super(context, fragment, devicePreferenceCallback); } - @VisibleForTesting - SavedBluetoothDeviceUpdater(DashboardFragment fragment, - DevicePreferenceCallback devicePreferenceCallback, - LocalBluetoothManager localBluetoothManager) { - super(fragment, devicePreferenceCallback, localBluetoothManager); - } - @Override public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { final BluetoothDevice device = cachedDevice.getDevice(); diff --git a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java index def8c4db37c..e25e4b22744 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java @@ -29,9 +29,8 @@ import android.content.Context; import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import org.junit.Before; import org.junit.Test; @@ -39,8 +38,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; @RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) public class SavedBluetoothDeviceUpdaterTest { @Mock @@ -51,10 +52,6 @@ public class SavedBluetoothDeviceUpdaterTest { private CachedBluetoothDevice mCachedBluetoothDevice; @Mock private BluetoothDevice mBluetoothDevice; - @Mock - private LocalBluetoothManager mLocalManager; - @Mock - private LocalBluetoothProfileManager mLocalBluetoothProfileManager; private Context mContext; private SavedBluetoothDeviceUpdater mBluetoothDeviceUpdater; @@ -67,11 +64,10 @@ public class SavedBluetoothDeviceUpdaterTest { mContext = RuntimeEnvironment.application; doReturn(mContext).when(mDashboardFragment).getContext(); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); - when(mLocalManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mDashboardFragment, - mDevicePreferenceCallback, mLocalManager)); + mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mContext, mDashboardFragment, + mDevicePreferenceCallback)); mBluetoothDeviceUpdater.setPrefContext(mContext); mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false); doNothing().when(mBluetoothDeviceUpdater).addPreference(any()); From 16d1bf0cdee7ee2afe86ec2c360618a55b63f400 Mon Sep 17 00:00:00 2001 From: ChenChen Chen Date: Tue, 14 Aug 2018 14:27:25 +0800 Subject: [PATCH 13/19] Always show items in options menu for Wi-Fi Direct settings Remove MenuItem.SHOW_AS_ACTION_IF_ROOM flag for menu items to avoid showing truncated texts on action bar. Bug: 112671955 Test: Manual Change-Id: I1c9678321442169bc86d719e820d4af68261dee1 --- src/com/android/settings/wifi/p2p/WifiP2pSettings.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java index 7ef10e5ad3c..f26564d0047 100644 --- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java +++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java @@ -354,11 +354,9 @@ public class WifiP2pSettings extends DashboardFragment int textId = mWifiP2pSearching ? R.string.wifi_p2p_menu_searching : R.string.wifi_p2p_menu_search; menu.add(Menu.NONE, MENU_ID_SEARCH, 0, textId) - .setEnabled(mWifiP2pEnabled) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + .setEnabled(mWifiP2pEnabled); menu.add(Menu.NONE, MENU_ID_RENAME, 0, R.string.wifi_p2p_menu_rename) - .setEnabled(mWifiP2pEnabled) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + .setEnabled(mWifiP2pEnabled); super.onCreateOptionsMenu(menu, inflater); } From a79c377fbc46596b4dcfda32d211c528c0ff8a48 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 14 Aug 2018 16:25:54 -0700 Subject: [PATCH 14/19] Declare "searchable" attribute for preferences. Now we can easily mark a preference nonIndexable in xml instead of adding key into searchIndexProvider. Bug: 112608186 Test: robotests Change-Id: I0ff16d44bb7b6ad148d3d35f09ca0da0163f73f4 --- res/values/attrs.xml | 2 + res/xml/app_and_notification.xml | 3 +- res/xml/app_default_settings.xml | 7 ++- res/xml/default_autofill_picker_settings.xml | 2 +- res/xml/development_settings.xml | 3 +- res/xml/manage_assist.xml | 3 +- .../AppAndNotificationDashboardFragment.java | 8 --- .../applications/DefaultAppSettings.java | 10 --- ...pSettingsActivityPreferenceController.java | 2 +- .../core/BasePreferenceController.java | 4 ++ .../core/PreferenceControllerMixin.java | 7 ++- .../core/PreferenceXmlParserUtils.java | 34 +++++------ .../AutofillDeveloperSettingsObserver.java | 3 +- .../gestures/SwipeUpPreferenceController.java | 3 + .../language/LanguageAndInputSettings.java | 3 +- .../language/TtsPreferenceController.java | 31 ++++------ .../search/BaseSearchIndexProvider.java | 61 +++++++++++-------- .../system/SystemDashboardFragment.java | 2 - .../android/settings/users/UserSettings.java | 24 ++++---- .../res/xml-mcc999/display_settings.xml | 1 + ...pAndNotificationDashboardFragmentTest.java | 11 ++-- .../applications/DefaultAppSettingsTest.java | 2 +- .../core/PreferenceXmlParserUtilsTest.java | 32 +++++++++- .../language/TtsPreferenceControllerTest.java | 10 +-- .../search/BaseSearchIndexProviderTest.java | 20 ++++++ .../SettingsSearchIndexablesProviderTest.java | 23 ++++--- .../security/EncryptionAndCredentialTest.java | 2 +- .../settings/testutils/XmlTestUtils.java | 36 +++++------ 28 files changed, 201 insertions(+), 148 deletions(-) diff --git a/res/values/attrs.xml b/res/values/attrs.xml index b77c5442f7e..a4ce9f0af7e 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -69,6 +69,8 @@ + + diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml index dd661e08872..5a55ba5dab0 100644 --- a/res/xml/app_and_notification.xml +++ b/res/xml/app_and_notification.xml @@ -71,6 +71,7 @@ android:key="special_access" android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings" android:title="@string/special_access" - android:order="20" /> + android:order="20" + settings:searchable="false"/> diff --git a/res/xml/app_default_settings.xml b/res/xml/app_default_settings.xml index 3b7c80b1925..54676679875 100644 --- a/res/xml/app_default_settings.xml +++ b/res/xml/app_default_settings.xml @@ -25,7 +25,7 @@ android:key="assist_and_voice_input" android:title="@string/assist_and_voice_input_title" android:fragment="com.android.settings.applications.assist.ManageAssist" - settings:keywords="@string/keywords_assist_input"/> + settings:searchable="false"/> + android:fragment="com.android.settings.applications.defaultapps.DefaultBrowserPicker" + settings:searchable="false"> @@ -94,7 +95,7 @@ android:key="work_default_phone_app" android:title="@string/default_phone_title" android:fragment="com.android.settings.applications.defaultapps.DefaultPhonePicker" - settings:keywords="@string/keywords_default_phone_app"> + settings:searchable="false"> diff --git a/res/xml/default_autofill_picker_settings.xml b/res/xml/default_autofill_picker_settings.xml index 26dff7eca87..da72b43fec8 100644 --- a/res/xml/default_autofill_picker_settings.xml +++ b/res/xml/default_autofill_picker_settings.xml @@ -32,7 +32,7 @@ + android:fragment="com.android.settings.development.featureflags.FeatureFlagsDashboard" + settings:searchable="false" /> + android:title="@string/assist_and_voice_input_title" + settings:keywords="@string/keywords_assist_input"> getNonIndexableKeys(Context context) { - List keys = super.getNonIndexableKeys(context); - keys.add((new SpecialAppAccessPreferenceController(context)) - .getPreferenceKey()); - return keys; - } }; } diff --git a/src/com/android/settings/applications/DefaultAppSettings.java b/src/com/android/settings/applications/DefaultAppSettings.java index 3af5bc2882f..d8fd9ebb725 100644 --- a/src/com/android/settings/applications/DefaultAppSettings.java +++ b/src/com/android/settings/applications/DefaultAppSettings.java @@ -101,16 +101,6 @@ public class DefaultAppSettings extends DashboardFragment { return Arrays.asList(sir); } - @Override - public List getNonIndexableKeys(Context context) { - List keys = super.getNonIndexableKeys(context); - keys.add(KEY_ASSIST_VOICE_INPUT); - // TODO (b/38230148) Remove these keys when we can differentiate work results - keys.add(DefaultWorkPhonePreferenceController.KEY); - keys.add(DefaultWorkBrowserPreferenceController.KEY); - return keys; - } - @Override public List createPreferenceControllers( Context context) { diff --git a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java index 4a99f5abed6..131a2340532 100644 --- a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java +++ b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java @@ -40,7 +40,7 @@ public class BackupSettingsActivityPreferenceController extends BasePreferenceCo @Override public int getAvailabilityStatus() { return mUm.isAdminUser() - ? AVAILABLE + ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE; } diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java index 6cceaf3e518..14b8a811de8 100644 --- a/src/com/android/settings/core/BasePreferenceController.java +++ b/src/com/android/settings/core/BasePreferenceController.java @@ -274,6 +274,10 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl Log.w(TAG, "Skipping updateNonIndexableKeys due to empty key " + toString()); return; } + if (keys.contains(key)) { + Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list. " + toString()); + return; + } keys.add(key); } } diff --git a/src/com/android/settings/core/PreferenceControllerMixin.java b/src/com/android/settings/core/PreferenceControllerMixin.java index 3310df2fddf..da0b7e73d14 100644 --- a/src/com/android/settings/core/PreferenceControllerMixin.java +++ b/src/com/android/settings/core/PreferenceControllerMixin.java @@ -42,7 +42,12 @@ public interface PreferenceControllerMixin { final String key = ((AbstractPreferenceController) this).getPreferenceKey(); if (TextUtils.isEmpty(key)) { Log.w(TAG, - "Skipping updateNonIndexableKeys due to empty key " + this.toString()); + "Skipping updateNonIndexableKeys due to empty key " + toString()); + return; + } + if (keys.contains(key)) { + Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list. " + + toString()); return; } keys.add(key); diff --git a/src/com/android/settings/core/PreferenceXmlParserUtils.java b/src/com/android/settings/core/PreferenceXmlParserUtils.java index ff196dcaaf6..8ef7f8dc460 100644 --- a/src/com/android/settings/core/PreferenceXmlParserUtils.java +++ b/src/com/android/settings/core/PreferenceXmlParserUtils.java @@ -29,6 +29,9 @@ import android.util.Log; import android.util.TypedValue; import android.util.Xml; +import androidx.annotation.IntDef; +import androidx.annotation.VisibleForTesting; + import com.android.settings.R; import org.xmlpull.v1.XmlPullParser; @@ -41,9 +44,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import androidx.annotation.IntDef; -import androidx.annotation.VisibleForTesting; - /** * Utility class to parse elements of XML preferences */ @@ -53,7 +53,8 @@ public class PreferenceXmlParserUtils { @VisibleForTesting static final String PREF_SCREEN_TAG = "PreferenceScreen"; private static final List SUPPORTED_PREF_TYPES = Arrays.asList( - "Preference", "PreferenceCategory", "PreferenceScreen"); + "Preference", "PreferenceCategory", "PreferenceScreen", + "com.android.settings.widget.WorkOnlyCategory"); /** * Flag definition to indicate which metadata should be extracted when @@ -67,7 +68,8 @@ public class PreferenceXmlParserUtils { MetadataFlag.FLAG_NEED_PREF_CONTROLLER, MetadataFlag.FLAG_NEED_PREF_TITLE, MetadataFlag.FLAG_NEED_PREF_SUMMARY, - MetadataFlag.FLAG_NEED_PREF_ICON}) + MetadataFlag.FLAG_NEED_PREF_ICON, + MetadataFlag.FLAG_NEED_SEARCHABLE}) @Retention(RetentionPolicy.SOURCE) public @interface MetadataFlag { int FLAG_INCLUDE_PREF_SCREEN = 1; @@ -79,6 +81,7 @@ public class PreferenceXmlParserUtils { int FLAG_NEED_PREF_ICON = 1 << 6; int FLAG_NEED_PLATFORM_SLICE_FLAG = 1 << 7; int FLAG_NEED_KEYWORDS = 1 << 8; + int FLAG_NEED_SEARCHABLE = 1 << 9; } public static final String METADATA_PREF_TYPE = "type"; @@ -89,6 +92,7 @@ public class PreferenceXmlParserUtils { public static final String METADATA_ICON = "icon"; public static final String METADATA_PLATFORM_SLICE_FLAG = "platform_slice"; public static final String METADATA_KEYWORDS = "keywords"; + public static final String METADATA_SEARCHABLE = "searchable"; private static final String ENTRIES_SEPARATOR = "|"; @@ -154,18 +158,6 @@ public class PreferenceXmlParserUtils { R.styleable.Preference_controller); } - /** - * Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_ICON} instead. - */ - @Deprecated - public static int getDataIcon(Context context, AttributeSet attrs) { - final TypedArray ta = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.Preference); - final int dataIcon = ta.getResourceId(com.android.internal.R.styleable.Icon_icon, 0); - ta.recycle(); - return dataIcon; - } - /** * Extracts metadata from preference xml and put them into a {@link Bundle}. * @@ -232,6 +224,10 @@ public class PreferenceXmlParserUtils { if (hasFlag(flags, MetadataFlag.FLAG_NEED_KEYWORDS)) { preferenceMetadata.putString(METADATA_KEYWORDS, getKeywords(preferenceAttributes)); } + if (hasFlag(flags, MetadataFlag.FLAG_NEED_SEARCHABLE)) { + preferenceMetadata.putBoolean(METADATA_SEARCHABLE, + isSearchable(preferenceAttributes)); + } metadata.add(preferenceMetadata); preferenceAttributes.recycle(); @@ -312,6 +308,10 @@ public class PreferenceXmlParserUtils { return styledAttributes.getBoolean(R.styleable.Preference_platform_slice, false /* def */); } + private static boolean isSearchable(TypedArray styledAttributes) { + return styledAttributes.getBoolean(R.styleable.Preference_searchable, true /* default */); + } + private static String getKeywords(TypedArray styleAttributes) { return styleAttributes.getString(R.styleable.Preference_keywords); } diff --git a/src/com/android/settings/development/autofill/AutofillDeveloperSettingsObserver.java b/src/com/android/settings/development/autofill/AutofillDeveloperSettingsObserver.java index ae8e246b0b4..90266e03a65 100644 --- a/src/com/android/settings/development/autofill/AutofillDeveloperSettingsObserver.java +++ b/src/com/android/settings/development/autofill/AutofillDeveloperSettingsObserver.java @@ -21,6 +21,7 @@ import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; +import android.os.Looper; import android.os.UserHandle; import android.provider.Settings; @@ -30,7 +31,7 @@ final class AutofillDeveloperSettingsObserver extends ContentObserver { private final ContentResolver mResolver; public AutofillDeveloperSettingsObserver(Context context, Runnable changeCallback) { - super(new Handler()); + super(new Handler(Looper.getMainLooper())); mResolver = context.getContentResolver(); mChangeCallback = changeCallback; diff --git a/src/com/android/settings/gestures/SwipeUpPreferenceController.java b/src/com/android/settings/gestures/SwipeUpPreferenceController.java index f48d21b90d4..5e882c4e44f 100644 --- a/src/com/android/settings/gestures/SwipeUpPreferenceController.java +++ b/src/com/android/settings/gestures/SwipeUpPreferenceController.java @@ -47,6 +47,9 @@ public class SwipeUpPreferenceController extends GesturePreferenceController { final ComponentName recentsComponentName = ComponentName.unflattenFromString( context.getString(R.string.config_recentsComponentName)); + if (recentsComponentName == null) { + return false; + } final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) .setPackage(recentsComponentName.getPackageName()); if (context.getPackageManager().resolveService(quickStepIntent, diff --git a/src/com/android/settings/language/LanguageAndInputSettings.java b/src/com/android/settings/language/LanguageAndInputSettings.java index 6c7c0d3861c..68b1b2446bb 100644 --- a/src/com/android/settings/language/LanguageAndInputSettings.java +++ b/src/com/android/settings/language/LanguageAndInputSettings.java @@ -108,7 +108,7 @@ public class LanguageAndInputSettings extends DashboardFragment { // Pointer and Tts final TtsPreferenceController ttsPreferenceController = - new TtsPreferenceController(context, new TtsEngines(context)); + new TtsPreferenceController(context, KEY_TEXT_TO_SPEECH); controllers.add(ttsPreferenceController); final PointerSpeedController pointerController = new PointerSpeedController(context); controllers.add(pointerController); @@ -180,7 +180,6 @@ public class LanguageAndInputSettings extends DashboardFragment { public List getNonIndexableKeys(Context context) { List keys = super.getNonIndexableKeys(context); // Duplicates in summary and details pages. - keys.add(KEY_TEXT_TO_SPEECH); keys.add(KEY_PHYSICAL_KEYBOARD); return keys; } diff --git a/src/com/android/settings/language/TtsPreferenceController.java b/src/com/android/settings/language/TtsPreferenceController.java index c83492c4883..7e34175ce99 100644 --- a/src/com/android/settings/language/TtsPreferenceController.java +++ b/src/com/android/settings/language/TtsPreferenceController.java @@ -19,31 +19,26 @@ package com.android.settings.language; import android.content.Context; import android.speech.tts.TtsEngines; +import androidx.annotation.VisibleForTesting; + import com.android.settings.R; -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settings.core.BasePreferenceController; -public class TtsPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { +public class TtsPreferenceController extends BasePreferenceController { - private static final String KEY_VOICE_CATEGORY = "voice_category"; - private static final String KEY_TTS_SETTINGS = "tts_settings_summary"; + @VisibleForTesting + TtsEngines mTtsEngines; - private final TtsEngines mTtsEngines; - - public TtsPreferenceController(Context context, TtsEngines ttsEngines) { - super(context); - mTtsEngines = ttsEngines; + public TtsPreferenceController(Context context, String key) { + super(context, key); + mTtsEngines = new TtsEngines(context); } @Override - public boolean isAvailable() { + public int getAvailabilityStatus() { return !mTtsEngines.getEngines().isEmpty() && - mContext.getResources().getBoolean(R.bool.config_show_tts_settings_summary); - } - - @Override - public String getPreferenceKey() { - return KEY_TTS_SETTINGS; + mContext.getResources().getBoolean(R.bool.config_show_tts_settings_summary) + ? AVAILABLE_UNSEARCHABLE + : CONDITIONALLY_UNAVAILABLE; } } diff --git a/src/com/android/settings/search/BaseSearchIndexProvider.java b/src/com/android/settings/search/BaseSearchIndexProvider.java index fcfa8aadb6a..efbefde5b48 100644 --- a/src/com/android/settings/search/BaseSearchIndexProvider.java +++ b/src/com/android/settings/search/BaseSearchIndexProvider.java @@ -16,14 +16,21 @@ package com.android.settings.search; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SEARCHABLE; +import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag + .FLAG_INCLUDE_PREF_SCREEN; +import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_KEY; +import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_SEARCHABLE; + import android.annotation.XmlRes; import android.content.Context; -import android.content.res.XmlResourceParser; +import android.os.Bundle; import android.provider.SearchIndexableResource; -import android.text.TextUtils; -import android.util.AttributeSet; import android.util.Log; -import android.util.Xml; + +import androidx.annotation.CallSuper; +import androidx.annotation.VisibleForTesting; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerListHelper; @@ -31,16 +38,12 @@ import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceXmlParserUtils; import com.android.settingslib.core.AbstractPreferenceController; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import androidx.annotation.CallSuper; -import androidx.annotation.VisibleForTesting; - /** * A basic SearchIndexProvider that returns no data to index. */ @@ -66,11 +69,12 @@ public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider { public List getNonIndexableKeys(Context context) { if (!isPageSearchEnabled(context)) { // Entire page should be suppressed, mark all keys from this page as non-indexable. - return getNonIndexableKeysFromXml(context); + return getNonIndexableKeysFromXml(context, true /* suppressAllPage */); } + final List nonIndexableKeys = new ArrayList<>(); + nonIndexableKeys.addAll(getNonIndexableKeysFromXml(context, false /* suppressAllPage */)); final List controllers = getPreferenceControllers(context); if (controllers != null && !controllers.isEmpty()) { - final List nonIndexableKeys = new ArrayList<>(); for (AbstractPreferenceController controller : controllers) { if (controller instanceof PreferenceControllerMixin) { ((PreferenceControllerMixin) controller) @@ -85,10 +89,8 @@ public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider { nonIndexableKeys.add(controller.getPreferenceKey()); } } - return nonIndexableKeys; - } else { - return new ArrayList<>(); } + return nonIndexableKeys; } @Override @@ -131,7 +133,11 @@ public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider { return true; } - private List getNonIndexableKeysFromXml(Context context) { + /** + * Get all non-indexable keys from xml. If {@param suppressAllPage} is set, all keys are + * considered non-indexable. Otherwise, only keys with searchable="false" are included. + */ + private List getNonIndexableKeysFromXml(Context context, boolean suppressAllPage) { final List resources = getXmlResourcesToIndex( context, true /* not used*/); if (resources == null || resources.isEmpty()) { @@ -139,27 +145,32 @@ public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider { } final List nonIndexableKeys = new ArrayList<>(); for (SearchIndexableResource res : resources) { - nonIndexableKeys.addAll(getNonIndexableKeysFromXml(context, res.xmlResId)); + nonIndexableKeys.addAll( + getNonIndexableKeysFromXml(context, res.xmlResId, suppressAllPage)); } return nonIndexableKeys; } @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) - public List getNonIndexableKeysFromXml(Context context, @XmlRes int xmlResId) { - final List nonIndexableKeys = new ArrayList<>(); - final XmlResourceParser parser = context.getResources().getXml(xmlResId); - final AttributeSet attrs = Xml.asAttributeSet(parser); + public List getNonIndexableKeysFromXml(Context context, @XmlRes int xmlResId, + boolean suppressAllPage) { + return getKeysFromXml(context, xmlResId, suppressAllPage); + } + + private List getKeysFromXml(Context context, @XmlRes int xmlResId, + boolean suppressAllPage) { + final List keys = new ArrayList<>(); try { - while (parser.next() != XmlPullParser.END_DOCUMENT) { - final String key = PreferenceXmlParserUtils.getDataKey(context, attrs); - if (!TextUtils.isEmpty(key)) { - nonIndexableKeys.add(key); + final List metadata = PreferenceXmlParserUtils.extractMetadata(context, + xmlResId, FLAG_NEED_KEY | FLAG_INCLUDE_PREF_SCREEN | FLAG_NEED_SEARCHABLE); + for (Bundle bundle : metadata) { + if (suppressAllPage || !bundle.getBoolean(METADATA_SEARCHABLE, true)) { + keys.add(bundle.getString(METADATA_KEY)); } } } catch (IOException | XmlPullParserException e) { Log.w(TAG, "Error parsing non-indexable from xml " + xmlResId); } - return nonIndexableKeys; + return keys; } - } diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java index 52349ae6fa8..0c73e4d8913 100644 --- a/src/com/android/settings/system/SystemDashboardFragment.java +++ b/src/com/android/settings/system/SystemDashboardFragment.java @@ -101,8 +101,6 @@ public class SystemDashboardFragment extends DashboardFragment { @Override public List getNonIndexableKeys(Context context) { List keys = super.getNonIndexableKeys(context); - keys.add((new BackupSettingsActivityPreferenceController( - context).getPreferenceKey())); keys.add(KEY_RESET); return keys; } diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 9aa56c234d3..89e9a67978c 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -49,6 +49,13 @@ import android.view.MenuItem; import android.view.View; import android.widget.SimpleAdapter; +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.UserIcons; import com.android.internal.widget.LockPatternUtils; @@ -76,13 +83,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; -import androidx.annotation.VisibleForTesting; -import androidx.annotation.WorkerThread; -import androidx.appcompat.app.AlertDialog; -import androidx.preference.Preference; -import androidx.preference.PreferenceGroup; -import androidx.preference.PreferenceScreen; - /** * Screen that manages the list of users on the device. * Guest user is an always visible entry, even if the guest is not currently @@ -635,8 +635,8 @@ public class UserSettings extends SettingsPreferenceFragment AlertDialog.Builder builder = new AlertDialog.Builder(context); SimpleAdapter adapter = new SimpleAdapter(builder.getContext(), data, R.layout.two_line_list_item, - new String[] {KEY_TITLE, KEY_SUMMARY}, - new int[] {R.id.title, R.id.summary}); + new String[]{KEY_TITLE, KEY_SUMMARY}, + new int[]{R.id.title, R.id.summary}); builder.setTitle(R.string.user_add_user_type_title); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @@ -1238,8 +1238,10 @@ public class UserSettings extends SettingsPreferenceFragment } @Override - public List getNonIndexableKeysFromXml(Context context, int xmlResId) { - final List niks = super.getNonIndexableKeysFromXml(context, xmlResId); + public List getNonIndexableKeysFromXml(Context context, int xmlResId, + boolean suppressAllPage) { + final List niks = super.getNonIndexableKeysFromXml(context, xmlResId, + suppressAllPage); new AddUserWhenLockedPreferenceController(context, KEY_ADD_USER_WHEN_LOCKED) .updateNonIndexableKeys(niks); new AutoSyncDataPreferenceController(context, null /* parent */) diff --git a/tests/robotests/res/xml-mcc999/display_settings.xml b/tests/robotests/res/xml-mcc999/display_settings.xml index 8c5d47ad4f5..fccad7fb454 100644 --- a/tests/robotests/res/xml-mcc999/display_settings.xml +++ b/tests/robotests/res/xml-mcc999/display_settings.xml @@ -59,5 +59,6 @@ android:title="pref_title_5" android:summaryOn="summary_on" android:summaryOff="summary_off" + settings:searchable="false" settings:keywords="keywords1, keywords2, keywords3" /> \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/applications/AppAndNotificationDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/AppAndNotificationDashboardFragmentTest.java index 97210b332b4..c332c064da3 100644 --- a/tests/robotests/src/com/android/settings/applications/AppAndNotificationDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppAndNotificationDashboardFragmentTest.java @@ -17,6 +17,7 @@ package com.android.settings.applications; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -26,7 +27,6 @@ import android.os.UserManager; import com.android.settings.notification.EmergencyBroadcastPreferenceController; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.testutils.XmlTestUtils; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,19 +42,16 @@ public class AppAndNotificationDashboardFragmentTest { @Test @Config(shadows = {ShadowEmergencyBroadcastPreferenceController.class}) - public void testNonIndexableKeys_existInXmlLayout() { + public void getNonIndexableKeys_shouldIncludeSpecialAppAccess() { final Context context = spy(RuntimeEnvironment.application); UserManager manager = mock(UserManager.class); when(manager.isAdminUser()).thenReturn(true); when(context.getSystemService(Context.USER_SERVICE)).thenReturn(manager); final List niks = AppAndNotificationDashboardFragment.SEARCH_INDEX_DATA_PROVIDER .getNonIndexableKeys(context); - AppAndNotificationDashboardFragment fragment = new AppAndNotificationDashboardFragment(); - final int xmlId = fragment.getPreferenceScreenResId(); - final List keys = XmlTestUtils.getKeysFromPreferenceXml(context, xmlId); - - assertThat(keys).containsAllIn(niks); + assertThat(niks).contains( + new SpecialAppAccessPreferenceController(context).getPreferenceKey()); } @Implements(EmergencyBroadcastPreferenceController.class) diff --git a/tests/robotests/src/com/android/settings/applications/DefaultAppSettingsTest.java b/tests/robotests/src/com/android/settings/applications/DefaultAppSettingsTest.java index fef8eefee69..dda6b72e9f2 100644 --- a/tests/robotests/src/com/android/settings/applications/DefaultAppSettingsTest.java +++ b/tests/robotests/src/com/android/settings/applications/DefaultAppSettingsTest.java @@ -165,7 +165,7 @@ public class DefaultAppSettingsTest { final List niks = DefaultAppSettings.SEARCH_INDEX_DATA_PROVIDER .getNonIndexableKeys(context); - final int xmlId = (new DefaultAppSettings()).getPreferenceScreenResId(); + final int xmlId = new DefaultAppSettings().getPreferenceScreenResId(); final List keys = XmlTestUtils.getKeysFromPreferenceXml(context, xmlId); diff --git a/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java b/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java index 70cfa21df20..b3dbdab01eb 100644 --- a/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java +++ b/tests/robotests/src/com/android/settings/core/PreferenceXmlParserUtilsTest.java @@ -16,7 +16,10 @@ package com.android.settings.core; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEYWORDS; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SEARCHABLE; + import static com.google.common.truth.Truth.assertThat; import android.content.Context; @@ -247,8 +250,7 @@ public class PreferenceXmlParserUtilsTest { @Test @Config(qualifiers = "mcc999") - public void extractMetadata_requestIncludesKeywords_shouldContainKeywords() - throws IOException, XmlPullParserException { + public void extractMetadata_requestIncludesKeywords_shouldContainKeywords() throws Exception { final String expectedKeywords = "a, b, c"; final List metadata = PreferenceXmlParserUtils.extractMetadata(mContext, R.xml.location_settings, @@ -260,6 +262,32 @@ public class PreferenceXmlParserUtilsTest { assertThat(keywords).isEqualTo(expectedKeywords); } + @Test + public void extractMetadata_requestSearchable_shouldDefaultToTrue() throws Exception { + final List metadata = PreferenceXmlParserUtils.extractMetadata(mContext, + R.xml.display_settings, MetadataFlag.FLAG_NEED_SEARCHABLE); + for (Bundle bundle : metadata) { + assertThat(bundle.getBoolean(METADATA_SEARCHABLE)).isTrue(); + } + } + + @Test + @Config(qualifiers = "mcc999") + public void extractMetadata_requestSearchable_shouldReturnAttributeValue() throws Exception { + final List metadata = PreferenceXmlParserUtils.extractMetadata(mContext, + R.xml.display_settings, + MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_SEARCHABLE); + boolean foundKey = false; + for (Bundle bundle : metadata) { + if (TextUtils.equals(bundle.getString(METADATA_KEY), "pref_key_5")) { + assertThat(bundle.getBoolean(METADATA_SEARCHABLE)).isFalse(); + foundKey = true; + break; + } + } + assertThat(foundKey).isTrue(); + } + /** * @param resId the ID for the XML preference * @return an XML resource parser that points to the start tag diff --git a/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java index cffe9213fb4..c483b8ccc9a 100644 --- a/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/language/TtsPreferenceControllerTest.java @@ -17,6 +17,7 @@ package com.android.settings.language; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -25,6 +26,9 @@ import android.content.Context; import android.speech.tts.TextToSpeech; import android.speech.tts.TtsEngines; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; @@ -38,9 +42,6 @@ import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; - @RunWith(SettingsRobolectricTestRunner.class) public class TtsPreferenceControllerTest { @@ -58,7 +59,8 @@ public class TtsPreferenceControllerTest { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - mController = new TtsPreferenceController(mContext, mTtsEngines); + mController = new TtsPreferenceController(mContext, "test_key"); + mController.mTtsEngines = mTtsEngines; mPreference = new Preference(RuntimeEnvironment.application); mPreference.setKey(mController.getPreferenceKey()); when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference); diff --git a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java index df60654c1f0..0c507d9605d 100644 --- a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java +++ b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java @@ -169,4 +169,24 @@ public class BaseSearchIndexProviderTest { assertThat(nonIndexableKeys).contains("status_header"); } + + @Test + @Config(qualifiers = "mcc999") + public void getNonIndexableKeys_hasSearchableAttributeInXml_shouldSuppressUnsearchable() { + final BaseSearchIndexProvider provider = new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex(Context context, + boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.display_settings; + return Collections.singletonList(sir); + } + + }; + + final List nonIndexableKeys = + provider.getNonIndexableKeys(RuntimeEnvironment.application); + + assertThat(nonIndexableKeys).contains("pref_key_5"); + } } diff --git a/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java b/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java index e76f43dd4b5..e3ef15e9174 100644 --- a/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java +++ b/tests/robotests/src/com/android/settings/search/SettingsSearchIndexablesProviderTest.java @@ -1,6 +1,7 @@ package com.android.settings.search; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -21,6 +22,9 @@ import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import java.util.ArrayList; +import java.util.List; + @RunWith(SettingsRobolectricTestRunner.class) public class SettingsSearchIndexablesProviderTest { @@ -96,16 +100,19 @@ public class SettingsSearchIndexablesProviderTest { @Test @Config(qualifiers = "mcc999") public void testNonIndexablesColumnFetched() { - Uri rawUri = Uri.parse("content://" + BASE_AUTHORITY + "/" + + final Uri rawUri = Uri.parse("content://" + BASE_AUTHORITY + "/" + SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH); - final Cursor cursor = mProvider.query(rawUri, - SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS, null, null, null); + final List keys = new ArrayList<>(); - cursor.moveToFirst(); - assertThat(cursor.getCount()).isEqualTo(2); - assertThat(cursor.getString(0)).isEqualTo("pref_key_1"); - cursor.moveToNext(); - assertThat(cursor.getString(0)).isEqualTo("pref_key_3"); + try (Cursor cursor = mProvider.query(rawUri, + SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS, null, null, null)) { + while (cursor.moveToNext()) { + keys.add(cursor.getString(0)); + } + } + + assertThat(keys).hasSize(3); + assertThat(keys).containsAllOf("pref_key_1", "pref_key_3", "pref_key_5"); } } diff --git a/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java b/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java index 6e49f1e3949..9d26de2e6d3 100644 --- a/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java +++ b/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java @@ -73,7 +73,7 @@ public class EncryptionAndCredentialTest { final List expectedKeys = new ArrayList<>(); for (SearchIndexableResource res : index) { expectedKeys.addAll(((BaseSearchIndexProvider) SEARCH_INDEX_DATA_PROVIDER) - .getNonIndexableKeysFromXml(mContext, res.xmlResId)); + .getNonIndexableKeysFromXml(mContext, res.xmlResId, true /* suppressAll */)); } final List keys = SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); diff --git a/tests/robotests/src/com/android/settings/testutils/XmlTestUtils.java b/tests/robotests/src/com/android/settings/testutils/XmlTestUtils.java index 7e8493e0efe..6a96cf0cf59 100644 --- a/tests/robotests/src/com/android/settings/testutils/XmlTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/XmlTestUtils.java @@ -1,15 +1,16 @@ package com.android.settings.testutils; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; +import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag + .FLAG_INCLUDE_PREF_SCREEN; +import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_KEY; + import android.content.Context; -import android.content.res.Resources; -import android.content.res.XmlResourceParser; +import android.os.Bundle; import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.Xml; import com.android.settings.core.PreferenceXmlParserUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.util.ArrayList; @@ -25,30 +26,21 @@ public class XmlTestUtils { * on the screen. * * @param context of the preference screen. - * @param xmlId of the Preference Xml to be parsed. + * @param xmlId of the Preference Xml to be parsed. * @return List of all keys in the preference Xml */ public static List getKeysFromPreferenceXml(Context context, int xmlId) { - final XmlResourceParser parser = context.getResources().getXml(xmlId); - final AttributeSet attrs = Xml.asAttributeSet(parser); final List keys = new ArrayList<>(); - String key; try { - while (parser.next() != XmlPullParser.END_DOCUMENT) { - try { - key = PreferenceXmlParserUtils.getDataKey(context, attrs); - if (!TextUtils.isEmpty(key)) { - keys.add(key); - } - } catch (NullPointerException e) { - continue; - } catch (Resources.NotFoundException e) { - continue; + List metadata = PreferenceXmlParserUtils.extractMetadata(context, xmlId, + FLAG_NEED_KEY | FLAG_INCLUDE_PREF_SCREEN); + for (Bundle bundle : metadata) { + final String key = bundle.getString(METADATA_KEY); + if (!TextUtils.isEmpty(key)) { + keys.add(key); } } - } catch (java.io.IOException e) { - return null; - } catch (XmlPullParserException e) { + } catch (java.io.IOException | XmlPullParserException e) { return null; } From 7193895c6afc52092646c9901c1ffea958182f3d Mon Sep 17 00:00:00 2001 From: Zimuzo Date: Wed, 15 Aug 2018 15:10:20 +0100 Subject: [PATCH 15/19] Launch work profile activity to add Autofill service ag/4666330 allowed choosing separate Autofill services for the main and work profile. However, the 'Add service' button for work profile always resolved action_view intent as the main user even when clicking the work profile preference. Now the intent is resolved to the right profile depending on the preference clicked. BUG: 112610177 Test: Verified manually Change-Id: I870346d7b5618e7c230edebd59ac7f52f8495d47 --- .../applications/defaultapps/DefaultAutofillPicker.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java index 1705dc578a6..b281336d0be 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java +++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java @@ -35,6 +35,7 @@ import android.text.Html; import android.text.TextUtils; import android.util.Log; import androidx.preference.Preference; +import androidx.preference.Preference.OnPreferenceClickListener; import com.android.internal.content.PackageMonitor; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; @@ -168,11 +169,15 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment { } final Intent addNewServiceIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)); - Preference preference = new Preference(getPrefContext()); + final Context context = getPrefContext(); + final Preference preference = new Preference(context); + preference.setOnPreferenceClickListener(p -> { + context.startActivityAsUser(addNewServiceIntent, UserHandle.of(mUserId)); + return true; + }); preference.setTitle(R.string.print_menu_item_add_service); preference.setIcon(R.drawable.ic_menu_add); preference.setOrder(Integer.MAX_VALUE -1); - preference.setIntent(addNewServiceIntent); preference.setPersistent(false); return preference; } From 2b575a40f3646a3061316e28ac198597051cd004 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 16 Aug 2018 10:41:08 -0700 Subject: [PATCH 16/19] Check condition displayable state in parallel. This speeds up condition display by about 40 ms. Bug: 112485407 Test: robotests still passes Change-Id: Iac66354492496a9ece9178c438db6506e6fe7be5 --- .../conditional/v2/ConditionManager.java | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/homepage/conditional/v2/ConditionManager.java b/src/com/android/settings/homepage/conditional/v2/ConditionManager.java index ff5d20a868f..c67b255d4f0 100644 --- a/src/com/android/settings/homepage/conditional/v2/ConditionManager.java +++ b/src/com/android/settings/homepage/conditional/v2/ConditionManager.java @@ -28,6 +28,13 @@ import com.android.settings.homepage.conditional.ConditionListener; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class ConditionManager { private static final String TAG = "ConditionManager"; @@ -37,6 +44,9 @@ public class ConditionManager { @VisibleForTesting final List mCardControllers; + private static final long DISPLAYABLE_CHECKER_TIMEOUT_MS = 20; + + private final ExecutorService mExecutorService; private final Context mAppContext; private final ConditionListener mListener; @@ -51,6 +61,7 @@ public class ConditionManager { public ConditionManager(Context context, ConditionListener listener) { mAppContext = context.getApplicationContext(); + mExecutorService = Executors.newCachedThreadPool(); mCandidates = new ArrayList<>(); mCardControllers = new ArrayList<>(); mListener = listener; @@ -62,9 +73,23 @@ public class ConditionManager { */ public List getDisplayableCards() { final List cards = new ArrayList<>(); + final List> displayableCards = new ArrayList<>(); + // Check displayable future for (ConditionalCard card : mCandidates) { - if (getController(card.getId()).isDisplayable()) { - cards.add(card); + final DisplayableChecker future = new DisplayableChecker( + card, getController(card.getId())); + displayableCards.add(mExecutorService.submit(future)); + } + // Collect future and add displayable cards + for (Future cardFuture : displayableCards) { + try { + final ConditionalCard card = cardFuture.get(DISPLAYABLE_CHECKER_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + if (card != null) { + cards.add(card); + } + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Log.w(TAG, "Failed to get displayable state for card, likely timeout. Skipping", e); } } return cards; @@ -89,7 +114,6 @@ public class ConditionManager { onConditionChanged(); } - /** * Start monitoring state change for all conditions */ @@ -163,6 +187,24 @@ public class ConditionManager { mCandidates.add(new RingerMutedConditionCard(mAppContext)); mCandidates.add(new RingerVibrateConditionCard(mAppContext)); mCandidates.add(new WorkModeConditionCard(mAppContext)); + } + /** + * Returns card if controller says it's displayable. Otherwise returns null. + */ + public static class DisplayableChecker implements Callable { + + private final ConditionalCard mCard; + private final ConditionalCardController mController; + + private DisplayableChecker(ConditionalCard card, ConditionalCardController controller) { + mCard = card; + mController = controller; + } + + @Override + public ConditionalCard call() throws Exception { + return mController.isDisplayable() ? mCard : null; + } } } From 723d39c95076af2671a6c0af50aa0c1f3e9f5422 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 16 Aug 2018 12:13:30 -0700 Subject: [PATCH 17/19] Refined the StrictMode for CardContentProvider - Modify CardContentProvider and add Mainthread checking Bug: 111820446 Test: robotest Change-Id: I7af25e8938b79c4c0fe225d58d59da4dde15ba45 --- .../homepage/CardContentProvider.java | 33 +++-- .../homepage/CardContentProviderTest.java | 121 ++++++++++++++++-- 2 files changed, 123 insertions(+), 31 deletions(-) diff --git a/src/com/android/settings/homepage/CardContentProvider.java b/src/com/android/settings/homepage/CardContentProvider.java index 3081ae1fa81..640a00b3683 100644 --- a/src/com/android/settings/homepage/CardContentProvider.java +++ b/src/com/android/settings/homepage/CardContentProvider.java @@ -29,6 +29,8 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.settingslib.utils.ThreadUtils; + /** * Provider stores and manages user interaction feedback for homepage contextual cards. */ @@ -61,9 +63,7 @@ public class CardContentProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); try { - if (Build.IS_DEBUGGABLE) { - enableStrictMode(true); - } + maybeEnableStrictMode(); final SQLiteDatabase database = mDBHelper.getWritableDatabase(); final String table = getTableFromMatch(uri); @@ -84,10 +84,7 @@ public class CardContentProvider extends ContentProvider { public int delete(Uri uri, String selection, String[] selectionArgs) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); try { - if (Build.IS_DEBUGGABLE) { - enableStrictMode(true); - } - + maybeEnableStrictMode(); final SQLiteDatabase database = mDBHelper.getWritableDatabase(); final String table = getTableFromMatch(uri); final int rowsDeleted = database.delete(table, selection, selectionArgs); @@ -108,9 +105,7 @@ public class CardContentProvider extends ContentProvider { String[] selectionArgs, String sortOrder) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); try { - if (Build.IS_DEBUGGABLE) { - enableStrictMode(true); - } + maybeEnableStrictMode(); final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); final String table = getTableFromMatch(uri); @@ -130,9 +125,7 @@ public class CardContentProvider extends ContentProvider { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); try { - if (Build.IS_DEBUGGABLE) { - enableStrictMode(true); - } + maybeEnableStrictMode(); final SQLiteDatabase database = mDBHelper.getWritableDatabase(); final String table = getTableFromMatch(uri); @@ -144,10 +137,16 @@ public class CardContentProvider extends ContentProvider { } } - private void enableStrictMode(boolean enabled) { - StrictMode.setThreadPolicy(enabled - ? new StrictMode.ThreadPolicy.Builder().detectAll().build() - : StrictMode.ThreadPolicy.LAX); + @VisibleForTesting + void maybeEnableStrictMode() { + if (Build.IS_DEBUGGABLE && ThreadUtils.isMainThread()) { + enableStrictMode(); + } + } + + @VisibleForTesting + void enableStrictMode() { + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().build()); } @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java b/tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java index bf1527a4a42..eec87b0286e 100644 --- a/tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java +++ b/tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java @@ -19,13 +19,19 @@ package com.android.settings.homepage; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowThreadUtils; import org.junit.After; import org.junit.Before; @@ -33,18 +39,23 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; @RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = ShadowThreadUtils.class) public class CardContentProviderTest { private Context mContext; private CardContentProvider mProvider; + private ContentResolver mResolver; private Uri mUri; @Before public void setUp() { mContext = RuntimeEnvironment.application; - mProvider = Robolectric.setupContentProvider(CardContentProvider.class); + mProvider = spy(Robolectric.setupContentProvider(CardContentProvider.class)); + mResolver = mContext.getContentResolver(); mUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CardContentProvider.CARD_AUTHORITY) @@ -54,6 +65,7 @@ public class CardContentProviderTest { @After public void cleanUp() { + ShadowThreadUtils.reset(); CardDatabaseHelper.getInstance(mContext).close(); CardDatabaseHelper.sCardDatabaseHelper = null; } @@ -61,7 +73,7 @@ public class CardContentProviderTest { @Test public void cardData_insert() { final int cnt_before_instert = getRowCount(); - mContext.getContentResolver().insert(mUri, insertOneRow()); + mResolver.insert(mUri, insertOneRow()); final int cnt_after_instert = getRowCount(); assertThat(cnt_after_instert - cnt_before_instert).isEqualTo(1); @@ -69,7 +81,7 @@ public class CardContentProviderTest { @Test public void cardData_query() { - mContext.getContentResolver().insert(mUri, insertOneRow()); + mResolver.insert(mUri, insertOneRow()); final int count = getRowCount(); assertThat(count).isGreaterThan(0); @@ -77,29 +89,27 @@ public class CardContentProviderTest { @Test public void cardData_delete() { - final ContentResolver contentResolver = mContext.getContentResolver(); - contentResolver.insert(mUri, insertOneRow()); - final int del_count = contentResolver.delete(mUri, null, null); + mResolver.insert(mUri, insertOneRow()); + final int del_count = mResolver.delete(mUri, null, null); assertThat(del_count).isGreaterThan(0); } @Test public void cardData_update() { - final ContentResolver contentResolver = mContext.getContentResolver(); - contentResolver.insert(mUri, insertOneRow()); + mResolver.insert(mUri, insertOneRow()); - final double updatingScore= 0.87; + final double updatingScore = 0.87; final ContentValues values = new ContentValues(); values.put(CardDatabaseHelper.CardColumns.SCORE, updatingScore); final String strWhere = CardDatabaseHelper.CardColumns.NAME + "=?"; final String[] selectionArgs = {"auto_rotate"}; - final int update_count = contentResolver.update(mUri, values, strWhere, selectionArgs); + final int update_count = mResolver.update(mUri, values, strWhere, selectionArgs); assertThat(update_count).isGreaterThan(0); final String[] columns = {CardDatabaseHelper.CardColumns.SCORE}; - final Cursor cr = contentResolver.query(mUri, columns, strWhere, selectionArgs, null); + final Cursor cr = mResolver.query(mUri, columns, strWhere, selectionArgs, null); cr.moveToFirst(); final double qryScore = cr.getDouble(0); @@ -107,6 +117,90 @@ public class CardContentProviderTest { assertThat(qryScore).isEqualTo(updatingScore); } + @Test + public void insert_isMainThread_shouldEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(true); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + + mProvider.insert(mUri, insertOneRow()); + + verify(mProvider).enableStrictMode(); + } + + @Test + public void query_isMainThread_shouldEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(true); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + + mProvider.query(mUri, null, null, null); + + verify(mProvider).enableStrictMode(); + } + + @Test + public void delete_isMainThread_shouldEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(true); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + + mProvider.delete(mUri, null, null); + + verify(mProvider).enableStrictMode(); + } + + @Test + public void update_isMainThread_shouldEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(true); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + final ContentValues values = new ContentValues(); + values.put(CardDatabaseHelper.CardColumns.SCORE, "0.01"); + + mProvider.update(mUri, values, null, null); + + verify(mProvider).enableStrictMode(); + } + + @Test + public void insert_notMainThread_shouldNotEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(false); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + + mProvider.insert(mUri, insertOneRow()); + + verify(mProvider, never()).enableStrictMode(); + } + + @Test + public void query_notMainThread_shouldNotEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(false); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + + mProvider.query(mUri, null, null, null); + + verify(mProvider, never()).enableStrictMode(); + } + + @Test + public void delete_notMainThread_shouldNotEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(false); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + + mProvider.delete(mUri, null, null); + + verify(mProvider, never()).enableStrictMode(); + } + + @Test + public void update_notMainThread_shouldNotEnableStrictMode() { + ShadowThreadUtils.setIsMainThread(false); + ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + final ContentValues values = new ContentValues(); + values.put(CardDatabaseHelper.CardColumns.SCORE, "0.01"); + + mProvider.update(mUri, values, null, null); + + verify(mProvider, never()).enableStrictMode(); + } + @Test(expected = UnsupportedOperationException.class) public void getType_shouldCrash() { mProvider.getType(null); @@ -138,10 +232,9 @@ public class CardContentProviderTest { } private int getRowCount() { - final ContentResolver contentResolver = mContext.getContentResolver(); - final Cursor cr = contentResolver.query(mUri, null, null, null); + final Cursor cr = mResolver.query(mUri, null, null, null); final int count = cr.getCount(); cr.close(); return count; } -} +} \ No newline at end of file From b53cdf09ddd04b098b949cec4f6bce661f8587cd Mon Sep 17 00:00:00 2001 From: tmfang Date: Thu, 16 Aug 2018 14:20:29 +0800 Subject: [PATCH 18/19] Fix bug about Wi-Fi dialog rotation When user clicks a Wi-Fi access point in WifiSettings, screen pops up a Wi-Fi point dialog. And then user rotates the screen, Wi-Fi access dialog changes to "Add network" full screen dialog. In old code, we check whether dialog is showing by dialog.isShowing() in onSaveInstanceState. For now, this design is not appropriate. Since isShowing() won't return true anymore when fragment calls onSaveInstanceState. So, we check dialog object whether is null or not now. If dialog is null, it means that there is no dialog was shown, before user rotates the screen. Change-Id: I7dc26369c005f576fe679abc70327f6a02620935 Fixes: 112624846 Test: manual test, robo test --- .../android/settings/wifi/WifiSettings.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 10111aa7204..877c70e909b 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -24,6 +24,7 @@ import android.app.Activity; import android.app.Dialog; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.net.ConnectivityManager; @@ -87,7 +88,7 @@ import java.util.List; @SearchIndexable public class WifiSettings extends RestrictedSettingsFragment implements Indexable, WifiTracker.WifiListener, AccessPointListener, - WifiDialog.WifiDialogListener { + WifiDialog.WifiDialogListener, DialogInterface.OnDismissListener { private static final String TAG = "WifiSettings"; @@ -432,9 +433,8 @@ public class WifiSettings extends RestrictedSettingsFragment @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - - // If the dialog is showing, save its state. - if (mDialog != null && mDialog.isShowing()) { + // If dialog has been shown, save its state. + if (mDialog != null) { outState.putInt(SAVE_DIALOG_MODE, mDialogMode); if (mDlgAccessPoint != null) { mAccessPointSavedState = new Bundle(); @@ -623,6 +623,18 @@ public class WifiSettings extends RestrictedSettingsFragment return super.onCreateDialog(dialogId); } + @Override + public void onDialogShowing() { + super.onDialogShowing(); + setOnDismissListener(this); + } + + @Override + public void onDismiss(DialogInterface dialog) { + // We don't keep any dialog object when dialog was dismissed. + mDialog = null; + } + @Override public int getDialogMetricsCategory(int dialogId) { switch (dialogId) { From 5f7c991162190b9a038b2126d5bf4148595494dc Mon Sep 17 00:00:00 2001 From: tmfang Date: Fri, 17 Aug 2018 15:16:12 +0800 Subject: [PATCH 19/19] Fix crash on "Set up Wi-Fi NFC tag" dialog When user launches "Set up Wi-Fi NFC tag" dialog and then tries to rotate screen, device will crash. Because isShowing() of dialog won't return true anymore when fragment calls onSaveIntance(), we can't save status of dialog successfully. And then, when fragment called onCreateDialog() again, it can't get any dialog object. So, we only check dialog whether is null or not. If dialog is null, it means that there is no dialog was shown before user rotates the screen. Fixes: 112741721 Test: NFC tag wifi test, robo test Change-Id: Idb448ea32c4215d8380c69bfd896cc91d8c1f8d1 --- src/com/android/settings/wifi/WifiSettings.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 877c70e909b..9e2fac32646 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -443,7 +443,7 @@ public class WifiSettings extends RestrictedSettingsFragment } } - if (mWifiToNfcDialog != null && mWifiToNfcDialog.isShowing()) { + if (mWifiToNfcDialog != null) { Bundle savedState = new Bundle(); mWifiToNfcDialog.saveState(savedState); outState.putBundle(SAVED_WIFI_NFC_DIALOG_STATE, savedState); @@ -617,7 +617,6 @@ public class WifiSettings extends RestrictedSettingsFragment mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(getActivity(), mWifiNfcDialogSavedState); } - return mWifiToNfcDialog; } return super.onCreateDialog(dialogId); @@ -633,6 +632,7 @@ public class WifiSettings extends RestrictedSettingsFragment public void onDismiss(DialogInterface dialog) { // We don't keep any dialog object when dialog was dismissed. mDialog = null; + mWifiToNfcDialog = null; } @Override