From 826879ed63bae0b459aeb7e6d42400d9c7f1b499 Mon Sep 17 00:00:00 2001 From: tmfang Date: Mon, 25 Feb 2019 18:12:24 +0800 Subject: [PATCH 01/15] Create a preference controller for "App info" We try to avoid managing too many preferences in a controller. So, we create another controller to manage all apps info preference. RecentAppsPreferenceController and AllAppsInfoPreferenceController share same state of recent apps in order to improve the performance. Test: visual, robo test Fixes: 126134996 Change-Id: I1d8a175b213831415797437c64fd9d432864f9d3 --- res/xml/app_and_notification.xml | 3 +- .../AllAppsInfoPreferenceController.java | 58 ++++++++++++ .../AppAndNotificationDashboardFragment.java | 25 +++++- .../RecentAppsPreferenceController.java | 45 +++------- .../AllAppsInfoPreferenceControllerTest.java | 64 +++++++++++++ .../RecentAppsPreferenceControllerTest.java | 90 +++++++------------ 6 files changed, 190 insertions(+), 95 deletions(-) create mode 100644 src/com/android/settings/applications/AllAppsInfoPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml index 518379ac86e..9dd5feaa1ce 100644 --- a/res/xml/app_and_notification.xml +++ b/res/xml/app_and_notification.xml @@ -27,7 +27,8 @@ android:key="all_app_info" android:title="@string/applications_settings" android:order="-999" - android:fragment="com.android.settings.applications.manageapplications.ManageApplications"/> + android:fragment="com.android.settings.applications.manageapplications.ManageApplications" + settings:controller="com.android.settings.applications.AllAppsInfoPreferenceController"/> mRecentApps; + + public AllAppsInfoPreferenceController(Context context, String key) { + super(context, key); + } + + public void setRecentApps(List recentApps) { + mRecentApps = recentApps; + } + + @Override + public int getAvailabilityStatus() { + return mRecentApps == null || mRecentApps.isEmpty() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + // Show total number of installed apps as See all's summary. + new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON, + mContext.getPackageManager()) { + @Override + protected void onCountComplete(int num) { + preference.setSummary(mContext.getString(R.string.apps_summary, num)); + } + }.execute(); + } +} diff --git a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java index 48483517f91..7aaf80de65c 100644 --- a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java +++ b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java @@ -36,6 +36,10 @@ public class AppAndNotificationDashboardFragment extends DashboardFragment { private static final String TAG = "AppAndNotifDashboard"; + private boolean mIsFirstLaunch; + private RecentAppsPreferenceController mRecentAppsPreferenceController; + private AllAppsInfoPreferenceController mAllAppsInfoPreferenceController; + @Override public int getMetricsCategory() { return SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY; @@ -61,7 +65,26 @@ public class AppAndNotificationDashboardFragment extends DashboardFragment { super.onAttach(context); use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle()); - use(RecentAppsPreferenceController.class).setFragment(this /* fragment */); + mRecentAppsPreferenceController = use(RecentAppsPreferenceController.class); + mRecentAppsPreferenceController.setFragment(this /* fragment */); + + mAllAppsInfoPreferenceController = use(AllAppsInfoPreferenceController.class); + mAllAppsInfoPreferenceController.setRecentApps( + mRecentAppsPreferenceController.getRecentApps()); + + mIsFirstLaunch = true; + } + + @Override + public void onResume() { + if (!mIsFirstLaunch) { + mRecentAppsPreferenceController.reloadData(); + mAllAppsInfoPreferenceController.setRecentApps( + mRecentAppsPreferenceController.getRecentApps()); + } + + super.onResume(); + mIsFirstLaunch = false; } @Override diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java index 838d75849ac..c0d18c6eb81 100644 --- a/src/com/android/settings/applications/RecentAppsPreferenceController.java +++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java @@ -66,8 +66,6 @@ import java.util.Set; public class RecentAppsPreferenceController extends BasePreferenceController implements Comparator { - @VisibleForTesting - static final String KEY_ALL_APP_INFO = "all_app_info"; @VisibleForTesting static final String KEY_DIVIDER = "recent_apps_divider"; @@ -79,11 +77,7 @@ public class RecentAppsPreferenceController extends BasePreferenceController @VisibleForTesting LayoutPreference mRecentAppsPreference; @VisibleForTesting - Preference mAllAppPref; - @VisibleForTesting Preference mDivider; - @VisibleForTesting - boolean mIsFirstLaunch; private final PackageManager mPm; private final UsageStatsManager mUsageStatsManager; @@ -119,7 +113,6 @@ public class RecentAppsPreferenceController extends BasePreferenceController mPowerManager = mContext.getSystemService(PowerManager.class); mUsageStatsManager = mContext.getSystemService(UsageStatsManager.class); mRecentApps = new ArrayList<>(); - mIsFirstLaunch = true; reloadData(); } @@ -129,14 +122,13 @@ public class RecentAppsPreferenceController extends BasePreferenceController @Override public int getAvailabilityStatus() { - return mRecentApps.isEmpty() ? AVAILABLE_UNSEARCHABLE : AVAILABLE; + return mRecentApps.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - mAllAppPref = screen.findPreference(KEY_ALL_APP_INFO); mDivider = screen.findPreference(KEY_DIVIDER); mRecentAppsPreference = (LayoutPreference) screen.findPreference(getPreferenceKey()); final View view = mRecentAppsPreference.findViewById(R.id.app_entities_header); @@ -157,26 +149,18 @@ public class RecentAppsPreferenceController extends BasePreferenceController @Override public void updateState(Preference preference) { super.updateState(preference); - // In order to improve launch time, we don't load data again at first launch. - if (!mIsFirstLaunch) { - reloadData(); - refreshUi(); - } + + refreshUi(); // Show total number of installed apps as See all's summary. new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON, mContext.getPackageManager()) { @Override protected void onCountComplete(int num) { - if (mHasRecentApps) { - mAppEntitiesController.setHeaderDetails( - mContext.getString(R.string.see_all_apps_title, num)); - mAppEntitiesController.apply(); - } else { - mAllAppPref.setSummary(mContext.getString(R.string.apps_summary, num)); - } + mAppEntitiesController.setHeaderDetails( + mContext.getString(R.string.see_all_apps_title, num)); + mAppEntitiesController.apply(); } }.execute(); - mIsFirstLaunch = false; } @Override @@ -185,14 +169,16 @@ public class RecentAppsPreferenceController extends BasePreferenceController return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed()); } + List getRecentApps() { + return mRecentApps; + } + @VisibleForTesting void refreshUi() { if (mRecentApps != null && !mRecentApps.isEmpty()) { - mHasRecentApps = true; displayRecentApps(); } else { - mHasRecentApps = false; - displayOnlyAppInfo(); + mDivider.setVisible(false); } } @@ -209,13 +195,6 @@ public class RecentAppsPreferenceController extends BasePreferenceController updateDisplayableRecentAppList(); } - private void displayOnlyAppInfo() { - mDivider.setVisible(false); - mAllAppPref.setTitle(R.string.applications_settings); - mAllAppPref.setVisible(true); - mRecentAppsPreference.setVisible(false); - } - private void displayRecentApps() { int showAppsCount = 0; @@ -230,8 +209,6 @@ public class RecentAppsPreferenceController extends BasePreferenceController } } mAppEntitiesController.apply(); - mRecentAppsPreference.setVisible(true); - mAllAppPref.setVisible(false); mDivider.setVisible(true); } diff --git a/tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java new file mode 100644 index 00000000000..ec3cf657421 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 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.applications; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.usage.UsageStats; +import android.content.Context; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class AllAppsInfoPreferenceControllerTest { + + private AllAppsInfoPreferenceController mController; + + @Before + public void setUp() { + final Context context = RuntimeEnvironment.application; + mController = new AllAppsInfoPreferenceController(context, "test_key"); + } + + @Test + public void getAvailabilityStatus_hasRecentApps_shouldReturnConditionallyUnavailable() { + final List stats = new ArrayList<>(); + final UsageStats stat1 = new UsageStats(); + stat1.mLastTimeUsed = System.currentTimeMillis(); + stat1.mPackageName = "pkg.class"; + stats.add(stat1); + mController.setRecentApps(stats); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + + @Test + public void getAvailabilityStatus_noRecentApps_shouldReturnAvailable() { + // No data + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java index e2a16579ad4..1a28f37548f 100644 --- a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java @@ -18,6 +18,7 @@ package com.android.settings.applications; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; +import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; @@ -111,7 +112,6 @@ public class RecentAppsPreferenceControllerTest { final View appEntitiesHeaderView = LayoutInflater.from(context).inflate( R.layout.app_entities_header, null /* root */); - final Preference seeAllPreference = new Preference(context); final Preference dividerPreference = new Preference(context); mRecentAppsPreference = spy(new LayoutPreference(context, appEntitiesHeaderView)); @@ -119,11 +119,8 @@ public class RecentAppsPreferenceControllerTest { mController.setFragment(mFragment); mController.mAppEntitiesController = mock(AppEntitiesHeaderController.class); mController.mRecentAppsPreference = mRecentAppsPreference; - mController.mAllAppPref = seeAllPreference; mController.mDivider = dividerPreference; - when(mScreen.findPreference(RecentAppsPreferenceController.KEY_ALL_APP_INFO)) - .thenReturn(seeAllPreference); when(mScreen.findPreference(RecentAppsPreferenceController.KEY_DIVIDER)) .thenReturn(dividerPreference); when(mScreen.findPreference("test_key")).thenReturn(mRecentAppsPreference); @@ -152,9 +149,33 @@ public class RecentAppsPreferenceControllerTest { } @Test - public void getAvailabilityStatus_noRecentApps_shouldReturnAvailableUnsearchable() { + public void getAvailabilityStatus_noRecentApps_shouldReturnConditionallyUnavailable() { // No data - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + + @Test + public void getAvailabilityStatus_powerSaverModeOn_shouldReturnConditionallyUnavailable() { + when(mPowerManager.isPowerSaveMode()).thenReturn(true); + + final List stats = new ArrayList<>(); + final UsageStats stat1 = new UsageStats(); + + stat1.mLastTimeUsed = System.currentTimeMillis(); + stat1.mPackageName = "pkg.class"; + stats.add(stat1); + + // stat1, stat2 are valid apps. stat3 is invalid. + when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())) + .thenReturn(mAppEntry); + when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) + .thenReturn(new ResolveInfo()); + when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) + .thenReturn(stats); + mAppEntry.info = mApplicationInfo; + mController.reloadData(); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); } @Test @@ -178,25 +199,6 @@ public class RecentAppsPreferenceControllerTest { assertThat(mController.mAppEntitiesController).isNotNull(); } - @Test - public void updateState_firstLaunch_shouldNotReloadData() { - mController.mIsFirstLaunch = true; - - mController.updateState(mRecentAppsPreference); - - verify(mController, never()).reloadData(); - } - - @Test - public void updateState_afterFirstLaunch_shouldReloadDataAndRefreshUi() { - mController.mIsFirstLaunch = false; - - mController.updateState(mRecentAppsPreference); - - verify(mController).reloadData(); - verify(mController).refreshUi(); - } - @Test public void updateState_threeValidRecentOpenAppsSet_setAppEntityThreeTime() { final List stats = new ArrayList<>(); @@ -227,7 +229,7 @@ public class RecentAppsPreferenceControllerTest { when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) .thenReturn(stats); mAppEntry.info = mApplicationInfo; - mController.mIsFirstLaunch = false; + mController.reloadData(); mController.updateState(mRecentAppsPreference); @@ -235,7 +237,6 @@ public class RecentAppsPreferenceControllerTest { .setAppEntity(anyInt(), any(AppEntityInfo.class)); assertThat(mController.mRecentAppsPreference.isVisible()).isTrue(); assertThat(mController.mDivider.isVisible()).isTrue(); - assertThat(mController.mAllAppPref.isVisible()).isFalse(); } @Test @@ -268,7 +269,7 @@ public class RecentAppsPreferenceControllerTest { when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) .thenReturn(stats); mAppEntry.info = mApplicationInfo; - mController.mIsFirstLaunch = false; + mController.reloadData(); mController.updateState(mRecentAppsPreference); @@ -278,35 +279,6 @@ public class RecentAppsPreferenceControllerTest { .setAppEntity(anyInt(), any(AppEntityInfo.class)); assertThat(mController.mRecentAppsPreference.isVisible()).isTrue(); assertThat(mController.mDivider.isVisible()).isTrue(); - assertThat(mController.mAllAppPref.isVisible()).isFalse(); - } - - @Test - public void updateState_powerSaverModeOn_headerIsNotVisible() { - when(mPowerManager.isPowerSaveMode()).thenReturn(true); - - final List stats = new ArrayList<>(); - final UsageStats stat1 = new UsageStats(); - - stat1.mLastTimeUsed = System.currentTimeMillis(); - stat1.mPackageName = "pkg.class"; - stats.add(stat1); - - // stat1, stat2 are valid apps. stat3 is invalid. - when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())) - .thenReturn(mAppEntry); - when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) - .thenReturn(new ResolveInfo()); - when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) - .thenReturn(stats); - mAppEntry.info = mApplicationInfo; - mController.mIsFirstLaunch = false; - - mController.updateState(mRecentAppsPreference); - - assertThat(mController.mRecentAppsPreference.isVisible()).isFalse(); - assertThat(mController.mDivider.isVisible()).isFalse(); - assertThat(mController.mAllAppPref.isVisible()).isTrue(); } @Test @@ -341,7 +313,7 @@ public class RecentAppsPreferenceControllerTest { // Make sure stat2 is considered an instant app. ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", (InstantAppDataProvider) (ApplicationInfo info) -> info == stat2Entry.info); - mController.mIsFirstLaunch = false; + mController.reloadData(); mController.updateState(mRecentAppsPreference); @@ -417,7 +389,7 @@ public class RecentAppsPreferenceControllerTest { .thenReturn(new ResolveInfo()); when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) .thenReturn(stats); - mController.mIsFirstLaunch = false; + mController.reloadData(); mController.updateState(mRecentAppsPreference); From 35e35622d9b677c82a2f26e76695c0fbbcd75436 Mon Sep 17 00:00:00 2001 From: Evan Laird Date: Fri, 15 Feb 2019 14:55:30 -0500 Subject: [PATCH 02/15] Use ThemedBatteryDrawable in settings Changed out BatteryMeterDrawable to inherit from ThemedBatteryDrawable instead of BatteryMeterDrawableBase. Also removed warning text paint because it seemed unused and simplified the interface. Bug: 123705805 Test: visual Change-Id: I30496e3d8881803d9d3d8a316c10387482a8f610 --- ...ancedBluetoothDetailsHeaderController.java | 3 +- .../settings/fuelgauge/BatteryMeterView.java | 31 ++++++------------- .../fuelgauge/BatteryMeterViewTest.java | 4 +-- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java index 4b8efd4e371..f91b4f9c3db 100644 --- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java @@ -98,8 +98,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont new BatteryMeterView.BatteryMeterDrawable(context, context.getColor(R.color.meter_background_color)); drawable.setBatteryLevel(level); - drawable.setShowPercent(false); - drawable.setBatteryColorFilter(new PorterDuffColorFilter( + drawable.setColorFilter(new PorterDuffColorFilter( com.android.settings.Utils.getColorAttrDefaultColor(context, android.R.attr.colorControlNormal), PorterDuff.Mode.SRC_IN)); diff --git a/src/com/android/settings/fuelgauge/BatteryMeterView.java b/src/com/android/settings/fuelgauge/BatteryMeterView.java index 822f0e9a190..aa26ff45553 100644 --- a/src/com/android/settings/fuelgauge/BatteryMeterView.java +++ b/src/com/android/settings/fuelgauge/BatteryMeterView.java @@ -29,7 +29,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.Utils; -import com.android.settingslib.graph.BatteryMeterDrawableBase; +import com.android.settingslib.graph.ThemedBatteryDrawable; public class BatteryMeterView extends ImageView { @VisibleForTesting @@ -54,29 +54,27 @@ public class BatteryMeterView extends ImageView { final int frameColor = context.getColor(R.color.meter_background_color); mAccentColorFilter = new PorterDuffColorFilter( Utils.getColorAttrDefaultColor(context, android.R.attr.colorAccent), - PorterDuff.Mode.SRC_IN); + PorterDuff.Mode.SRC); mErrorColorFilter = new PorterDuffColorFilter( context.getColor(R.color.battery_icon_color_error), PorterDuff.Mode.SRC_IN); mDrawable = new BatteryMeterDrawable(context, frameColor); - mDrawable.setShowPercent(false); - mDrawable.setBatteryColorFilter(mAccentColorFilter); - mDrawable.setWarningColorFilter( - new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + mDrawable.setColorFilter(mAccentColorFilter); setImageDrawable(mDrawable); + setLayerType(LAYER_TYPE_SOFTWARE, null); } public void setBatteryLevel(int level) { mDrawable.setBatteryLevel(level); if (level < mDrawable.getCriticalLevel()) { - mDrawable.setBatteryColorFilter(mErrorColorFilter); + mDrawable.setColorFilter(mErrorColorFilter); } else { - mDrawable.setBatteryColorFilter(mAccentColorFilter); + mDrawable.setColorFilter(mAccentColorFilter); } } public void setPowerSave(boolean powerSave) { - mDrawable.setPowerSave(powerSave); + mDrawable.setPowerSaveEnabled(powerSave); mPowerSaveEnabled = powerSave; } @@ -85,7 +83,7 @@ public class BatteryMeterView extends ImageView { } public int getBatteryLevel() { - return mDrawable.getBatteryLevel(); + return mDrawable.getLevel(); } public void setCharging(boolean charging) { @@ -97,7 +95,7 @@ public class BatteryMeterView extends ImageView { return mDrawable.getCharging(); } - public static class BatteryMeterDrawable extends BatteryMeterDrawableBase { + public static class BatteryMeterDrawable extends ThemedBatteryDrawable { private final int mIntrinsicWidth; private final int mIntrinsicHeight; @@ -119,16 +117,5 @@ public class BatteryMeterView extends ImageView { public int getIntrinsicHeight() { return mIntrinsicHeight; } - - public void setWarningColorFilter(@Nullable ColorFilter colorFilter) { - mWarningTextPaint.setColorFilter(colorFilter); - } - - public void setBatteryColorFilter(@Nullable ColorFilter colorFilter) { - mFramePaint.setColorFilter(colorFilter); - mBatteryPaint.setColorFilter(colorFilter); - mBoltPaint.setColorFilter(colorFilter); - } } - } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java index 4bdcd5535b3..3e994334f9e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java @@ -73,14 +73,14 @@ public class BatteryMeterViewTest { public void testSetBatteryInfo_levelLow_setErrorColor() { mBatteryMeterView.setBatteryLevel(BATTERY_LOW_LEVEL); - verify(mDrawable).setBatteryColorFilter(mErrorColorFilter); + verify(mDrawable).setColorFilter(mErrorColorFilter); } @Test public void testSetBatteryInfo_levelNormal_setNormalColor() { mBatteryMeterView.setBatteryLevel(BATTERY_LEVEL); - verify(mDrawable).setBatteryColorFilter(mAccentColorFilter); + verify(mDrawable).setColorFilter(mAccentColorFilter); } @Test From 7d0d684339c172b05562e51ba1a3c4936b7d2a24 Mon Sep 17 00:00:00 2001 From: Sunil Ravi Date: Tue, 26 Feb 2019 11:43:03 -0800 Subject: [PATCH 03/15] WiFi: Replace -1 with LINK_SPEED_UNKNOWN constant Defined constant LINK_SPEED_UNKNOWN = -1 in WiFiInfo for unknown link speeds. So replacing -1 with this new macro. Bug: 124302657 Test: Connect STA to AP and verify the link speeds in network details. Change-Id: Ie21069984adb02420ca55b08a8cb161aaad2b7b3 --- src/com/android/settings/wifi/WifiConfigController.java | 4 ++-- .../wifi/details/WifiDetailPreferenceControllerTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java index 1d78485a94b..59d22de6742 100644 --- a/src/com/android/settings/wifi/WifiConfigController.java +++ b/src/com/android/settings/wifi/WifiConfigController.java @@ -376,12 +376,12 @@ public class WifiConfigController implements TextWatcher, } WifiInfo info = mAccessPoint.getInfo(); - if (info != null && info.getTxLinkSpeedMbps() != -1) { + if (info != null && info.getTxLinkSpeedMbps() != WifiInfo.LINK_SPEED_UNKNOWN) { addRow(group, R.string.tx_wifi_speed, String.format( res.getString(R.string.tx_link_speed), info.getTxLinkSpeedMbps())); } - if (info != null && info.getRxLinkSpeedMbps() != -1) { + if (info != null && info.getRxLinkSpeedMbps() != WifiInfo.LINK_SPEED_UNKNOWN) { addRow(group, R.string.rx_wifi_speed, String.format( res.getString(R.string.rx_link_speed), info.getRxLinkSpeedMbps())); } diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index 308d4b57cc1..3944f84072e 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -429,7 +429,7 @@ public class WifiDetailPreferenceControllerTest { @Test public void linkSpeedPref_shouldNotShowIfNotSet() { - when(mockWifiInfo.getTxLinkSpeedMbps()).thenReturn(-1); + when(mockWifiInfo.getTxLinkSpeedMbps()).thenReturn(WifiInfo.LINK_SPEED_UNKNOWN); displayAndResume(); @@ -447,7 +447,7 @@ public class WifiDetailPreferenceControllerTest { @Test public void rxLinkSpeedPref_shouldNotShowIfNotSet() { - when(mockWifiInfo.getRxLinkSpeedMbps()).thenReturn(-1); + when(mockWifiInfo.getRxLinkSpeedMbps()).thenReturn(WifiInfo.LINK_SPEED_UNKNOWN); displayAndResume(); From b27c4308a2ab839cff4f4751451440f6dc752e8e Mon Sep 17 00:00:00 2001 From: Rich Cannings Date: Tue, 19 Feb 2019 13:15:30 -0800 Subject: [PATCH 04/15] Refactor passwords/pins/patterns to byte[] Relating to packages/apps/Settings Bug: 120484642 Test: manual - test setting and unlocking passwords/pins/patterns. automated - 20 of about 500 tests fail due to fragile synthetic password test code. Change-Id: Idec8338d141c185bef67ade12035fdb2fa9d17ea --- .../android/settings/CryptKeeperConfirm.java | 8 +- .../settings/password/ChooseLockGeneric.java | 15 ++-- .../settings/password/ChooseLockPassword.java | 77 ++++++++++++------- .../settings/password/ChooseLockPattern.java | 37 +++++---- .../password/ConfirmLockPassword.java | 9 ++- .../settings/password/ConfirmLockPattern.java | 4 +- .../password/ManagedLockPasswordProvider.java | 2 +- .../security/CryptKeeperSettings.java | 14 ++-- .../LockUnificationPreferenceController.java | 10 +-- .../password/ChooseLockPasswordTest.java | 10 ++- .../password/ChooseLockPatternTest.java | 6 +- .../shadow/ShadowLockPatternUtils.java | 4 +- 12 files changed, 116 insertions(+), 80 deletions(-) diff --git a/src/com/android/settings/CryptKeeperConfirm.java b/src/com/android/settings/CryptKeeperConfirm.java index 248494877d3..49d027ba492 100644 --- a/src/com/android/settings/CryptKeeperConfirm.java +++ b/src/com/android/settings/CryptKeeperConfirm.java @@ -38,6 +38,7 @@ import android.widget.Button; import com.android.internal.widget.LockPatternUtils; import com.android.settings.core.InstrumentedFragment; +import java.util.Arrays; import java.util.Locale; public class CryptKeeperConfirm extends InstrumentedFragment { @@ -87,7 +88,12 @@ public class CryptKeeperConfirm extends InstrumentedFragment { IStorageManager storageManager = IStorageManager.Stub.asInterface(service); try { Bundle args = getIntent().getExtras(); - storageManager.encryptStorage(args.getInt("type", -1), args.getString("password")); + // TODO(b/120484642): Update vold to accept a password as a byte array + byte[] passwordBytes = args.getByteArray("password"); + String password = passwordBytes != null ? new String(passwordBytes) : null; + Arrays.fill(passwordBytes, (byte) 0); + storageManager.encryptStorage(args.getInt("type", -1), + password); } catch (Exception e) { Log.e("CryptKeeper", "Error while encrypting...", e); } diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index cf6211246f0..4a758be809a 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -76,6 +76,7 @@ import com.android.settingslib.RestrictedPreference; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.FooterPreferenceMixinCompat; +import java.util.Arrays; import java.util.List; public class ChooseLockGeneric extends SettingsActivity { @@ -151,7 +152,7 @@ public class ChooseLockGeneric extends SettingsActivity { private boolean mPasswordConfirmed = false; private boolean mWaitingForConfirmation = false; private boolean mForChangeCredRequiredForBoot = false; - private String mUserPassword; + private byte[] mUserPassword; private LockPatternUtils mLockPatternUtils; private FingerprintManager mFingerprintManager; private FaceManager mFaceManager; @@ -200,7 +201,7 @@ public class ChooseLockGeneric extends SettingsActivity { .getBooleanExtra(CONFIRM_CREDENTIALS, true); if (getActivity() instanceof ChooseLockGeneric.InternalActivity) { mPasswordConfirmed = !confirmCredentials; - mUserPassword = getActivity().getIntent().getStringExtra( + mUserPassword = getActivity().getIntent().getByteArrayExtra( ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); } @@ -224,7 +225,7 @@ public class ChooseLockGeneric extends SettingsActivity { mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED); mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION); if (mUserPassword == null) { - mUserPassword = savedInstanceState.getString( + mUserPassword = savedInstanceState.getByteArray( ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); } } @@ -383,11 +384,11 @@ public class ChooseLockGeneric extends SettingsActivity { if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) { mPasswordConfirmed = true; mUserPassword = data != null - ? data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD) + ? data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD) : null; updatePreferencesOrFinish(false /* isRecreatingActivity */); if (mForChangeCredRequiredForBoot) { - if (!TextUtils.isEmpty(mUserPassword)) { + if (!(mUserPassword == null || mUserPassword.length == 0)) { maybeEnableEncryption( mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false); } else { @@ -447,7 +448,7 @@ public class ChooseLockGeneric extends SettingsActivity { outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed); outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation); if (mUserPassword != null) { - outState.putString(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword); + outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword); } } @@ -669,7 +670,7 @@ public class ChooseLockGeneric extends SettingsActivity { setPreferenceSummary(ScreenLockType.MANAGED, R.string.secure_lock_encryption_warning); } - protected Intent getLockManagedPasswordIntent(String password) { + protected Intent getLockManagedPasswordIntent(byte[] password) { return mManagedPasswordProvider.createIntent(false, password); } diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index cd481823ec0..c9d1af339d8 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -78,6 +78,7 @@ import com.google.android.setupcompat.template.FooterButton; import com.google.android.setupdesign.GlifLayout; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class ChooseLockPassword extends SettingsActivity { @@ -123,7 +124,7 @@ public class ChooseLockPassword extends SettingsActivity { return this; } - public IntentBuilder setPassword(String password) { + public IntentBuilder setPassword(byte[] password) { mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password); return this; } @@ -185,8 +186,8 @@ public class ChooseLockPassword extends SettingsActivity { private static final String KEY_CURRENT_PASSWORD = "current_password"; private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker"; - private String mCurrentPassword; - private String mChosenPassword; + private byte[] mCurrentPassword; + private byte[] mChosenPassword; private boolean mHasChallenge; private long mChallenge; private ImeAwareEditText mPasswordEntry; @@ -215,7 +216,7 @@ public class ChooseLockPassword extends SettingsActivity { protected boolean mForFingerprint; protected boolean mForFace; - private String mFirstPin; + private byte[] mFirstPin; private RecyclerView mPasswordRestrictionView; protected boolean mIsAlphaMode; protected FooterButton mSkipOrClearButton; @@ -234,7 +235,7 @@ public class ChooseLockPassword extends SettingsActivity { private static final int MIN_NUMBER_IN_PASSWORD = 4; private static final int MIN_NON_LETTER_IN_PASSWORD = 5; - // Error code returned from {@link #validatePassword(String)}. + // Error code returned from {@link #validatePassword(byte[])}. static final int NO_ERROR = 0; static final int CONTAIN_INVALID_CHARACTERS = 1 << 0; static final int TOO_SHORT = 1 << 1; @@ -394,12 +395,13 @@ public class ChooseLockPassword extends SettingsActivity { SaveAndFinishWorker w = new SaveAndFinishWorker(); final boolean required = getActivity().getIntent().getBooleanExtra( EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true); - String current = intent.getStringExtra( + byte[] currentBytes = intent.getByteArrayExtra( ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); + w.setBlocking(true); w.setListener(this); - w.start(mChooseLockSettingsHelper.utils(), required, - false, 0, current, current, mRequestedQuality, mUserId); + w.start(mChooseLockSettingsHelper.utils(), required, false, 0, + currentBytes, currentBytes, mRequestedQuality, mUserId); } mTextChangedHandler = new TextChangedHandler(); } @@ -474,7 +476,8 @@ public class ChooseLockPassword extends SettingsActivity { Intent intent = getActivity().getIntent(); final boolean confirmCredentials = intent.getBooleanExtra( ChooseLockGeneric.CONFIRM_CREDENTIALS, true); - mCurrentPassword = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); + mCurrentPassword = intent.getByteArrayExtra( + ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); mHasChallenge = intent.getBooleanExtra( ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false); mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0); @@ -486,8 +489,9 @@ public class ChooseLockPassword extends SettingsActivity { mUserId); } } else { + // restore from previous state - mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN); + mFirstPin = savedInstanceState.getByteArray(KEY_FIRST_PIN); final String state = savedInstanceState.getString(KEY_UI_STAGE); if (state != null) { mUiStage = Stage.valueOf(state); @@ -495,7 +499,7 @@ public class ChooseLockPassword extends SettingsActivity { } if (mCurrentPassword == null) { - mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD); + mCurrentPassword = savedInstanceState.getByteArray(KEY_CURRENT_PASSWORD); } // Re-attach to the exiting worker if there is one. @@ -553,8 +557,8 @@ public class ChooseLockPassword extends SettingsActivity { public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_UI_STAGE, mUiStage.name()); - outState.putString(KEY_FIRST_PIN, mFirstPin); - outState.putString(KEY_CURRENT_PASSWORD, mCurrentPassword); + outState.putByteArray(KEY_FIRST_PIN, mFirstPin); + outState.putByteArray(KEY_CURRENT_PASSWORD, mCurrentPassword); } @Override @@ -567,7 +571,7 @@ public class ChooseLockPassword extends SettingsActivity { getActivity().setResult(RESULT_FINISHED); getActivity().finish(); } else { - mCurrentPassword = data.getStringExtra( + mCurrentPassword = data.getByteArrayExtra( ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); } break; @@ -712,22 +716,22 @@ public class ChooseLockPassword extends SettingsActivity { * @return the validation result. */ @VisibleForTesting - int validatePassword(String password) { + int validatePassword(byte[] password) { int errorCode = NO_ERROR; final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password); mergeMinComplexityAndDpmRequirements(metrics.quality); - if (password.length() < mPasswordMinLength) { + if (password == null || password.length < mPasswordMinLength) { if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) { errorCode |= TOO_SHORT; } - } else if (password.length() > mPasswordMaxLength) { + } else if (password.length > mPasswordMaxLength) { errorCode |= TOO_LONG; } else { // The length requirements are fulfilled. if (!mPasswordNumSequenceAllowed && !requiresLettersOrSymbols() - && metrics.numeric == password.length()) { + && metrics.numeric == password.length) { // Check for repeated characters or sequences (e.g. '1234', '0000', '2468') // if DevicePolicyManager or min password complexity requires a complex numeric // password. There can be two cases in the UI: 1. User chooses to enroll a @@ -757,8 +761,8 @@ public class ChooseLockPassword extends SettingsActivity { } // Allow non-control Latin-1 characters only. - for (int i = 0; i < password.length(); i++) { - char c = password.charAt(i); + for (int i = 0; i < password.length; i++) { + char c = (char) password[i]; if (c < 32 || c > 127) { errorCode |= CONTAIN_INVALID_CHARACTERS; break; @@ -809,8 +813,9 @@ public class ChooseLockPassword extends SettingsActivity { public void handleNext() { if (mSaveAndFinishWorker != null) return; - mChosenPassword = mPasswordEntry.getText().toString(); - if (TextUtils.isEmpty(mChosenPassword)) { + // TODO(b/120484642): This is a point of entry for passwords from the UI + mChosenPassword = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText()); + if (mChosenPassword == null || mChosenPassword.length == 0) { return; } if (mUiStage == Stage.Introduction) { @@ -818,9 +823,11 @@ public class ChooseLockPassword extends SettingsActivity { mFirstPin = mChosenPassword; mPasswordEntry.setText(""); updateStage(Stage.NeedToConfirm); + } else { + Arrays.fill(mChosenPassword, (byte) 0); } } else if (mUiStage == Stage.NeedToConfirm) { - if (mFirstPin.equals(mChosenPassword)) { + if (Arrays.equals(mFirstPin, mChosenPassword)) { startSaveAndFinish(); } else { CharSequence tmp = mPasswordEntry.getText(); @@ -828,6 +835,7 @@ public class ChooseLockPassword extends SettingsActivity { Selection.setSelection((Spannable) tmp, 0, tmp.length()); } updateStage(Stage.ConfirmWrong); + Arrays.fill(mChosenPassword, (byte) 0); } } } @@ -940,8 +948,8 @@ public class ChooseLockPassword extends SettingsActivity { */ protected void updateUi() { final boolean canInput = mSaveAndFinishWorker == null; - String password = mPasswordEntry.getText().toString(); - final int length = password.length(); + byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText()); + final int length = password.length; if (mUiStage == Stage.Introduction) { mPasswordRestrictionView.setVisibility(View.VISIBLE); final int errorCode = validatePassword(password); @@ -967,6 +975,7 @@ public class ChooseLockPassword extends SettingsActivity { setNextText(mUiStage.buttonText); mPasswordEntryInputDisabler.setInputEnabled(canInput); + Arrays.fill(password, (byte) 0); } protected int toVisibility(boolean visibleOrGone) { @@ -1025,6 +1034,18 @@ public class ChooseLockPassword extends SettingsActivity { public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) { getActivity().setResult(RESULT_FINISHED, resultData); + if (mChosenPassword != null) { + Arrays.fill(mChosenPassword, (byte) 0); + } + if (mCurrentPassword != null) { + Arrays.fill(mCurrentPassword, (byte) 0); + } + if (mFirstPin != null) { + Arrays.fill(mFirstPin, (byte) 0); + } + + mPasswordEntry.setText(""); + if (!wasSecureBefore) { Intent intent = getRedactionInterstitialIntent(getActivity()); if (intent != null) { @@ -1061,13 +1082,13 @@ public class ChooseLockPassword extends SettingsActivity { public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase { - private String mChosenPassword; - private String mCurrentPassword; + private byte[] mChosenPassword; + private byte[] mCurrentPassword; private int mRequestedQuality; public void start(LockPatternUtils utils, boolean required, boolean hasChallenge, long challenge, - String chosenPassword, String currentPassword, int requestedQuality, int userId) { + byte[] chosenPassword, byte[] currentPassword, int requestedQuality, int userId) { prepare(utils, required, hasChallenge, challenge, userId); mChosenPassword = chosenPassword; diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index 584cc6162e3..d5ad1abfd70 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -55,6 +55,7 @@ import com.google.android.setupcompat.template.FooterButton; import com.google.android.setupdesign.GlifLayout; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -114,7 +115,7 @@ public class ChooseLockPattern extends SettingsActivity { return this; } - public IntentBuilder setPattern(String pattern) { + public IntentBuilder setPattern(byte[] pattern) { mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pattern); return this; } @@ -187,7 +188,7 @@ public class ChooseLockPattern extends SettingsActivity { private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker"; - private String mCurrentPattern; + private byte[] mCurrentPattern; private boolean mHasChallenge; private long mChallenge; protected TextView mTitleText; @@ -224,7 +225,7 @@ public class ChooseLockPattern extends SettingsActivity { getActivity().setResult(RESULT_FINISHED); getActivity().finish(); } else { - mCurrentPattern = data.getStringExtra( + mCurrentPattern = data.getByteArrayExtra( ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); } @@ -457,12 +458,12 @@ public class ChooseLockPattern extends SettingsActivity { SaveAndFinishWorker w = new SaveAndFinishWorker(); final boolean required = getActivity().getIntent().getBooleanExtra( EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true); - String current = intent.getStringExtra( + byte[] current = intent.getByteArrayExtra( ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); w.setBlocking(true); w.setListener(this); w.start(mChooseLockSettingsHelper.utils(), required, - false, 0, LockPatternUtils.stringToPattern(current), current, mUserId); + false, 0, LockPatternUtils.byteArrayToPattern(current), current, mUserId); } mForFingerprint = intent.getBooleanExtra( ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false); @@ -540,7 +541,8 @@ public class ChooseLockPattern extends SettingsActivity { final boolean confirmCredentials = getActivity().getIntent() .getBooleanExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, true); Intent intent = getActivity().getIntent(); - mCurrentPattern = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); + mCurrentPattern = + intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); mHasChallenge = intent.getBooleanExtra( ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false); mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0); @@ -563,13 +565,13 @@ public class ChooseLockPattern extends SettingsActivity { } } else { // restore from previous state - final String patternString = savedInstanceState.getString(KEY_PATTERN_CHOICE); - if (patternString != null) { - mChosenPattern = LockPatternUtils.stringToPattern(patternString); + final byte[] pattern = savedInstanceState.getByteArray(KEY_PATTERN_CHOICE); + if (pattern != null) { + mChosenPattern = LockPatternUtils.byteArrayToPattern(pattern); } if (mCurrentPattern == null) { - mCurrentPattern = savedInstanceState.getString(KEY_CURRENT_PATTERN); + mCurrentPattern = savedInstanceState.getByteArray(KEY_CURRENT_PATTERN); } updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]); @@ -665,13 +667,12 @@ public class ChooseLockPattern extends SettingsActivity { outState.putInt(KEY_UI_STAGE, mUiStage.ordinal()); if (mChosenPattern != null) { - outState.putString(KEY_PATTERN_CHOICE, - LockPatternUtils.patternToString(mChosenPattern)); + outState.putByteArray(KEY_PATTERN_CHOICE, + LockPatternUtils.patternToByteArray(mChosenPattern)); } if (mCurrentPattern != null) { - outState.putString(KEY_CURRENT_PATTERN, - mCurrentPattern); + outState.putByteArray(KEY_CURRENT_PATTERN, mCurrentPattern); } } @@ -818,6 +819,10 @@ public class ChooseLockPattern extends SettingsActivity { public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) { getActivity().setResult(RESULT_FINISHED, resultData); + if (mCurrentPattern != null) { + Arrays.fill(mCurrentPattern, (byte) 0); + } + if (!wasSecureBefore) { Intent intent = getRedactionInterstitialIntent(getActivity()); if (intent != null) { @@ -831,12 +836,12 @@ public class ChooseLockPattern extends SettingsActivity { public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase { private List mChosenPattern; - private String mCurrentPattern; + private byte[] mCurrentPattern; private boolean mLockVirgin; public void start(LockPatternUtils utils, boolean credentialRequired, boolean hasChallenge, long challenge, - List chosenPattern, String currentPattern, int userId) { + List chosenPattern, byte[] currentPattern, int userId) { prepare(utils, credentialRequired, hasChallenge, challenge, userId); mCurrentPattern = currentPattern; diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java index f8938703bd7..6c619674d5f 100644 --- a/src/com/android/settings/password/ConfirmLockPassword.java +++ b/src/com/android/settings/password/ConfirmLockPassword.java @@ -323,8 +323,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { return; } - final String pin = mPasswordEntry.getText().toString(); - if (TextUtils.isEmpty(pin)) { + // TODO(b/120484642): This is a point of entry for passwords from the UI + final byte[] pin = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText()); + if (pin == null || pin.length == 0) { return; } @@ -350,7 +351,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { return getActivity() instanceof ConfirmLockPassword.InternalActivity; } - private void startVerifyPassword(final String pin, final Intent intent) { + private void startVerifyPassword(final byte[] pin, final Intent intent) { long challenge = getActivity().getIntent().getLongExtra( ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0); final int localEffectiveUserId = mEffectiveUserId; @@ -381,7 +382,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { onVerifyCallback); } - private void startCheckPassword(final String pin, final Intent intent) { + private void startCheckPassword(final byte[] pin, final Intent intent) { final int localEffectiveUserId = mEffectiveUserId; mPendingLockCheck = LockPatternChecker.checkPassword( mLockPatternUtils, diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java index 29cdfef0516..6d3d6e56cfc 100644 --- a/src/com/android/settings/password/ConfirmLockPattern.java +++ b/src/com/android/settings/password/ConfirmLockPattern.java @@ -448,7 +448,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { mLockPatternUtils, pattern, challenge, localUserId, onVerifyCallback) : LockPatternChecker.verifyTiedProfileChallenge( - mLockPatternUtils, LockPatternUtils.patternToString(pattern), + mLockPatternUtils, LockPatternUtils.patternToByteArray(pattern), true, challenge, localUserId, onVerifyCallback); } @@ -473,7 +473,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE, StorageManager.CRYPT_TYPE_PATTERN); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, - LockPatternUtils.patternToString(pattern)); + LockPatternUtils.patternToByteArray(pattern)); } mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs, localEffectiveUserId); diff --git a/src/com/android/settings/password/ManagedLockPasswordProvider.java b/src/com/android/settings/password/ManagedLockPasswordProvider.java index 2494af556ed..5006926feb1 100644 --- a/src/com/android/settings/password/ManagedLockPasswordProvider.java +++ b/src/com/android/settings/password/ManagedLockPasswordProvider.java @@ -59,7 +59,7 @@ public class ManagedLockPasswordProvider { * @param password Current lock password. * @return Intent that should update lock password to a managed password. */ - Intent createIntent(boolean requirePasswordToDecrypt, String password) { + Intent createIntent(boolean requirePasswordToDecrypt, byte[] password) { return null; } } diff --git a/src/com/android/settings/security/CryptKeeperSettings.java b/src/com/android/settings/security/CryptKeeperSettings.java index c80ad115ff5..6555f568947 100644 --- a/src/com/android/settings/security/CryptKeeperSettings.java +++ b/src/com/android/settings/security/CryptKeeperSettings.java @@ -173,7 +173,7 @@ public class CryptKeeperSettings extends InstrumentedPreferenceFragment { if (helper.utils().getKeyguardStoredPasswordQuality(UserHandle.myUserId()) == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - showFinalConfirmation(StorageManager.CRYPT_TYPE_DEFAULT, ""); + showFinalConfirmation(StorageManager.CRYPT_TYPE_DEFAULT, "".getBytes()); return true; } @@ -193,14 +193,14 @@ public class CryptKeeperSettings extends InstrumentedPreferenceFragment { // confirmation prompt; otherwise, go back to the initial state. if (resultCode == Activity.RESULT_OK && data != null) { int type = data.getIntExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE, -1); - String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); - if (!TextUtils.isEmpty(password)) { + byte[] password = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); + if (!(password == null || password.length == 0)) { showFinalConfirmation(type, password); } } } - private void showFinalConfirmation(int type, String password) { + private void showFinalConfirmation(int type, byte[] password) { Preference preference = new Preference(getPreferenceManager().getContext()); preference.setFragment(CryptKeeperConfirm.class.getName()); preference.setTitle(R.string.crypt_keeper_confirm_title); @@ -208,16 +208,16 @@ public class CryptKeeperSettings extends InstrumentedPreferenceFragment { ((SettingsActivity) getActivity()).onPreferenceStartFragment(null, preference); } - private void addEncryptionInfoToPreference(Preference preference, int type, String password) { + private void addEncryptionInfoToPreference(Preference preference, int type, byte[] password) { Activity activity = getActivity(); DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); if (dpm.getDoNotAskCredentialsOnBoot()) { preference.getExtras().putInt(TYPE, StorageManager.CRYPT_TYPE_DEFAULT); - preference.getExtras().putString(PASSWORD, ""); + preference.getExtras().putByteArray(PASSWORD, "".getBytes()); } else { preference.getExtras().putInt(TYPE, type); - preference.getExtras().putString(PASSWORD, password); + preference.getExtras().putByteArray(PASSWORD, password); } } } diff --git a/src/com/android/settings/security/LockUnificationPreferenceController.java b/src/com/android/settings/security/LockUnificationPreferenceController.java index 978986c4c83..bf374de81cf 100644 --- a/src/com/android/settings/security/LockUnificationPreferenceController.java +++ b/src/com/android/settings/security/LockUnificationPreferenceController.java @@ -70,8 +70,8 @@ public class LockUnificationPreferenceController extends AbstractPreferenceContr private RestrictedSwitchPreference mUnifyProfile; - private String mCurrentDevicePassword; - private String mCurrentProfilePassword; + private byte[] mCurrentDevicePassword; + private byte[] mCurrentProfilePassword; private boolean mKeepDeviceLock; @Override @@ -151,13 +151,13 @@ public class LockUnificationPreferenceController extends AbstractPreferenceContr } else if (requestCode == UNIFY_LOCK_CONFIRM_DEVICE_REQUEST && resultCode == Activity.RESULT_OK) { mCurrentDevicePassword = - data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); + data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); launchConfirmProfileLock(); return true; } else if (requestCode == UNIFY_LOCK_CONFIRM_PROFILE_REQUEST && resultCode == Activity.RESULT_OK) { mCurrentProfilePassword = - data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); + data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); unifyLocks(); return true; } @@ -226,7 +226,7 @@ public class LockUnificationPreferenceController extends AbstractPreferenceContr // PASSWORD_QUALITY_SOMETHING means pattern, everything above means PIN/password. if (profileQuality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { mLockPatternUtils.saveLockPattern( - LockPatternUtils.stringToPattern(mCurrentProfilePassword), + LockPatternUtils.byteArrayToPattern(mCurrentProfilePassword), mCurrentDevicePassword, MY_USER_ID); } else { mLockPatternUtils.saveLockPassword( diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java index 404d2057aa4..73707a63012 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java @@ -87,7 +87,7 @@ public class ChooseLockPasswordTest { @Test public void intentBuilder_setPassword_shouldAddExtras() { Intent intent = new IntentBuilder(application) - .setPassword("password") + .setPassword("password".getBytes()) .setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) .setUserId(123) .build(); @@ -95,9 +95,9 @@ public class ChooseLockPasswordTest { assertThat(intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true)) .named("EXTRA_KEY_HAS_CHALLENGE") .isFalse(); - assertThat(intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)) + assertThat(intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)) .named("EXTRA_KEY_PASSWORD") - .isEqualTo("password"); + .isEqualTo("password".getBytes()); assertThat(intent.getIntExtra(PASSWORD_TYPE_KEY, 0)) .named("PASSWORD_TYPE_KEY") .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); @@ -366,7 +366,9 @@ public class ChooseLockPasswordTest { intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity); ChooseLockPassword activity = buildChooseLockPasswordActivity(intent); ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity); - int validateResult = fragment.validatePassword(userEnteredPassword); + byte[] userEnteredPasswordBytes = userEnteredPassword != null + ? userEnteredPassword.getBytes() : null; + int validateResult = fragment.validatePassword(userEnteredPasswordBytes); String[] messages = fragment.convertErrorCodeToMessages(validateResult); assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult); diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java index 6b1029c7237..3509d75ab81 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java @@ -52,7 +52,7 @@ public class ChooseLockPatternTest { @Test public void intentBuilder_setPattern_shouldAddExtras() { Intent intent = new IntentBuilder(application) - .setPattern("pattern") + .setPattern("pattern".getBytes()) .setUserId(123) .build(); @@ -61,9 +61,9 @@ public class ChooseLockPatternTest { .named("EXTRA_KEY_HAS_CHALLENGE") .isFalse(); assertThat(intent - .getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)) + .getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)) .named("EXTRA_KEY_PASSWORD") - .isEqualTo("pattern"); + .isEqualTo("pattern".getBytes()); assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0)) .named("EXTRA_USER_ID") .isEqualTo(123); diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java index f2870854750..e9be346eb20 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java @@ -66,12 +66,12 @@ public class ShadowLockPatternUtils { } @Implementation - protected byte[] getPasswordHistoryHashFactor(String currentPassword, int userId) { + protected byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) { return null; } @Implementation - protected boolean checkPasswordHistory(String passwordToCheck, byte[] hashFactor, int userId) { + protected boolean checkPasswordHistory(byte[] passwordToCheck, byte[] hashFactor, int userId) { return false; } } From a78b9222b0a2cfa8ad5f21d992f3f2fd5133b798 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Fri, 15 Feb 2019 09:26:22 +0800 Subject: [PATCH 05/15] Duplicate dark mode settings to accessibility display settings Bug: 123227114 Test: Manual make RunSettingsRoboTests ROBOTEST_FILTER=AccessibilitySettings Change-Id: Icb60bc18e31377a2e73780004bd1bdea7bc96eea --- res/xml/accessibility_settings.xml | 8 +++++ .../accessibility/AccessibilitySettings.java | 15 +++++++++ .../AccessibilitySettingsTest.java | 32 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml index 9cb73a2ea6b..da103c57bf4 100644 --- a/res/xml/accessibility_settings.xml +++ b/res/xml/accessibility_settings.xml @@ -58,6 +58,14 @@ android:title="@string/screen_zoom_title" settings:searchable="false"/> + + Date: Tue, 26 Feb 2019 08:51:10 +0800 Subject: [PATCH 06/15] Use Device Policy dialog in switchbar We need to pop dialog when switchbar is disabled by Device Policy Test: visual, robolectric Fixes: 124855614 Change-Id: Ief65baa2c4912cb7c85d5eccc2c0f363d2fc898b --- .../users/MultiUserSwitchBarController.java | 22 ++++- .../testutils/shadow/ShadowUserManager.java | 17 ++++ .../MultiUserSwitchBarControllerTest.java | 83 +++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java diff --git a/src/com/android/settings/users/MultiUserSwitchBarController.java b/src/com/android/settings/users/MultiUserSwitchBarController.java index 9588f714648..58de14963fa 100644 --- a/src/com/android/settings/users/MultiUserSwitchBarController.java +++ b/src/com/android/settings/users/MultiUserSwitchBarController.java @@ -17,27 +17,34 @@ package com.android.settings.users; import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.Log; +import androidx.annotation.VisibleForTesting; + import com.android.settings.widget.SwitchWidgetController; +import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; public class MultiUserSwitchBarController implements SwitchWidgetController.OnSwitchChangeListener, LifecycleObserver, OnStart, OnStop { + private static final String TAG = "MultiUserSwitchBarCtrl"; interface OnMultiUserSwitchChangedListener { void onMultiUserSwitchChanged(boolean newState); } + @VisibleForTesting + final SwitchWidgetController mSwitchBar; - private static final String TAG = "MultiUserSwitchBarCtrl"; private final Context mContext; - private final SwitchWidgetController mSwitchBar; private final UserCapabilities mUserCapabilities; private final OnMultiUserSwitchChangedListener mListener; + MultiUserSwitchBarController(Context context, SwitchWidgetController switchBar, OnMultiUserSwitchChangedListener listener) { mContext = context; @@ -45,8 +52,15 @@ public class MultiUserSwitchBarController implements SwitchWidgetController.OnSw mListener = listener; mUserCapabilities = UserCapabilities.create(context); mSwitchBar.setChecked(mUserCapabilities.mUserSwitcherEnabled); - mSwitchBar.setEnabled(!mUserCapabilities.mDisallowSwitchUser - && !mUserCapabilities.mIsGuest && mUserCapabilities.isAdmin()); + + if (mUserCapabilities.mDisallowSwitchUser) { + mSwitchBar.setDisabledByAdmin(RestrictedLockUtilsInternal + .checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_USER_SWITCH, + UserHandle.myUserId())); + } else { + mSwitchBar.setEnabled(!mUserCapabilities.mDisallowSwitchUser + && !mUserCapabilities.mIsGuest && mUserCapabilities.isAdmin()); + } mSwitchBar.setListener(this); } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java index 9a18c1f9aaf..bceba3c9864 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java @@ -26,6 +26,7 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadow.api.Shadow; +import org.robolectric.annotation.Resetter; import java.util.ArrayList; import java.util.Collections; @@ -38,6 +39,8 @@ import java.util.Set; @Implements(value = UserManager.class) public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { + private static boolean sIsSupportsMultipleUsers; + private final List mRestrictions = new ArrayList<>(); private final Map> mRestrictionSources = new HashMap<>(); private final List mUserProfileInfos = new ArrayList<>(); @@ -50,6 +53,11 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager mUserProfileInfos.add(userInfo); } + @Resetter + public static void reset() { + sIsSupportsMultipleUsers = false; + } + @Implementation protected List getProfiles(@UserIdInt int userHandle) { return mUserProfileInfos; @@ -124,4 +132,13 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager public void setUserSwitcherEnabled(boolean userSwitchEnabled) { mUserSwitchEnabled = userSwitchEnabled; } + + @Implementation + protected static boolean supportsMultipleUsers() { + return sIsSupportsMultipleUsers; + } + + public void setSupportsMultipleUsers(boolean supports) { + sIsSupportsMultipleUsers = supports; + } } diff --git a/tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java new file mode 100644 index 00000000000..718e01bebe0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 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.users; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.settings.testutils.shadow.ShadowUserManager; +import com.android.settings.widget.SwitchBar; +import com.android.settings.widget.SwitchBarController; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowUserManager.class}) +public class MultiUserSwitchBarControllerTest { + + private Context mContext; + private ShadowUserManager mUserManager; + private SwitchBarController mSwitchBarController; + + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mUserManager = ShadowUserManager.getShadow(); + mSwitchBarController = spy(new SwitchBarController(new SwitchBar(mContext))); + mUserManager.setSupportsMultipleUsers(true); + } + + @After + public void tearDown() { + ShadowUserManager.reset(); + } + + @Test + public void onStart_disallowUserSwitch_shouldSetDisabledByAdmin() { + mUserManager.setUserRestriction(UserHandle.of(UserHandle.myUserId()), + UserManager.DISALLOW_USER_SWITCH, true); + + final MultiUserSwitchBarController controller = new MultiUserSwitchBarController(mContext, + mSwitchBarController, null); + + verify(mSwitchBarController).setDisabledByAdmin(any()); + } + + @Test + public void onStart_allowUserSwitch_shouldNotSetDisabledByAdmin() { + mUserManager.setUserRestriction(UserHandle.of(UserHandle.myUserId()), + UserManager.DISALLOW_USER_SWITCH, false); + + final MultiUserSwitchBarController controller = new MultiUserSwitchBarController(mContext, + mSwitchBarController, null); + + verify(mSwitchBarController, never()).setDisabledByAdmin(any()); + } +} From b97bdc38dac4a3b9ceecab3e394f1b3ea9cac7ac Mon Sep 17 00:00:00 2001 From: Ng Zhi An Date: Fri, 1 Feb 2019 12:39:31 -0800 Subject: [PATCH 07/15] Disable system alert window permissions on low ram devices Bug: 63697002 Bug: 117832554 Test: adb am start-activity -a android.settings.action.MANAGE_OVERLAY_PERMISSION Change-Id: I44c64001cd07fd4934cdc55f455384cebd5c9cfb --- .../manage_applications_apps_unsupported.xml | 56 +++++++++++++++++++ res/values/strings.xml | 4 ++ res/xml/special_access.xml | 3 +- src/com/android/settings/Utils.java | 11 ++++ .../appinfo/DrawOverlayDetails.java | 24 ++++++++ .../ManageApplications.java | 13 +++++ ...SystemAlertWindowPreferenceController.java | 36 ++++++++++++ ...emAlertWindowPreferenceControllerTest.java | 56 +++++++++++++++++++ .../testutils/shadow/ShadowUtils.java | 10 ++++ 9 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 res/layout/manage_applications_apps_unsupported.xml create mode 100644 src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceControllerTest.java diff --git a/res/layout/manage_applications_apps_unsupported.xml b/res/layout/manage_applications_apps_unsupported.xml new file mode 100644 index 00000000000..d7c6726e377 --- /dev/null +++ b/res/layout/manage_applications_apps_unsupported.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 90b72e16395..1058c2825ec 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10183,6 +10183,10 @@ This feature is not available on this device + + This feature is not available + + It will slow down this phone Force full GNSS measurements diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml index 4417d0ffb43..05f4a81ef2a 100644 --- a/res/xml/special_access.xml +++ b/res/xml/special_access.xml @@ -42,7 +42,8 @@ android:key="system_alert_window" android:title="@string/system_alert_window_settings" android:fragment="com.android.settings.applications.manageapplications.ManageApplications" - settings:keywords="@string/keywords_system_alert_window"> + settings:keywords="@string/keywords_system_alert_window" + settings:controller="com.android.settings.applications.specialaccess.SystemAlertWindowPreferenceController"> diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 804c7830d20..69b8c569a60 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -55,6 +55,7 @@ import android.net.LinkProperties; import android.net.Network; import android.net.wifi.WifiManager; import android.os.BatteryManager; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.INetworkManagementService; @@ -1007,4 +1008,14 @@ public final class Utils extends com.android.settingslib.Utils { return context.getResources(); } } + + /** + * Returns true if SYSTEM_ALERT_WINDOW permission is available. + * Starting from Q, SYSTEM_ALERT_WINDOW is disabled on low ram phones. + */ + public static boolean isSystemAlertWindowEnabled(Context context) { + // SYSTEM_ALERT_WINDOW is disabled on on low ram devices starting from Q + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + return !(am.isLowRamDevice() && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)); + } } diff --git a/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java b/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java index 9cf57bd1f65..d0b26a53328 100644 --- a/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java +++ b/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java @@ -17,6 +17,7 @@ package com.android.settings.applications.appinfo; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.settings.SettingsEnums; import android.content.Context; @@ -25,6 +26,9 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.provider.Settings; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; @@ -36,6 +40,7 @@ import androidx.preference.Preference.OnPreferenceClickListener; import androidx.preference.SwitchPreference; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.applications.AppInfoWithHeader; import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; import com.android.settings.applications.AppStateOverlayBridge; @@ -70,6 +75,11 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc mOverlayBridge = new AppStateOverlayBridge(context, mState, null); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + if (!Utils.isSystemAlertWindowEnabled(context)) { + mPackageInfo = null; + return; + } + // find preferences addPreferencesFromResource(R.xml.draw_overlay_permissions_details); mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH); @@ -81,6 +91,18 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc .setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); } + // Override here so we don't have an empty screen + @Override + public View onCreateView (LayoutInflater inflater, + ViewGroup container, + Bundle savedInstanceState) { + // if we don't have a package info, show a page saying this is unsupported + if (mPackageInfo == null) { + return inflater.inflate(R.layout.manage_applications_apps_unsupported, null); + } + return super.onCreateView(inflater, container, savedInstanceState); + } + @Override public void onResume() { super.onResume(); @@ -142,6 +164,8 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc @Override protected boolean refreshUi() { + if (mPackageInfo == null) return true; + mOverlayState = mOverlayBridge.getOverlayInfo(mPackageName, mPackageInfo.applicationInfo.uid); diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index a9de206a0da..9586be01be1 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -33,6 +33,7 @@ import static com.android.settings.applications.manageapplications.AppFilterRegi import android.annotation.Nullable; import android.annotation.StringRes; import android.app.Activity; +import android.app.ActivityManager; import android.app.settings.SettingsEnums; import android.app.usage.IUsageStatsManager; import android.content.Context; @@ -49,18 +50,22 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.IconDrawableFactory; import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.Filter; import android.widget.FrameLayout; import android.widget.SearchView; import android.widget.Spinner; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -79,6 +84,7 @@ import com.android.settings.Settings.StorageUseActivity; import com.android.settings.Settings.UsageAccessSettingsActivity; import com.android.settings.Settings.WriteSettingsActivity; import com.android.settings.SettingsActivity; +import com.android.settings.Utils; import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; import com.android.settings.applications.AppStateBaseBridge; @@ -327,12 +333,19 @@ public class ManageApplications extends InstrumentedFragment @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (mListType == LIST_TYPE_OVERLAY && !Utils.isSystemAlertWindowEnabled(getContext())) { + mRootView = inflater.inflate(R.layout.manage_applications_apps_unsupported, null); + setHasOptionsMenu(false); + return mRootView; + } + mRootView = inflater.inflate(R.layout.manage_applications_apps, null); mLoadingContainer = mRootView.findViewById(R.id.loading_container); mListContainer = mRootView.findViewById(R.id.list_container); if (mListContainer != null) { // Create adapter and list view here mEmptyView = mListContainer.findViewById(android.R.id.empty); + mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter, savedInstanceState); if (savedInstanceState != null) { diff --git a/src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceController.java b/src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceController.java new file mode 100644 index 00000000000..5d9e8b612b8 --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceController.java @@ -0,0 +1,36 @@ +/* + * 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.applications.specialaccess; + +import static com.android.settings.Utils.isSystemAlertWindowEnabled; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.Build; + +import com.android.settings.core.BasePreferenceController; + +public class SystemAlertWindowPreferenceController extends BasePreferenceController { + public SystemAlertWindowPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return isSystemAlertWindowEnabled(mContext) + ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE ; + } +} diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceControllerTest.java new file mode 100644 index 00000000000..bc90f2bead0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/SystemAlertWindowPreferenceControllerTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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.applications.specialaccess; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import com.android.settings.testutils.shadow.ShadowUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowUtils.class}) +public class SystemAlertWindowPreferenceControllerTest { + + private Context mContext; + private SystemAlertWindowPreferenceController mController; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mController = new SystemAlertWindowPreferenceController(mContext, "key"); + } + + @Test + public void systemAlertWindow_byDefault_shouldBeShown() { + ShadowUtils.setIsSystemAlertWindowEnabled(true); + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void systemAlertWindow_lowMemory_shouldNotBeShown() { + ShadowUtils.setIsSystemAlertWindowEnabled(false); + assertThat(mController.isAvailable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java index 4797e8c68fb..f8644d9e712 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java @@ -38,6 +38,7 @@ public class ShadowUtils { private static boolean sIsDemoUser; private static ComponentName sDeviceOwnerComponentName; private static Map sAppNameMap; + private static boolean sIsSystemAlertWindowEnabled; @Implementation protected static int enforceSameOwner(Context context, int userId) { @@ -113,4 +114,13 @@ public class ShadowUtils { } sAppNameMap.put(packageName, appLabel); } + + @Implementation + protected static boolean isSystemAlertWindowEnabled(Context context) { + return sIsSystemAlertWindowEnabled; + } + + public static void setIsSystemAlertWindowEnabled(boolean enabled) { + sIsSystemAlertWindowEnabled = enabled; + } } From 3881e2986c36c232583fbf408b5877966846c2c7 Mon Sep 17 00:00:00 2001 From: Raff Tsai Date: Sun, 17 Feb 2019 01:22:39 +0800 Subject: [PATCH 08/15] Do not log Contextual card display when card is dismissed If the db change comes from dismiss card uri. We don't need to log card display again. Fixes: 121196921 Test: Robolectric Change-Id: I4e222187fafa8325e803fa6ee17ebb0b51fb8cb2 --- .../contextualcards/CardContentProvider.java | 8 +++++- .../contextualcards/CardDatabaseHelper.java | 4 +-- .../contextualcards/ContextualCardLoader.java | 20 ++++++++++----- .../slices/SliceContextualCardRenderer.java | 3 ++- .../ContextualCardLoaderTest.java | 25 +++++++++++++++++++ .../SliceContextualCardControllerTest.java | 6 ++--- 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java index e7ede14d663..a9a832d6640 100644 --- a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java +++ b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java @@ -39,12 +39,18 @@ public class CardContentProvider extends ContentProvider { public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider"; - public static final Uri URI = new Uri.Builder() + public static final Uri REFRESH_CARD_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CardContentProvider.CARD_AUTHORITY) .appendPath(CardDatabaseHelper.CARD_TABLE) .build(); + public static final Uri DELETE_CARD_URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(CardContentProvider.CARD_AUTHORITY) + .appendPath(CardDatabaseHelper.CardColumns.CARD_DISMISSED) + .build(); + private static final String TAG = "CardContentProvider"; /** URI matcher for ContentProvider queries. */ private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); diff --git a/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java b/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java index b9bab21386b..39c48c1e847 100644 --- a/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java +++ b/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java @@ -208,7 +208,7 @@ public class CardDatabaseHelper extends SQLiteOpenHelper { * Mark a specific ContextualCard with dismissal flag in the database to indicate that the * card has been dismissed. * - * @param context Context + * @param context Context * @param cardName The card name of the ContextualCard which is dismissed by user. * @return The number of rows updated */ @@ -220,7 +220,7 @@ public class CardDatabaseHelper extends SQLiteOpenHelper { final String[] selectionArgs = {cardName}; final int rowsUpdated = database.update(CARD_TABLE, values, selection, selectionArgs); database.close(); - context.getContentResolver().notifyChange(CardContentProvider.URI, null); + context.getContentResolver().notifyChange(CardContentProvider.DELETE_CARD_URI, null); return rowsUpdated; } } diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java index d6ea6ca668a..ea6ac43c693 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java @@ -59,13 +59,16 @@ public class ContextualCardLoader extends AsyncLoaderCompat private final ContentObserver mObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @Override - public void onChange(boolean selfChange) { + public void onChange(boolean selfChange, Uri uri) { if (isStarted()) { + mNotifyUri = uri; forceLoad(); } } }; + @VisibleForTesting + Uri mNotifyUri; private Context mContext; ContextualCardLoader(Context context) { @@ -77,7 +80,10 @@ public class ContextualCardLoader extends AsyncLoaderCompat @Override protected void onStartLoading() { super.onStartLoading(); - mContext.getContentResolver().registerContentObserver(CardContentProvider.URI, + mNotifyUri = null; + mContext.getContentResolver().registerContentObserver(CardContentProvider.REFRESH_CARD_URI, + false /*notifyForDescendants*/, mObserver); + mContext.getContentResolver().registerContentObserver(CardContentProvider.DELETE_CARD_URI, false /*notifyForDescendants*/, mObserver); } @@ -156,10 +162,12 @@ public class ContextualCardLoader extends AsyncLoaderCompat // Two large cards return visibleCards; } finally { - //TODO(b/121196921): Should not call this if user click dismiss - final ContextualCardFeatureProvider contextualCardFeatureProvider = - FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext); - contextualCardFeatureProvider.logContextualCardDisplay(visibleCards, hiddenCards); + if (!CardContentProvider.DELETE_CARD_URI.equals(mNotifyUri)) { + final ContextualCardFeatureProvider contextualCardFeatureProvider = + FeatureFactory.getFactory(mContext) + .getContextualCardFeatureProvider(mContext); + contextualCardFeatureProvider.logContextualCardDisplay(visibleCards, hiddenCards); + } } } diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java index 2d40efe4f63..48e9f1e7266 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java +++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java @@ -118,7 +118,8 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life sliceLiveData.observe(mLifecycleOwner, slice -> { if (slice == null) { Log.w(TAG, "Slice is null"); - mContext.getContentResolver().notifyChange(CardContentProvider.URI, null); + mContext.getContentResolver().notifyChange(CardContentProvider.REFRESH_CARD_URI, + null); return; } else { //TODO(b/120629936): Take this out once blank card issue is fixed. diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java index 18aa1c4720b..8b04ef3fed6 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java @@ -24,7 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import android.content.Context; import android.net.Uri; @@ -33,6 +35,7 @@ import androidx.slice.Slice; import com.android.settings.R; import com.android.settings.slices.CustomSliceRegistry; +import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; import org.junit.Test; @@ -52,6 +55,7 @@ public class ContextualCardLoaderTest { private Context mContext; private ContextualCardLoader mContextualCardLoader; private EligibleCardChecker mEligibleCardChecker; + private FakeFeatureFactory mFakeFeatureFactory; @Before public void setUp() { @@ -59,6 +63,7 @@ public class ContextualCardLoaderTest { mContextualCardLoader = spy(new ContextualCardLoader(mContext)); mEligibleCardChecker = spy(new EligibleCardChecker(mContext, getContextualCard(TEST_SLICE_URI))); + mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); } @Test @@ -158,6 +163,26 @@ public class ContextualCardLoaderTest { assertThat(mContextualCardLoader.loadInBackground()).isEmpty(); } + @Test + public void getDisplayableCards_refreshCardUri_shouldLogContextualCardDisplay() { + mContextualCardLoader.mNotifyUri = CardContentProvider.REFRESH_CARD_URI; + + mContextualCardLoader.getDisplayableCards(new ArrayList()); + + verify(mFakeFeatureFactory.mContextualCardFeatureProvider).logContextualCardDisplay( + any(List.class), any(List.class)); + } + + @Test + public void getDisplayableCards_deleteCardUri_shouldNotLogContextualCardDisplay() { + mContextualCardLoader.mNotifyUri = CardContentProvider.DELETE_CARD_URI; + + mContextualCardLoader.getDisplayableCards(new ArrayList()); + + verify(mFakeFeatureFactory.mContextualCardFeatureProvider, never()) + .logContextualCardDisplay(any(List.class), any(List.class)); + } + private ContextualCard getContextualCard(String sliceUri) { return new ContextualCard.Builder() .setName("test_card") diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java index 7e1a32c81f4..e97e01e9701 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java @@ -75,7 +75,7 @@ public class SliceContextualCardControllerTest { @Test public void onDismissed_cardShouldBeMarkedAsDismissed() { - final Uri providerUri = CardContentProvider.URI; + final Uri providerUri = CardContentProvider.REFRESH_CARD_URI; mResolver.insert(providerUri, generateOneRow()); doNothing().when(mController).showFeedbackDialog(any(ContextualCard.class)); @@ -96,7 +96,7 @@ public class SliceContextualCardControllerTest { @Test public void onDismissed_noFeedbackEmail_shouldNotShowFeedbackDialog() { - mResolver.insert(CardContentProvider.URI, generateOneRow()); + mResolver.insert(CardContentProvider.REFRESH_CARD_URI, generateOneRow()); final ContextualCardsFragment fragment = FragmentController.of(new ContextualCardsFragment()).create().get(); final ShadowActivity shadowActivity = Shadows.shadowOf(fragment.getActivity()); @@ -109,7 +109,7 @@ public class SliceContextualCardControllerTest { @Test @Config(qualifiers = "mcc999") public void onDismissed_hasFeedbackEmail_shouldShowFeedbackDialog() { - mResolver.insert(CardContentProvider.URI, generateOneRow()); + mResolver.insert(CardContentProvider.REFRESH_CARD_URI, generateOneRow()); final ContextualCardsFragment fragment = FragmentController.of(new ContextualCardsFragment()).create().get(); final ShadowActivity shadowActivity = Shadows.shadowOf(fragment.getActivity()); From 44e5aeca654e1fe41a227141b50aca08a09d6817 Mon Sep 17 00:00:00 2001 From: tmfang Date: Tue, 26 Feb 2019 13:22:59 +0800 Subject: [PATCH 09/15] Bar chart preserves data before rotating device We saw chart view is gone while rotating device. Fow now, we save the old permission data before fragment was recreated. Then, we can initialize the view quickly with old data. Test: robotest, visual Fixes: 123539793 Change-Id: I230fecc6001a17a0c0205f2a1dcb5dd79f32e744 --- ...ermissionBarChartPreferenceController.java | 30 +++++++++++++++++-- ...ssionBarChartPreferenceControllerTest.java | 29 +++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java index 2c6368e9468..453cdbfaf86 100644 --- a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java +++ b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.permission.PermissionControllerManager; import android.permission.RuntimePermissionUsageInfo; import android.provider.DeviceConfig; @@ -31,12 +32,15 @@ import android.util.Log; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settingslib.Utils; import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnCreate; +import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.widget.BarChartInfo; import com.android.settingslib.widget.BarChartPreference; @@ -48,14 +52,17 @@ import java.util.List; public class PermissionBarChartPreferenceController extends BasePreferenceController implements - PermissionControllerManager.OnPermissionUsageResultCallback, LifecycleObserver, OnStart { + PermissionControllerManager.OnPermissionUsageResultCallback, LifecycleObserver, OnCreate, + OnStart, OnSaveInstanceState { private static final String TAG = "BarChartPreferenceCtl"; + private static final String KEY_PERMISSION_USAGE = "usage_infos"; + @VisibleForTesting + List mOldUsageInfos; private PackageManager mPackageManager; private PrivacyDashboardFragment mParent; private BarChartPreference mBarChartPreference; - private List mOldUsageInfos; public PermissionBarChartPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); @@ -67,6 +74,18 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro mParent = fragment; } + @Override + public void onCreate(Bundle savedInstanceState) { + if (savedInstanceState != null) { + mOldUsageInfos = savedInstanceState.getParcelableArrayList(KEY_PERMISSION_USAGE); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putParcelableList(KEY_PERMISSION_USAGE, mOldUsageInfos); + } + @Override public int getAvailabilityStatus() { return Boolean.parseBoolean( @@ -92,6 +111,9 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro .build(); mBarChartPreference.initializeBarChart(info); + if (!mOldUsageInfos.isEmpty()) { + mBarChartPreference.setBarViewInfos(createBarViews(mOldUsageInfos)); + } } @Override @@ -100,7 +122,9 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro return; } - mBarChartPreference.updateLoadingState(true /* isLoading */); + // We don't hide chart when we have existing data. + mBarChartPreference.updateLoadingState(mOldUsageInfos.isEmpty() /* isLoading */); + // But we still need to hint user with progress bar that we are updating new usage data. mParent.setLoadingEnabled(true /* enabled */); retrievePermissionUsageData(); } diff --git a/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java index f664f4f9140..332156e8b9a 100644 --- a/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java @@ -124,6 +124,18 @@ public class PermissionBarChartPreferenceControllerTest { verify(mPreference).initializeBarChart(any(BarChartInfo.class)); } + @Test + public void displayPreference_usageInfosSet_shouldSetBarViewInfos() { + final RuntimePermissionUsageInfo info1 = + new RuntimePermissionUsageInfo("permission 1", 10); + mController.mOldUsageInfos.add(info1); + + mController.displayPreference(mScreen); + + verify(mPreference).setBarViewInfos(any(BarViewInfo[].class)); + verify(mPreference).initializeBarChart(any(BarChartInfo.class)); + } + @Test public void onPermissionUsageResult_differentPermissionResultSet_shouldSetBarViewInfos() { final List infos1 = new ArrayList<>(); @@ -159,7 +171,7 @@ public class PermissionBarChartPreferenceControllerTest { } @Test - public void onStart_permissionHubEnabled_shouldShowProgressBar() { + public void onStart_usageInfosNotSetAndPermissionHubEnabled_shouldShowProgressBar() { DeviceConfig.setProperty(DeviceConfig.Privacy.NAMESPACE, DeviceConfig.Privacy.PROPERTY_PERMISSIONS_HUB_ENABLED, "true", true); mController.displayPreference(mScreen); @@ -170,6 +182,21 @@ public class PermissionBarChartPreferenceControllerTest { verify(mPreference).updateLoadingState(true /* isLoading */); } + @Test + public void onStart_usageInfosSetAndPermissionHubEnabled_shouldNotUpdatePrefLoadingState() { + DeviceConfig.setProperty(DeviceConfig.Privacy.NAMESPACE, + DeviceConfig.Privacy.PROPERTY_PERMISSIONS_HUB_ENABLED, "true", true); + final RuntimePermissionUsageInfo info1 = + new RuntimePermissionUsageInfo("permission 1", 10); + mController.mOldUsageInfos.add(info1); + mController.displayPreference(mScreen); + + mController.onStart(); + + verify(mFragment).setLoadingEnabled(true /* enabled */); + verify(mPreference).updateLoadingState(false /* isLoading */); + } + @Test public void onStart_permissionHubDisabled_shouldNotShowProgressBar() { DeviceConfig.setProperty(DeviceConfig.Privacy.NAMESPACE, From 730a6c0f5e042edd705e2a431b360b3988a566f4 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Wed, 27 Feb 2019 11:33:43 +0800 Subject: [PATCH 10/15] Fix Settings crash after clicking 'Share Wi-Fi' button When connecting to an open network, Settings crash after scanning Wi-Fi DPP QR code and clicking 'Share Wi-Fi' button. When choosing default network for configurator usage, should make sure if it supports Wi-Fi DPP or not. Bug: 126456582 Test: manual test atest WifiQrCodeTest WifiDppConfiguratorActivityTest WifiDppEnrolleeActivityTest WifiDppQrCodeGeneratorFragmentTest WifiDppQrCodeScannerFragmentTest WifiNetworkListFragmentTest WifiDppChooseSavedWifiNetworkFragmentTest Change-Id: I66ee9b01314b84213ecb3016e6e72ff71af8dfc7 --- .../android/settings/wifi/dpp/WifiDppConfiguratorActivity.java | 2 +- .../settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java | 2 +- src/com/android/settings/wifi/dpp/WifiNetworkConfig.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java index 1770a02c8f6..be5a12ffbf8 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java +++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java @@ -156,7 +156,7 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements cancelActivity = true; } else { final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull(); - if (connectedConfig == null) { + if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) { showChooseSavedWifiNetworkFragment(/* addToBackStack */ false); } else { mWifiNetworkConfig = connectedConfig; diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java index 2264ba6a1b9..daa41d9f8f6 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java +++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java @@ -86,7 +86,7 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity(); MenuItem menuItem; - if (wifiNetworkConfig.isSupportConfiguratorQrCodeScanner(getActivity())) { + if (wifiNetworkConfig.isSupportWifiDpp(getActivity())) { menuItem = menu.add(0, Menu.FIRST, 0, R.string.next_label); menuItem.setIcon(R.drawable.ic_scan_24dp); menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); diff --git a/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java b/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java index 979e602bf4a..1d82bb920b0 100644 --- a/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java +++ b/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java @@ -212,7 +212,7 @@ public class WifiNetworkConfig { wifiManager.connect(wifiConfiguration, listener); } - public boolean isSupportConfiguratorQrCodeScanner(Context context) { + public boolean isSupportWifiDpp(Context context) { if (!WifiDppUtils.isWifiDppEnabled(context)) { return false; } From 6bbe6e2362794201367b49fa9614c95d43a1ed27 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Tue, 26 Feb 2019 18:01:00 +0800 Subject: [PATCH 11/15] Leverage the behavior of showing the searching message in wifi slice Fixes: 124823973 Test: robotest Change-Id: Id89ec5ef09f05f83e019a6f4488cacf2ab4e635b --- .../settings/wifi/slice/WifiSlice.java | 15 ++- .../settings/testutils/SliceTester.java | 12 ++ .../settings/wifi/slice/WifiSliceTest.java | 127 +++++++++++++++++- 3 files changed, 149 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java index 679ab8b41ee..8d20f7f847b 100644 --- a/src/com/android/settings/wifi/slice/WifiSlice.java +++ b/src/com/android/settings/wifi/slice/WifiSlice.java @@ -118,10 +118,19 @@ public class WifiSlice implements CustomSliceable { final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance(getUri()); final List results = worker != null ? worker.getResults() : null; + final int apCount = results == null ? 0 : results.size(); - // Need a loading text when results are not ready. - boolean needLoadingRow = results == null; - final int apCount = needLoadingRow ? 0 : results.size(); + // Need a loading text when results are not ready or out of date. + boolean needLoadingRow = true; + int index = apCount > 0 && results.get(0).isActive() ? 1 : 0; + // This loop checks the existence of reachable APs to determine the validity of the current + // AP list. + for (; index < apCount; index++) { + if (results.get(index).isReachable()) { + needLoadingRow = false; + break; + } + } // Add AP rows final CharSequence placeholder = mContext.getText(R.string.summary_placeholder); diff --git a/tests/robotests/src/com/android/settings/testutils/SliceTester.java b/tests/robotests/src/com/android/settings/testutils/SliceTester.java index d84d42c3e7d..fdd447545a9 100644 --- a/tests/robotests/src/com/android/settings/testutils/SliceTester.java +++ b/tests/robotests/src/com/android/settings/testutils/SliceTester.java @@ -253,6 +253,18 @@ public class SliceTester { assertThat(hasText(sliceItems, subtitle, null /* hints */)).isTrue(); } + /** + * Assert no slice item contains subtitle. + * + * @param sliceItems All slice items of a Slice. + * @param subtitle Subtitle for asserting. + */ + public static void assertNoSliceItemContainsSubtitle(List sliceItems, + String subtitle) { + // Subtitle has no hints + assertThat(hasText(sliceItems, subtitle, null /* hints */)).isFalse(); + } + private static boolean hasText(List sliceItems, String text, String hints) { boolean hasText = false; for (SliceItem item : sliceItems) { diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java index 6af8f803e1e..01feb8ecaed 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java @@ -21,6 +21,7 @@ import static android.app.slice.SliceItem.FORMAT_SLICE; import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI; import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT; +import static com.android.settings.wifi.slice.WifiSlice.WifiScanWorker; import static com.google.common.truth.Truth.assertThat; @@ -32,6 +33,8 @@ import static org.mockito.Mockito.verify; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.net.NetworkInfo; +import android.net.Uri; import android.net.wifi.WifiManager; import androidx.core.graphics.drawable.IconCompat; @@ -44,24 +47,33 @@ import androidx.slice.core.SliceQuery; import androidx.slice.widget.SliceLiveData; import com.android.settings.R; +import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.testutils.SliceTester; +import com.android.settingslib.wifi.AccessPoint; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) public class WifiSliceTest { + private static final String AP1_NAME = "ap1"; + private static final String AP2_NAME = "ap2"; + private Context mContext; private ContentResolver mResolver; private WifiManager mWifiManager; private WifiSlice mWifiSlice; - private WifiSlice.WifiScanWorker mWifiScanWorker; + private WifiScanWorker mWifiScanWorker; @Before public void setUp() { @@ -75,7 +87,7 @@ public class WifiSliceTest { mWifiManager.setWifiEnabled(true); mWifiSlice = new WifiSlice(mContext); - mWifiScanWorker = new WifiSlice.WifiScanWorker(mContext, WIFI_SLICE_URI); + mWifiScanWorker = new WifiScanWorker(mContext, WIFI_SLICE_URI); } @Test @@ -122,6 +134,107 @@ public class WifiSliceTest { mContext.getString(R.string.wifi_empty_list_wifi_on)); } + private AccessPoint createAccessPoint(String name, boolean active, boolean reachable) { + final AccessPoint accessPoint = mock(AccessPoint.class); + doReturn(name).when(accessPoint).getConfigName(); + doReturn(active).when(accessPoint).isActive(); + doReturn(reachable).when(accessPoint).isReachable(); + if (active) { + final NetworkInfo networkInfo = mock(NetworkInfo.class); + doReturn(networkInfo).when(accessPoint).getNetworkInfo(); + doReturn(NetworkInfo.State.CONNECTED).when(networkInfo).getState(); + } + return accessPoint; + } + + private void setWorkerResults(AccessPoint... accessPoints) { + final ArrayList results = new ArrayList<>(); + for (AccessPoint ap : accessPoints) { + results.add(ap); + } + final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance(mWifiSlice.getUri()); + doReturn(results).when(worker).getResults(); + } + + @Test + @Config(shadows = ShadowSliceBackgroundWorker.class) + public void getWifiSlice_noReachableAp_shouldReturnLoadingRow() { + setWorkerResults( + createAccessPoint(AP1_NAME, false, false), + createAccessPoint(AP2_NAME, false, false)); + final Slice wifiSlice = mWifiSlice.getSlice(); + + final List sliceItems = wifiSlice.getItems(); + + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP2_NAME); + // Has scanning text + SliceTester.assertAnySliceItemContainsSubtitle(sliceItems, + mContext.getString(R.string.wifi_empty_list_wifi_on)); + } + + @Test + @Config(shadows = ShadowSliceBackgroundWorker.class) + public void getWifiSlice_oneActiveAp_shouldReturnLoadingRow() { + setWorkerResults(createAccessPoint(AP1_NAME, true, true)); + final Slice wifiSlice = mWifiSlice.getSlice(); + + final List sliceItems = wifiSlice.getItems(); + + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); + // Has scanning text + SliceTester.assertAnySliceItemContainsSubtitle(sliceItems, + mContext.getString(R.string.wifi_empty_list_wifi_on)); + } + + @Test + @Config(shadows = ShadowSliceBackgroundWorker.class) + public void getWifiSlice_oneActiveApAndOneUnreachableAp_shouldReturnLoadingRow() { + setWorkerResults( + createAccessPoint(AP1_NAME, true, true), + createAccessPoint(AP2_NAME, false, false)); + final Slice wifiSlice = mWifiSlice.getSlice(); + + final List sliceItems = wifiSlice.getItems(); + + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP2_NAME); + // Has scanning text + SliceTester.assertAnySliceItemContainsSubtitle(sliceItems, + mContext.getString(R.string.wifi_empty_list_wifi_on)); + } + + @Test + @Config(shadows = ShadowSliceBackgroundWorker.class) + public void getWifiSlice_oneReachableAp_shouldNotReturnLoadingRow() { + setWorkerResults(createAccessPoint(AP1_NAME, false, true)); + final Slice wifiSlice = mWifiSlice.getSlice(); + + final List sliceItems = wifiSlice.getItems(); + + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); + // No scanning text + SliceTester.assertNoSliceItemContainsSubtitle(sliceItems, + mContext.getString(R.string.wifi_empty_list_wifi_on)); + } + + @Test + @Config(shadows = ShadowSliceBackgroundWorker.class) + public void getWifiSlice_allReachableAps_shouldNotReturnLoadingRow() { + setWorkerResults( + createAccessPoint(AP1_NAME, false, true), + createAccessPoint(AP2_NAME, false, true)); + final Slice wifiSlice = mWifiSlice.getSlice(); + + final List sliceItems = wifiSlice.getItems(); + + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); + SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP2_NAME); + // No scanning text + SliceTester.assertNoSliceItemContainsSubtitle(sliceItems, + mContext.getString(R.string.wifi_empty_list_wifi_on)); + } + @Test public void handleUriChange_updatesWifi() { final Intent intent = mWifiSlice.getIntent(); @@ -146,4 +259,14 @@ public class WifiSliceTest { verify(mResolver).notifyChange(WIFI_SLICE_URI, null); } + + @Implements(SliceBackgroundWorker.class) + public static class ShadowSliceBackgroundWorker { + private static WifiScanWorker mWifiScanWorker = mock(WifiScanWorker.class); + + @Implementation + public static SliceBackgroundWorker getInstance(Uri uri) { + return mWifiScanWorker; + } + } } From e1fd5af2e5ac87d96dd32801afecafb43e851874 Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Fri, 22 Feb 2019 20:40:52 +0800 Subject: [PATCH 12/15] Update wallpaper icon for SUW Anything else screen A dot icon is displaying for "Change wallpaper" option on SUW Anything else screen, the cause is that we updated new colorful icons for homepage suggestions. So we changed the drawable back and deleted the unused icon. Bug: 125426694 Test: visual Change-Id: I8cf6d6dda311a45362a47ea31dc352ba4d452f80 --- AndroidManifest.xml | 2 +- res/drawable/ic_suggestion_wallpaper.xml | 25 ------------------------ 2 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 res/drawable/ic_suggestion_wallpaper.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a5586f7955e..e42715f7d1e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -788,7 +788,7 @@ diff --git a/res/drawable/ic_suggestion_wallpaper.xml b/res/drawable/ic_suggestion_wallpaper.xml deleted file mode 100644 index 32defcecdc1..00000000000 --- a/res/drawable/ic_suggestion_wallpaper.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - From fd85a9fae63953ebf04e2ac3e03ca0d044eba02e Mon Sep 17 00:00:00 2001 From: Issei Suzuki Date: Tue, 26 Feb 2019 16:16:47 +0100 Subject: [PATCH 13/15] Move AmbientDisplayConfiguration class out of internal package. Test: m droid (run full build) Bug: 126327497 Change-Id: Idc07c811864edb4f88151051e25d6e05e4caf3b2 --- .../display/AmbientDisplayAlwaysOnPreferenceController.java | 2 +- .../AmbientDisplayNotificationsPreferenceController.java | 2 +- .../settings/gestures/DoubleTapScreenPreferenceController.java | 3 +-- src/com/android/settings/gestures/DoubleTapScreenSettings.java | 2 +- src/com/android/settings/gestures/GestureSettings.java | 2 +- .../settings/gestures/GesturesSettingPreferenceController.java | 2 +- .../settings/gestures/PickupGesturePreferenceController.java | 3 +-- src/com/android/settings/gestures/PickupGestureSettings.java | 2 +- .../gestures/TapScreenGesturePreferenceController.java | 3 +-- .../android/settings/gestures/TapScreenGestureSettings.java | 2 +- .../gestures/WakeScreenGesturePreferenceController.java | 2 +- .../android/settings/security/LockscreenDashboardFragment.java | 2 +- .../AmbientDisplayAlwaysOnPreferenceControllerTest.java | 2 +- .../AmbientDisplayNotificationsPreferenceControllerTest.java | 2 +- .../gestures/DoubleTapScreenPreferenceControllerTest.java | 2 +- .../gestures/PickupGesturePreferenceControllerTest.java | 2 +- .../gestures/TapScreenGesturePreferenceControllerTest.java | 3 +-- .../gestures/WakeScreenGesturePreferenceControllerTest.java | 2 +- 18 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java index a3cff3da3d4..6a9e9fc2f7c 100644 --- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java +++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java @@ -16,11 +16,11 @@ package com.android.settings.display; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.core.TogglePreferenceController; public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreferenceController { diff --git a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java index 5c19ca7f732..daaf7b15f14 100644 --- a/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java +++ b/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceController.java @@ -17,6 +17,7 @@ import static android.provider.Settings.Secure.DOZE_ENABLED; import android.app.settings.SettingsEnums; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; @@ -24,7 +25,6 @@ import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.core.TogglePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; diff --git a/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java b/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java index 9f0a613424b..4abf09af120 100644 --- a/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java +++ b/src/com/android/settings/gestures/DoubleTapScreenPreferenceController.java @@ -21,14 +21,13 @@ import static android.provider.Settings.Secure.DOZE_DOUBLE_TAP_GESTURE; import android.annotation.UserIdInt; import android.content.Context; import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; -import com.android.internal.hardware.AmbientDisplayConfiguration; - public class DoubleTapScreenPreferenceController extends GesturePreferenceController { private final int ON = 1; diff --git a/src/com/android/settings/gestures/DoubleTapScreenSettings.java b/src/com/android/settings/gestures/DoubleTapScreenSettings.java index 81225042b4f..300ce487a3e 100644 --- a/src/com/android/settings/gestures/DoubleTapScreenSettings.java +++ b/src/com/android/settings/gestures/DoubleTapScreenSettings.java @@ -19,9 +19,9 @@ package com.android.settings.gestures; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; import android.provider.SearchIndexableResource; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; diff --git a/src/com/android/settings/gestures/GestureSettings.java b/src/com/android/settings/gestures/GestureSettings.java index c41d3c63e2a..db402cc04d9 100644 --- a/src/com/android/settings/gestures/GestureSettings.java +++ b/src/com/android/settings/gestures/GestureSettings.java @@ -18,9 +18,9 @@ package com.android.settings.gestures; import android.app.settings.SettingsEnums; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.provider.SearchIndexableResource; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; diff --git a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java index 9366a159817..98eddffb8fa 100644 --- a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java +++ b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java @@ -18,11 +18,11 @@ package com.android.settings.gestures; import android.content.ContentResolver; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.provider.Settings; import androidx.annotation.NonNull; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; diff --git a/src/com/android/settings/gestures/PickupGesturePreferenceController.java b/src/com/android/settings/gestures/PickupGesturePreferenceController.java index 7619b69621a..0738a51f0ff 100644 --- a/src/com/android/settings/gestures/PickupGesturePreferenceController.java +++ b/src/com/android/settings/gestures/PickupGesturePreferenceController.java @@ -21,12 +21,11 @@ import static android.provider.Settings.Secure.DOZE_PICK_UP_GESTURE; import android.annotation.UserIdInt; import android.content.Context; import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; -import com.android.internal.hardware.AmbientDisplayConfiguration; - public class PickupGesturePreferenceController extends GesturePreferenceController { private static final int ON = 1; diff --git a/src/com/android/settings/gestures/PickupGestureSettings.java b/src/com/android/settings/gestures/PickupGestureSettings.java index 6d479027bf0..f1cc3f05ed9 100644 --- a/src/com/android/settings/gestures/PickupGestureSettings.java +++ b/src/com/android/settings/gestures/PickupGestureSettings.java @@ -19,9 +19,9 @@ package com.android.settings.gestures; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; import android.provider.SearchIndexableResource; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; diff --git a/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java b/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java index bbffc7cdab7..ba2b86973fe 100644 --- a/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java +++ b/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java @@ -20,12 +20,11 @@ import static android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE; import android.annotation.UserIdInt; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; -import com.android.internal.hardware.AmbientDisplayConfiguration; - public class TapScreenGesturePreferenceController extends GesturePreferenceController { private static final String PREF_KEY_VIDEO = "gesture_tap_screen_video"; diff --git a/src/com/android/settings/gestures/TapScreenGestureSettings.java b/src/com/android/settings/gestures/TapScreenGestureSettings.java index d80c03d8f78..a86e6820afd 100644 --- a/src/com/android/settings/gestures/TapScreenGestureSettings.java +++ b/src/com/android/settings/gestures/TapScreenGestureSettings.java @@ -18,9 +18,9 @@ package com.android.settings.gestures; import android.content.Context; import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; 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; diff --git a/src/com/android/settings/gestures/WakeScreenGesturePreferenceController.java b/src/com/android/settings/gestures/WakeScreenGesturePreferenceController.java index 96a3580e699..e9d03d716ac 100644 --- a/src/com/android/settings/gestures/WakeScreenGesturePreferenceController.java +++ b/src/com/android/settings/gestures/WakeScreenGesturePreferenceController.java @@ -20,12 +20,12 @@ import static android.provider.Settings.Secure.DOZE_WAKE_SCREEN_GESTURE; import android.annotation.UserIdInt; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.aware.AwareFeatureProvider; import com.android.settings.overlay.FeatureFactory; diff --git a/src/com/android/settings/security/LockscreenDashboardFragment.java b/src/com/android/settings/security/LockscreenDashboardFragment.java index 1935f05ef81..3472d4802ab 100644 --- a/src/com/android/settings/security/LockscreenDashboardFragment.java +++ b/src/com/android/settings/security/LockscreenDashboardFragment.java @@ -18,11 +18,11 @@ package com.android.settings.security; import android.app.settings.SettingsEnums; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.provider.SearchIndexableResource; import androidx.annotation.VisibleForTesting; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.display.AmbientDisplayAlwaysOnPreferenceController; diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java index f8e0a74b8c2..7cd5a232713 100644 --- a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java @@ -23,9 +23,9 @@ import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.provider.Settings; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.testutils.shadow.ShadowSecureSettings; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java index e36b3947625..fbfb8a493fb 100644 --- a/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayNotificationsPreferenceControllerTest.java @@ -29,12 +29,12 @@ import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import androidx.preference.SwitchPreference; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.testutils.shadow.ShadowSecureSettings; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; diff --git a/tests/robotests/src/com/android/settings/gestures/DoubleTapScreenPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/DoubleTapScreenPreferenceControllerTest.java index 52cb75d8fe3..0dc7a78999a 100644 --- a/tests/robotests/src/com/android/settings/gestures/DoubleTapScreenPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/gestures/DoubleTapScreenPreferenceControllerTest.java @@ -26,8 +26,8 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/gestures/PickupGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PickupGesturePreferenceControllerTest.java index 5422eabc053..b2ba216830f 100644 --- a/tests/robotests/src/com/android/settings/gestures/PickupGesturePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/gestures/PickupGesturePreferenceControllerTest.java @@ -26,8 +26,8 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/gestures/TapScreenGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/TapScreenGesturePreferenceControllerTest.java index c09e4ebe011..12715fd8aa1 100644 --- a/tests/robotests/src/com/android/settings/gestures/TapScreenGesturePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/gestures/TapScreenGesturePreferenceControllerTest.java @@ -25,8 +25,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.when; import android.content.Context; - -import com.android.internal.hardware.AmbientDisplayConfiguration; +import android.hardware.display.AmbientDisplayConfiguration; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/gestures/WakeScreenGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/WakeScreenGesturePreferenceControllerTest.java index ecda5fd4386..471914af614 100644 --- a/tests/robotests/src/com/android/settings/gestures/WakeScreenGesturePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/gestures/WakeScreenGesturePreferenceControllerTest.java @@ -28,8 +28,8 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.aware.AwareFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; From d0e076192eaa9e86453814e5a7b5f42141392ced Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Wed, 27 Feb 2019 14:58:45 +0000 Subject: [PATCH 14/15] Update the timestamp used for time zone filtering Update the timestamp used for time zone filtering and improve the comment explaining what it is for. Bug: 124756276 Test: build only Change-Id: I2e53b1f3bd36602f8d08967e1335066d93ae05ea --- .../timezone/model/FilteredCountryTimeZones.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java b/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java index 6af0911b34f..d7fcb2f95c6 100644 --- a/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java +++ b/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java @@ -27,9 +27,19 @@ import java.util.stream.Collectors; */ public class FilteredCountryTimeZones { - // New timezone list and the meta data of time zone, notUsedAfter, is introduced in Android P - // in 2018. Only show time zone used in or after 2018. - private static final long MIN_USE_DATE_OF_TIMEZONE = 1514764800000L; // 1/1/2018 00:00 UTC + /** + * The timestamp used to determine which time zones to show to users by using the notUsedAfter + * metadata Android holds for each time zone. + * + * notUsedAfter exists because some time zones effectively "merge" with other time zones after + * a given point in time (i.e. they have identical transitions, offsets, etc.). After that + * point we only need to show one of the functionally identical ones. + * + * Rather than using System.currentTimeMillis(), UX folks asked for consistent behavior and so + * a timestamp known to be in the recent past is used. This should be updated occasionally but + * it doesn't have to be very often. + */ + private static final long MIN_USE_DATE_OF_TIMEZONE = 1546300800000L; // 1/1/2019 00:00 UTC private final CountryTimeZones mCountryTimeZones; private final List mTimeZoneIds; From 625ffbd0e9a3a4f89273e9f1c9dcd9046f75e7ef Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 27 Feb 2019 14:30:27 -0800 Subject: [PATCH 15/15] Fix homepage display test. We updated the IA but it's still checking old keywords. Fixes: n/a Test: atest Change-Id: I495201d0a2580e7d600982139907d216aa1f558d --- .../src/com/android/settings/ui/HomepageDisplayTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/uitests/src/com/android/settings/ui/HomepageDisplayTests.java b/tests/uitests/src/com/android/settings/ui/HomepageDisplayTests.java index 4c72b343d55..807ac6fa060 100644 --- a/tests/uitests/src/com/android/settings/ui/HomepageDisplayTests.java +++ b/tests/uitests/src/com/android/settings/ui/HomepageDisplayTests.java @@ -52,7 +52,9 @@ public class HomepageDisplayTests { "Display", "Sound", "Storage", - "Security & location", + "Security", + "Location", + "Privacy", "Accounts", "Accessibility", "System"