From 95abf59af43bafe4922b8cc096b10b65440419b8 Mon Sep 17 00:00:00 2001 From: Ivan Chiang Date: Thu, 24 May 2018 17:52:49 +0800 Subject: [PATCH 01/25] Fix gray out previously connected device preference issue Count the dock devices in previously connected device controller. Fix: 80227045 Test: m -j SettingsRoboTests RunSettingsRoboTests Change-Id: I08d82ec715698ecf894a4a7cd0631c5f1de0f591 Merged-In: I08d82ec715698ecf894a4a7cd0631c5f1de0f591 --- ...ouslyConnectedDevicePreferenceController.java | 16 +++++++++++++--- ...yConnectedDevicePreferenceControllerTest.java | 6 ++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java index ba3ee1f1054..73c95ab4e38 100644 --- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java @@ -21,26 +21,29 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; -import android.util.Log; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; +import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; - +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; -import com.android.settingslib.utils.ThreadUtils; public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback { private Preference mPreference; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private DockUpdater mSavedDockUpdater; private int mPreferenceSize; public PreviouslyConnectedDevicePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); + + mSavedDockUpdater = FeatureFactory.getFactory( + context).getDockUpdaterFeatureProvider().getSavedDockUpdater(context, this); } @Override @@ -62,12 +65,14 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc @Override public void onStart() { mBluetoothDeviceUpdater.registerCallback(); + mSavedDockUpdater.registerCallback(); updatePreferenceOnSizeChanged(); } @Override public void onStop() { mBluetoothDeviceUpdater.unregisterCallback(); + mSavedDockUpdater.unregisterCallback(); } public void init(DashboardFragment fragment) { @@ -92,6 +97,11 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mBluetoothDeviceUpdater = bluetoothDeviceUpdater; } + @VisibleForTesting + void setSavedDockUpdater(DockUpdater savedDockUpdater) { + mSavedDockUpdater = savedDockUpdater; + } + @VisibleForTesting void setPreferenceSize(int size) { mPreferenceSize = size; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java index 444728a1c4c..d04b206c6dd 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java @@ -21,6 +21,7 @@ import android.content.pm.PackageManager; import android.support.v7.preference.Preference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -46,6 +47,8 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Mock private BluetoothDeviceUpdater mBluetoothDeviceUpdater; @Mock + private DockUpdater mDockUpdater; + @Mock private PackageManager mPackageManager; private Context mContext; @@ -61,6 +64,7 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { mPreConnectedDeviceController = new PreviouslyConnectedDevicePreferenceController(mContext, KEY); mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); + mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater); mPreference = new Preference(mContext); mPreConnectedDeviceController.setPreference(mPreference); @@ -71,10 +75,12 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { // register the callback in onStart() mPreConnectedDeviceController.onStart(); verify(mBluetoothDeviceUpdater).registerCallback(); + verify(mDockUpdater).registerCallback(); // unregister the callback in onStop() mPreConnectedDeviceController.onStop(); verify(mBluetoothDeviceUpdater).unregisterCallback(); + verify(mDockUpdater).unregisterCallback(); } @Test From 6244b5d0aa591fd5e69fda577b48a97fd95594ac Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Mon, 11 Jun 2018 14:12:13 -0700 Subject: [PATCH 02/25] Add padding between battery text and icon Change-Id: I4625ae6168278f7e69e9e26ed9c12958645e920e Fixes: 80281413 Test: Screenshot --- res/layout/battery_header.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/res/layout/battery_header.xml b/res/layout/battery_header.xml index df45d15fd6d..9f5bde99019 100644 --- a/res/layout/battery_header.xml +++ b/res/layout/battery_header.xml @@ -31,6 +31,7 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginStart="56dp" + android:layout_marginEnd="8dp" android:orientation="vertical"> Date: Mon, 11 Jun 2018 15:20:39 +0800 Subject: [PATCH 03/25] Fix back stack didn't perserved when Files Activity launched by Setting storage then back case. Due to FilesActivity set "android:documentLaunchMode="intoExisting", it will create new task when launched from Storage Setting page. When select FilesActivity task from recents & press back key, it will not back to settings page. Using startActivityForResult to set task as source task can fix task separated case. Change-Id: I26362928261a1a9c0e485a486be493423d1fcf61 Fixes: 33117269 Test: Manual Test: atest StorageItemPreferenceControllerTest --- .../StorageItemPreferenceController.java | 8 ++++-- .../StorageItemPreferenceControllerTest.java | 28 ++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java index 9604f74fe3e..0272b5c6dfa 100644 --- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java +++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java @@ -402,10 +402,14 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle try { final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1); + // b/33117269: Note that launchIntent may launch activity in different task which set + // different launchMode (e.g. Files), using startActivityForesult to set task as + // source task, and set requestCode as 0 means don't care about returnCode currently. if (userId == -1) { - mFragment.startActivity(intent); + mFragment.startActivityForResult(intent, 0 /* requestCode not used */); } else { - mFragment.getActivity().startActivityAsUser(intent, new UserHandle(userId)); + mFragment.getActivity().startActivityForResultAsUser(intent, + 0 /* requestCode not used */, new UserHandle(userId)); } } catch (ActivityNotFoundException e) { Log.w(TAG, "No activity found for " + intent); diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java index a4107f5b186..c8ed42e342c 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java @@ -19,6 +19,7 @@ import static com.android.settings.applications.manageapplications.ManageApplica import static com.android.settings.applications.manageapplications.ManageApplications.EXTRA_WORK_ONLY; import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -123,7 +124,8 @@ public class StorageItemPreferenceControllerTest { mController.handlePreferenceTreeClick(mPreference); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mActivity).startActivityAsUser(argumentCaptor.capture(), nullable(UserHandle.class)); + verify(mActivity).startActivityForResultAsUser(argumentCaptor.capture(), anyInt(), + nullable(UserHandle.class)); final Intent intent = argumentCaptor.getValue(); assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN); @@ -140,8 +142,8 @@ public class StorageItemPreferenceControllerTest { mController.handlePreferenceTreeClick(mPreference); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mFragment.getActivity()) - .startActivityAsUser(argumentCaptor.capture(), nullable(UserHandle.class)); + verify(mFragment.getActivity()).startActivityForResultAsUser(argumentCaptor.capture(), + anyInt(), nullable(UserHandle.class)); final Intent intent = argumentCaptor.getValue(); assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN); @@ -167,8 +169,8 @@ public class StorageItemPreferenceControllerTest { mController.handlePreferenceTreeClick(mPreference); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mFragment.getActivity()).startActivityAsUser(argumentCaptor.capture(), - nullable(UserHandle.class)); + verify(mFragment.getActivity()).startActivityForResultAsUser(argumentCaptor.capture(), + anyInt(), nullable(UserHandle.class)); final Intent intent = argumentCaptor.getValue(); assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN); @@ -186,8 +188,8 @@ public class StorageItemPreferenceControllerTest { mController.handlePreferenceTreeClick(mPreference); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mFragment.getActivity()) - .startActivityAsUser(argumentCaptor.capture(), nullable(UserHandle.class)); + verify(mFragment.getActivity()).startActivityForResultAsUser(argumentCaptor.capture(), + anyInt(), nullable(UserHandle.class)); Intent intent = argumentCaptor.getValue(); assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN); @@ -223,8 +225,8 @@ public class StorageItemPreferenceControllerTest { .isTrue(); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mFragment.getActivity()).startActivityAsUser(argumentCaptor.capture(), - nullable(UserHandle.class)); + verify(mFragment.getActivity()).startActivityForResultAsUser(argumentCaptor.capture(), + anyInt(), nullable(UserHandle.class)); Intent intent = argumentCaptor.getValue(); Intent browseIntent = mVolume.buildBrowseIntent(); @@ -240,8 +242,8 @@ public class StorageItemPreferenceControllerTest { mController.handlePreferenceTreeClick(mPreference); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mFragment.getActivity()).startActivityAsUser(argumentCaptor.capture(), - nullable(UserHandle.class)); + verify(mFragment.getActivity()).startActivityForResultAsUser(argumentCaptor.capture(), + anyInt(), nullable(UserHandle.class)); Intent intent = argumentCaptor.getValue(); assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN); @@ -258,8 +260,8 @@ public class StorageItemPreferenceControllerTest { mController.handlePreferenceTreeClick(mPreference); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mFragment.getActivity()).startActivityAsUser(argumentCaptor.capture(), - nullable(UserHandle.class)); + verify(mFragment.getActivity()).startActivityForResultAsUser(argumentCaptor.capture(), + anyInt(), nullable(UserHandle.class)); Intent intent = argumentCaptor.getValue(); assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN); From 2e4f9fe61b9a2ba445dc49ad4d2cf11ac079e101 Mon Sep 17 00:00:00 2001 From: Leo Hsu Date: Fri, 20 Apr 2018 10:40:08 +0800 Subject: [PATCH 04/25] Usage graphs support RTL style. Flips the canvas horizontally before drawing paths under RTL mode. Bug: 78008951 Test: manually verified graphs of wifi data usage, app data usage, and battery usage. Change-Id: I79286e9f3f98632e3a9710d42c3e197231a14c39 --- res/layout/usage_view.xml | 3 +-- src/com/android/settings/widget/UsageGraph.java | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/res/layout/usage_view.xml b/res/layout/usage_view.xml index 16b4916eff5..c24f28974ac 100644 --- a/res/layout/usage_view.xml +++ b/res/layout/usage_view.xml @@ -80,8 +80,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:orientation="horizontal" - android:layoutDirection="ltr"> + android:orientation="horizontal"> diff --git a/src/com/android/settings/widget/UsageGraph.java b/src/com/android/settings/widget/UsageGraph.java index da61607278f..7238e5de128 100644 --- a/src/com/android/settings/widget/UsageGraph.java +++ b/src/com/android/settings/widget/UsageGraph.java @@ -255,9 +255,15 @@ public class UsageGraph extends View { return; } + canvas.save(); + if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) { + // Flip the canvas along the y-axis of the center of itself before drawing paths. + canvas.scale(-1, 1, canvas.getWidth() * 0.5f, 0); + } drawLinePath(canvas, mLocalProjectedPaths, mDottedPaint); drawFilledPath(canvas, mLocalPaths, mFillPaint); drawLinePath(canvas, mLocalPaths, mLinePaint); + canvas.restore(); BatteryUtils.logRuntime(LOG_TAG, "onDraw", startTime); } From 1c21bff1034b5124b69bda22c2fc9f70a449ffa7 Mon Sep 17 00:00:00 2001 From: timhypeng Date: Fri, 25 May 2018 14:01:02 +0800 Subject: [PATCH 05/25] To show hearing aids device in the available devices group Bug: 79553082 Test: make -j50 RunSettingsRoboTests Change-Id: I8fe5eb653d332ef5ac4a566146f150774581167c --- .../AvailableMediaBluetoothDeviceUpdater.java | 5 ++++ .../ConnectedBluetoothDeviceUpdater.java | 5 ++++ ...ilableMediaBluetoothDeviceUpdaterTest.java | 27 ++++++++++++++++++ .../ConnectedBluetoothDeviceUpdaterTest.java | 28 +++++++++++++++++++ 4 files changed, 65 insertions(+) diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java index 9a287f1786b..ea520ea88bf 100644 --- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java @@ -97,6 +97,11 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater if (DBG) { Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile); } + // If device is Hearing Aid, it is compatible with HFP and A2DP. + // It would show in Available Devices group. + if (cachedDevice.isConnectedHearingAidDevice()) { + return true; + } // According to the current audio profile type, // this page will show the bluetooth device that have corresponding profile. // For example: diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java index 0ac3e6d850b..bb543bad496 100644 --- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java @@ -101,6 +101,11 @@ public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater { if (DBG) { Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile); } + // If device is Hearing Aid, it is compatible with HFP and A2DP. + // It would not show in Connected Devices group. + if (cachedDevice.isConnectedHearingAidDevice()) { + return false; + } // According to the current audio profile type, // this page will show the bluetooth device that doesn't have corresponding profile. // For example: diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java index ced8fc4a2e4..0b1311d9364 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java @@ -203,6 +203,33 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); } + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_notInCall_addPreference() + { + mShadowAudioManager.setMode(AudioManager.MODE_NORMAL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); + } + + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_inCall_addPreference() { + mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); + } + @Test public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() { mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java index 483df01a6b8..8120d91d28f 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java @@ -204,6 +204,34 @@ public class ConnectedBluetoothDeviceUpdaterTest { verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); } + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_inCall_removePreference() + { + mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); + } + + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_notInCall_removePreference + () { + mShadowAudioManager.setMode(AudioManager.MODE_NORMAL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); + } + @Test public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() { mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, From ca85cc9d278d19eef92d9ed37668e906522f56c2 Mon Sep 17 00:00:00 2001 From: Artem Iglikov Date: Tue, 12 Jun 2018 11:40:24 +0000 Subject: [PATCH 06/25] DO NOT MERGE Revert "Fix gray out previously connected device preference issue" This reverts commit 95abf59af43bafe4922b8cc096b10b65440419b8. Reason for revert: Crashes settings (b/110075201) Bug: 110075201 Change-Id: Ie6ea94e0891754f18576bd21447b64410d2fd23a --- ...ouslyConnectedDevicePreferenceController.java | 16 +++------------- ...yConnectedDevicePreferenceControllerTest.java | 6 ------ 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java index 73c95ab4e38..ba3ee1f1054 100644 --- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java @@ -21,29 +21,26 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; +import android.util.Log; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; -import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; -import com.android.settings.overlay.FeatureFactory; + import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.utils.ThreadUtils; public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback { private Preference mPreference; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; - private DockUpdater mSavedDockUpdater; private int mPreferenceSize; public PreviouslyConnectedDevicePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); - - mSavedDockUpdater = FeatureFactory.getFactory( - context).getDockUpdaterFeatureProvider().getSavedDockUpdater(context, this); } @Override @@ -65,14 +62,12 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc @Override public void onStart() { mBluetoothDeviceUpdater.registerCallback(); - mSavedDockUpdater.registerCallback(); updatePreferenceOnSizeChanged(); } @Override public void onStop() { mBluetoothDeviceUpdater.unregisterCallback(); - mSavedDockUpdater.unregisterCallback(); } public void init(DashboardFragment fragment) { @@ -97,11 +92,6 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mBluetoothDeviceUpdater = bluetoothDeviceUpdater; } - @VisibleForTesting - void setSavedDockUpdater(DockUpdater savedDockUpdater) { - mSavedDockUpdater = savedDockUpdater; - } - @VisibleForTesting void setPreferenceSize(int size) { mPreferenceSize = size; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java index d04b206c6dd..444728a1c4c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java @@ -21,7 +21,6 @@ import android.content.pm.PackageManager; import android.support.v7.preference.Preference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; -import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -47,8 +46,6 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Mock private BluetoothDeviceUpdater mBluetoothDeviceUpdater; @Mock - private DockUpdater mDockUpdater; - @Mock private PackageManager mPackageManager; private Context mContext; @@ -64,7 +61,6 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { mPreConnectedDeviceController = new PreviouslyConnectedDevicePreferenceController(mContext, KEY); mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); - mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater); mPreference = new Preference(mContext); mPreConnectedDeviceController.setPreference(mPreference); @@ -75,12 +71,10 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { // register the callback in onStart() mPreConnectedDeviceController.onStart(); verify(mBluetoothDeviceUpdater).registerCallback(); - verify(mDockUpdater).registerCallback(); // unregister the callback in onStop() mPreConnectedDeviceController.onStop(); verify(mBluetoothDeviceUpdater).unregisterCallback(); - verify(mDockUpdater).unregisterCallback(); } @Test From ff73564ea0fb26f3a2f688d10bede49d05b4417f Mon Sep 17 00:00:00 2001 From: yuemingw Date: Mon, 9 Apr 2018 17:00:38 +0100 Subject: [PATCH 07/25] Remove internal ApnSetting. Remove com.android.internal.telephony.dataconnection.ApnSetting and use android.telephony.data.ApnSetting instead. Bug: 77511388 Test: existing test Change-Id: I8ca1fb399026a8ef4ba9afa18678d986daa50606 --- src/com/android/settings/network/ApnSettings.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/network/ApnSettings.java b/src/com/android/settings/network/ApnSettings.java index bbbcdbcbb63..0537d8fe160 100755 --- a/src/com/android/settings/network/ApnSettings.java +++ b/src/com/android/settings/network/ApnSettings.java @@ -43,6 +43,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.Log; import android.view.Menu; @@ -54,7 +55,7 @@ import android.widget.Toast; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.dataconnection.ApnSetting; +import com.android.internal.telephony.dataconnection.ApnSettingUtils; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.UiccController; import com.android.settings.R; @@ -330,7 +331,8 @@ public class ApnSettings extends RestrictedSettingsFragment implements ArrayList mvnoList, IccRecords r, String mvnoType, String mvnoMatchData) { if (r != null && !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData)) { - if (ApnSetting.mvnoMatches(r, mvnoType, mvnoMatchData)) { + if (ApnSettingUtils.mvnoMatches(r, ApnSetting.getMvnoTypeIntFromString(mvnoType), + mvnoMatchData)) { mvnoList.add(pref); // Since adding to mvno list, save mvno info mMvnoType = mvnoType; From addc82e454e313ae8228c1f3ee90f212d8c5dff2 Mon Sep 17 00:00:00 2001 From: Irina Dumitrescu Date: Fri, 8 Jun 2018 19:26:25 +0100 Subject: [PATCH 08/25] Clean up KeyStore exception warning caused by adding a new VPN. Bug:109791294 Test: manually navigate to settings and click the + button in VPN. Change-Id: Ib94778ef3fdab0fc8147477b91f644c54dc04c24 --- src/com/android/settings/vpn2/VpnUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/vpn2/VpnUtils.java b/src/com/android/settings/vpn2/VpnUtils.java index 4528180b0ee..38c56c527d2 100644 --- a/src/com/android/settings/vpn2/VpnUtils.java +++ b/src/com/android/settings/vpn2/VpnUtils.java @@ -38,7 +38,8 @@ public class VpnUtils { private static final String TAG = "VpnUtils"; public static String getLockdownVpn() { - final byte[] value = KeyStore.getInstance().get(Credentials.LOCKDOWN_VPN); + final byte[] value = KeyStore.getInstance().get( + Credentials.LOCKDOWN_VPN, true /* suppressKeyNotFoundWarning */); return value == null ? null : new String(value); } From 63767d2b9769fc5e8e5f0c2baf382f9667c1a309 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 12 Jun 2018 12:06:17 -0700 Subject: [PATCH 09/25] Hide quicksetting tile for dev options when it's turned off Change-Id: I3e11700a59c8a88bb586c77a876963b5e6a62c89 Fixes: 78652607 Test: robotests --- .../development/qstile/DevelopmentTiles.java | 20 ++++++++++++++++++- .../qstile/DevelopmentTilesTest.java | 13 ++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/development/qstile/DevelopmentTiles.java b/src/com/android/settings/development/qstile/DevelopmentTiles.java index 5482b028d35..ec017c510be 100644 --- a/src/com/android/settings/development/qstile/DevelopmentTiles.java +++ b/src/com/android/settings/development/qstile/DevelopmentTiles.java @@ -16,7 +16,9 @@ package com.android.settings.development.qstile; +import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; @@ -25,7 +27,6 @@ import android.os.SystemProperties; import android.provider.Settings; import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; -import androidx.annotation.VisibleForTesting; import android.util.Log; import android.view.IWindowManager; import android.view.ThreadedRenderer; @@ -34,9 +35,12 @@ import android.view.WindowManagerGlobal; import android.widget.Toast; import com.android.internal.app.LocalePicker; +import com.android.internal.statusbar.IStatusBarService; import com.android.settingslib.development.DevelopmentSettingsEnabler; import com.android.settingslib.development.SystemPropPoker; +import androidx.annotation.VisibleForTesting; + public abstract class DevelopmentTiles extends TileService { private static final String TAG = "DevelopmentTiles"; @@ -58,6 +62,20 @@ public abstract class DevelopmentTiles extends TileService { setIsEnabled(false); SystemPropPoker.getInstance().poke(); } + final ComponentName cn = new ComponentName(getPackageName(), getClass().getName()); + try { + getPackageManager().setComponentEnabledSetting( + cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); + if (statusBarService != null) { + statusBarService.remTile(cn); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to modify QS tile for component " + + cn.toString(), e); + } state = Tile.STATE_UNAVAILABLE; } else { state = isEnabled() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; diff --git a/tests/robotests/src/com/android/settings/development/qstile/DevelopmentTilesTest.java b/tests/robotests/src/com/android/settings/development/qstile/DevelopmentTilesTest.java index 85c1cb522d2..8367fd3980e 100644 --- a/tests/robotests/src/com/android/settings/development/qstile/DevelopmentTilesTest.java +++ b/tests/robotests/src/com/android/settings/development/qstile/DevelopmentTilesTest.java @@ -19,7 +19,10 @@ package com.android.settings.development.qstile; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import android.content.ComponentName; +import android.content.pm.PackageManager; import android.service.quicksettings.Tile; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -37,6 +40,9 @@ public class DevelopmentTilesTest { @Mock private Tile mTile; + @Mock + private PackageManager mPackageManager; + private DevelopmentTiles mService; @Before @@ -48,11 +54,18 @@ public class DevelopmentTilesTest { @Test public void refresh_devOptionIsDisabled_shouldResetTileValue() { + final ComponentName cn = new ComponentName( + mService.getPackageName(), mService.getClass().getName()); + doReturn(mPackageManager).when(mService).getPackageManager(); + DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(mService, false); mService.setIsEnabled(true); mService.refresh(); + verify(mPackageManager).setComponentEnabledSetting(cn, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); assertThat(mService.isEnabled()).isFalse(); } } From 2511d108ed8052d6f13bc431609bae1ec41b3dc5 Mon Sep 17 00:00:00 2001 From: Doris Ling Date: Mon, 11 Jun 2018 15:11:44 -0700 Subject: [PATCH 10/25] Consolidate the 2 app button prefernece controllers. - move the fuelgauge app button controller and the button dialog fragment into app info package and change app detail fragment to use that controller instead. - remove the original appinfo/AppActionButtonPreferenceController Change-Id: I94ca072a8dfe6051853eb23efa1f96ac3e13d79d Fixes: 80312809 Test: make RunSettingsRoboTests --- .../AppActionButtonPreferenceController.java | 321 ---------------- .../AppButtonsPreferenceController.java | 23 +- .../appinfo/AppInfoDashboardFragment.java | 215 +---------- .../appinfo}/ButtonActionDialogFragment.java | 5 +- .../fuelgauge/AdvancedPowerUsageDetail.java | 14 +- ...lid_base_preference_controller_constructor | 2 +- ...pActionButtonPreferenceControllerTest.java | 360 ------------------ .../AppButtonsPreferenceControllerTest.java | 21 +- .../appinfo/AppInfoDashboardFragmentTest.java | 9 - .../ButtonActionDialogFragmentTest.java | 4 +- ...referenceControllerSignatureInspector.java | 2 +- 11 files changed, 58 insertions(+), 918 deletions(-) delete mode 100644 src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java rename src/com/android/settings/{fuelgauge => applications/appinfo}/AppButtonsPreferenceController.java (97%) rename src/com/android/settings/{fuelgauge => applications/appinfo}/ButtonActionDialogFragment.java (96%) delete mode 100644 tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java rename tests/robotests/src/com/android/settings/{fuelgauge => applications/appinfo}/AppButtonsPreferenceControllerTest.java (93%) rename tests/robotests/src/com/android/settings/{fuelgauge => applications/appinfo}/ButtonActionDialogFragmentTest.java (97%) diff --git a/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java b/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java deleted file mode 100644 index 79c22248a69..00000000000 --- a/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * 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.appinfo; - -import android.app.Activity; -import android.app.ActivityManager; -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.os.UserManager; -import androidx.annotation.VisibleForTesting; -import androidx.preference.PreferenceScreen; -import android.util.Log; -import android.webkit.IWebViewUpdateService; - -import com.android.settings.R; -import com.android.settings.Utils; -import com.android.settings.applications.ApplicationFeatureProvider; -import com.android.settings.core.BasePreferenceController; -import com.android.settings.overlay.FeatureFactory; -import com.android.settings.widget.ActionButtonPreference; -import com.android.settingslib.RestrictedLockUtils; -import com.android.settingslib.applications.AppUtils; -import com.android.settingslib.applications.ApplicationsState.AppEntry; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -public class AppActionButtonPreferenceController extends BasePreferenceController - implements AppInfoDashboardFragment.Callback { - - private static final String TAG = "AppActionButtonControl"; - private static final String KEY_ACTION_BUTTONS = "action_buttons"; - - @VisibleForTesting - ActionButtonPreference mActionButtons; - private final AppInfoDashboardFragment mParent; - private final String mPackageName; - private final HashSet mHomePackages = new HashSet<>(); - private final ApplicationFeatureProvider mApplicationFeatureProvider; - - private int mUserId; - private DevicePolicyManager mDpm; - private UserManager mUserManager; - private PackageManager mPm; - - private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final boolean enabled = getResultCode() != Activity.RESULT_CANCELED; - Log.d(TAG, "Got broadcast response: Restart status for " - + mParent.getAppEntry().info.packageName + " " + enabled); - updateForceStopButton(enabled); - } - }; - - public AppActionButtonPreferenceController(Context context, AppInfoDashboardFragment parent, - String packageName) { - super(context, KEY_ACTION_BUTTONS); - mParent = parent; - mPackageName = packageName; - mUserId = UserHandle.myUserId(); - mApplicationFeatureProvider = FeatureFactory.getFactory(context) - .getApplicationFeatureProvider(context); - } - - @Override - public int getAvailabilityStatus() { - return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo) - ? DISABLED_FOR_USER : AVAILABLE; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mActionButtons = ((ActionButtonPreference) screen.findPreference(KEY_ACTION_BUTTONS)) - .setButton2Text(R.string.force_stop) - .setButton2Positive(false) - .setButton2Enabled(false); - } - - @Override - public void refreshUi() { - if (mPm == null) { - mPm = mContext.getPackageManager(); - } - if (mDpm == null) { - mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - } - if (mUserManager == null) { - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - } - final AppEntry appEntry = mParent.getAppEntry(); - final PackageInfo packageInfo = mParent.getPackageInfo(); - - // Get list of "home" apps and trace through any meta-data references - final List homeActivities = new ArrayList(); - mPm.getHomeActivities(homeActivities); - mHomePackages.clear(); - for (int i = 0; i < homeActivities.size(); i++) { - final ResolveInfo ri = homeActivities.get(i); - final String activityPkg = ri.activityInfo.packageName; - mHomePackages.add(activityPkg); - - // Also make sure to include anything proxying for the home app - final Bundle metadata = ri.activityInfo.metaData; - if (metadata != null) { - final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); - if (signaturesMatch(metaPkg, activityPkg)) { - mHomePackages.add(metaPkg); - } - } - } - - checkForceStop(appEntry, packageInfo); - initUninstallButtons(appEntry, packageInfo); - } - - @VisibleForTesting - void initUninstallButtons(AppEntry appEntry, PackageInfo packageInfo) { - final boolean isBundled = (appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - boolean enabled; - if (isBundled) { - enabled = handleDisableable(appEntry, packageInfo); - } else { - enabled = initUninstallButtonForUserApp(); - } - // If this is a device admin, it can't be uninstalled or disabled. - // We do this here so the text of the button is still set correctly. - if (isBundled && mDpm.packageHasActiveAdmins(packageInfo.packageName)) { - enabled = false; - } - - // We don't allow uninstalling DO/PO on *any* users, because if it's a system app, - // "uninstall" is actually "downgrade to the system version + disable", and "downgrade" - // will clear data on all users. - if (Utils.isProfileOrDeviceOwner(mUserManager, mDpm, packageInfo.packageName)) { - enabled = false; - } - - // Don't allow uninstalling the device provisioning package. - if (Utils.isDeviceProvisioningPackage(mContext.getResources(), appEntry.info.packageName)) { - enabled = false; - } - - // If the uninstall intent is already queued, disable the uninstall button - if (mDpm.isUninstallInQueue(mPackageName)) { - enabled = false; - } - - // Home apps need special handling. Bundled ones we don't risk downgrading - // because that can interfere with home-key resolution. Furthermore, we - // can't allow uninstallation of the only home app, and we don't want to - // allow uninstallation of an explicitly preferred one -- the user can go - // to Home settings and pick a different one, after which we'll permit - // uninstallation of the now-not-default one. - if (enabled && mHomePackages.contains(packageInfo.packageName)) { - if (isBundled) { - enabled = false; - } else { - ArrayList homeActivities = new ArrayList(); - ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); - if (currentDefaultHome == null) { - // No preferred default, so permit uninstall only when - // there is more than one candidate - enabled = (mHomePackages.size() > 1); - } else { - // There is an explicit default home app -- forbid uninstall of - // that one, but permit it for installed-but-inactive ones. - enabled = !packageInfo.packageName.equals(currentDefaultHome.getPackageName()); - } - } - } - - if (RestrictedLockUtils.hasBaseUserRestriction( - mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId)) { - enabled = false; - } - - try { - final IWebViewUpdateService webviewUpdateService = - IWebViewUpdateService.Stub.asInterface( - ServiceManager.getService("webviewupdate")); - if (webviewUpdateService.isFallbackPackage(appEntry.info.packageName)) { - enabled = false; - } - } catch (RemoteException e) { - throw new RuntimeException(e); - } - - mActionButtons.setButton1Enabled(enabled); - if (enabled) { - // Register listener - mActionButtons.setButton1OnClickListener(v -> mParent.handleUninstallButtonClick()); - } - } - - @VisibleForTesting - boolean initUninstallButtonForUserApp() { - boolean enabled = true; - final PackageInfo packageInfo = mParent.getPackageInfo(); - if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 - && mUserManager.getUsers().size() >= 2) { - // When we have multiple users, there is a separate menu - // to uninstall for all users. - enabled = false; - } else if (AppUtils.isInstant(packageInfo.applicationInfo)) { - enabled = false; - mActionButtons.setButton1Visible(false); - } - mActionButtons.setButton1Text(R.string.uninstall_text).setButton1Positive(false); - return enabled; - } - - @VisibleForTesting - boolean handleDisableable(AppEntry appEntry, PackageInfo packageInfo) { - boolean disableable = false; - // Try to prevent the user from bricking their phone - // by not allowing disabling of apps signed with the - // system cert and any launcher app in the system. - if (mHomePackages.contains(appEntry.info.packageName) - || Utils.isSystemPackage(mContext.getResources(), mPm, packageInfo)) { - // Disable button for core system applications. - mActionButtons - .setButton1Text(R.string.disable_text) - .setButton1Positive(false); - } else if (appEntry.info.enabled && appEntry.info.enabledSetting - != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { - mActionButtons - .setButton1Text(R.string.disable_text) - .setButton1Positive(false); - disableable = !mApplicationFeatureProvider.getKeepEnabledPackages() - .contains(appEntry.info.packageName); - } else { - mActionButtons - .setButton1Text(R.string.enable_text) - .setButton1Positive(true); - disableable = true; - } - - return disableable; - } - - private void updateForceStopButton(boolean enabled) { - final boolean disallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction( - mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId); - mActionButtons - .setButton2Enabled(disallowedBySystem ? false : enabled) - .setButton2OnClickListener( - disallowedBySystem ? null : v -> mParent.handleForceStopButtonClick()); - } - - void checkForceStop(AppEntry appEntry, PackageInfo packageInfo) { - if (mDpm.packageHasActiveAdmins(packageInfo.packageName)) { - // User can't force stop device admin. - Log.w(TAG, "User can't force stop device admin"); - updateForceStopButton(false); - } else if (mPm.isPackageStateProtected(packageInfo.packageName, - UserHandle.getUserId(appEntry.info.uid))) { - Log.w(TAG, "User can't force stop protected packages"); - updateForceStopButton(false); - } else if (AppUtils.isInstant(packageInfo.applicationInfo)) { - updateForceStopButton(false); - mActionButtons.setButton2Visible(false); - } else if ((appEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) { - // If the app isn't explicitly stopped, then always show the - // force stop button. - Log.w(TAG, "App is not explicitly stopped"); - updateForceStopButton(true); - } else { - final Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, - Uri.fromParts("package", appEntry.info.packageName, null)); - intent.putExtra(Intent.EXTRA_PACKAGES, new String[] {appEntry.info.packageName}); - intent.putExtra(Intent.EXTRA_UID, appEntry.info.uid); - intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(appEntry.info.uid)); - Log.d(TAG, "Sending broadcast to query restart status for " - + appEntry.info.packageName); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, - mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); - } - } - - private boolean signaturesMatch(String pkg1, String pkg2) { - if (pkg1 != null && pkg2 != null) { - try { - return mPm.checkSignatures(pkg1, pkg2) >= PackageManager.SIGNATURE_MATCH; - } catch (Exception e) { - // e.g. named alternate package not found during lookup; - // this is an expected case sometimes - } - } - return false; - } - -} diff --git a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java similarity index 97% rename from src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java rename to src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java index a2ba9a96181..dfbe064b832 100644 --- a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings.fuelgauge; +package com.android.settings.applications.appinfo; import android.app.Activity; import android.app.ActivityManager; @@ -49,13 +49,13 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.ActionButtonPreference; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; -import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -74,8 +74,7 @@ import java.util.List; * An easy way to handle them is to delegate them to {@link #handleDialogClick(int)} and * {@link #handleActivityResult(int, int, Intent)} in this controller. */ -//TODO(80312809): Merge this class into {@link AppActionButtonPreferenceController} -public class AppButtonsPreferenceController extends AbstractPreferenceController implements +public class AppButtonsPreferenceController extends BasePreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnDestroy, ApplicationsState.Callbacks { public static final String APP_CHG = "chg"; @@ -120,9 +119,8 @@ public class AppButtonsPreferenceController extends AbstractPreferenceController public AppButtonsPreferenceController(SettingsActivity activity, Fragment fragment, Lifecycle lifecycle, String packageName, ApplicationsState state, - DevicePolicyManager dpm, UserManager userManager, - PackageManager packageManager, int requestUninstall, int requestRemoveDeviceAdmin) { - super(activity); + int requestUninstall, int requestRemoveDeviceAdmin) { + super(activity, KEY_ACTION_BUTTONS); if (!(fragment instanceof ButtonActionDialogFragment.AppButtonsDialogListener)) { throw new IllegalArgumentException( @@ -133,9 +131,9 @@ public class AppButtonsPreferenceController extends AbstractPreferenceController mMetricsFeatureProvider = factory.getMetricsFeatureProvider(); mApplicationFeatureProvider = factory.getApplicationFeatureProvider(activity); mState = state; - mDpm = dpm; - mUserManager = userManager; - mPm = packageManager; + mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); + mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); + mPm = activity.getPackageManager(); mPackageName = packageName; mActivity = activity; mFragment = fragment; @@ -153,9 +151,10 @@ public class AppButtonsPreferenceController extends AbstractPreferenceController } @Override - public boolean isAvailable() { + public int getAvailabilityStatus() { // TODO(b/37313605): Re-enable once this controller supports instant apps - return mAppEntry != null && !AppUtils.isInstant(mAppEntry.info); + return mAppEntry != null && !AppUtils.isInstant(mAppEntry.info) + ? AVAILABLE : DISABLED_FOR_USER ; } @Override diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 1f8a3a07585..7107ff754aa 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -19,14 +19,12 @@ package com.android.settings.applications.appinfo; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import android.app.Activity; -import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; @@ -35,7 +33,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -47,7 +44,6 @@ import android.view.MenuInflater; import android.view.MenuItem; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settings.DeviceAdminAdd; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; @@ -62,7 +58,6 @@ import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -77,7 +72,8 @@ import java.util.List; * uninstall the application. */ public class AppInfoDashboardFragment extends DashboardFragment - implements ApplicationsState.Callbacks { + implements ApplicationsState.Callbacks, + ButtonActionDialogFragment.AppButtonsDialogListener { private static final String TAG = "AppInfoDashboard"; @@ -101,10 +97,7 @@ public class AppInfoDashboardFragment extends DashboardFragment // Dialog identifiers used in showDialog private static final int DLG_BASE = 0; - private static final int DLG_FORCE_STOP = DLG_BASE + 1; - private static final int DLG_DISABLE = DLG_BASE + 2; - private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; - static final int DLG_CLEAR_INSTANT_APP = DLG_BASE + 4; + static final int DLG_CLEAR_INSTANT_APP = DLG_BASE + 1; public static final String ARG_PACKAGE_NAME = "package"; public static final String ARG_PACKAGE_UID = "uid"; @@ -132,12 +125,11 @@ public class AppInfoDashboardFragment extends DashboardFragment private boolean mInitialized; private boolean mShowUninstalled; private boolean mUpdatedSysApp = false; - private boolean mDisableAfterUninstall; private List mCallbacks = new ArrayList<>(); private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; - private AppActionButtonPreferenceController mAppActionButtonPreferenceController; + private AppButtonsPreferenceController mAppButtonsPreferenceController; /** * Callback to invoke when app info has been changed. @@ -146,11 +138,6 @@ public class AppInfoDashboardFragment extends DashboardFragment void refreshUi(); } - private boolean isDisabledUntilUsed() { - return mAppEntry.info.enabledSetting - == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; - } - @Override public void onAttach(Context context) { super.onAttach(context); @@ -262,9 +249,6 @@ public class AppInfoDashboardFragment extends DashboardFragment // when app state changes. controllers.add( new AppHeaderViewPreferenceController(context, this, packageName, lifecycle)); - mAppActionButtonPreferenceController = - new AppActionButtonPreferenceController(context, this, packageName); - controllers.add(mAppActionButtonPreferenceController); for (AbstractPreferenceController controller : controllers) { mCallbacks.add((Callback) controller); @@ -275,6 +259,10 @@ public class AppInfoDashboardFragment extends DashboardFragment mInstantAppButtonPreferenceController = new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle); controllers.add(mInstantAppButtonPreferenceController); + mAppButtonsPreferenceController = new AppButtonsPreferenceController( + (SettingsActivity) getActivity(), this, lifecycle, packageName, mState, + REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); + controllers.add(mAppButtonsPreferenceController); controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle)); controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName)); @@ -374,30 +362,19 @@ public class AppInfoDashboardFragment extends DashboardFragment @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case REQUEST_UNINSTALL: - // Refresh option menu - getActivity().invalidateOptionsMenu(); + if (requestCode == REQUEST_UNINSTALL) { + // Refresh option menu + getActivity().invalidateOptionsMenu(); + } + if (mAppButtonsPreferenceController != null) { + mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data); + } + } - if (mDisableAfterUninstall) { - mDisableAfterUninstall = false; - new DisableChanger(this, mAppEntry.info, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) - .execute((Object) null); - } - if (!refreshUi()) { - onPackageRemoved(); - } else { - startListeningToPackageRemove(); - } - break; - case REQUEST_REMOVE_DEVICE_ADMIN: - if (!refreshUi()) { - setIntentAndFinish(true, true); - } else { - startListeningToPackageRemove(); - } - break; + @Override + public void handleDialogClick(int id) { + if (mAppButtonsPreferenceController != null) { + mAppButtonsPreferenceController.handleDialogClick(id); } } @@ -442,6 +419,7 @@ public class AppInfoDashboardFragment extends DashboardFragment for (Callback callback : mCallbacks) { callback.refreshUi(); } + mAppButtonsPreferenceController.refreshUi(); if (!mInitialized) { // First time init: are we displaying an uninstalled app? @@ -471,53 +449,6 @@ public class AppInfoDashboardFragment extends DashboardFragment @VisibleForTesting AlertDialog createDialog(int id, int errorCode) { - switch (id) { - case DLG_DISABLE: - return new AlertDialog.Builder(getActivity()) - .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) - .setPositiveButton(R.string.app_disable_dlg_positive, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Disable the app - mMetricsFeatureProvider.action(getContext(), - MetricsEvent.ACTION_SETTINGS_DISABLE_APP); - new DisableChanger(AppInfoDashboardFragment.this, - mAppEntry.info, - PackageManager - .COMPONENT_ENABLED_STATE_DISABLED_USER) - .execute((Object) null); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - case DLG_SPECIAL_DISABLE: - return new AlertDialog.Builder(getActivity()) - .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) - .setPositiveButton(R.string.app_disable_dlg_positive, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Disable the app and ask for uninstall - mMetricsFeatureProvider.action(getContext(), - MetricsEvent.ACTION_SETTINGS_DISABLE_APP); - uninstallPkg(mAppEntry.info.packageName, - false, true); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - case DLG_FORCE_STOP: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) - .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) - .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Force stop - forceStopPackage(mAppEntry.info.packageName); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - } return mInstantAppButtonPreferenceController.createDialog(id); } @@ -530,22 +461,6 @@ public class AppInfoDashboardFragment extends DashboardFragment mMetricsFeatureProvider.action( getContext(), MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP); startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); - mDisableAfterUninstall = andDisable; - } - - private void forceStopPackage(String pkgName) { - mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName); - final ActivityManager am = (ActivityManager) getActivity().getSystemService( - Context.ACTIVITY_SERVICE); - Log.d(TAG, "Stopping package " + pkgName); - am.forceStopPackage(pkgName); - final int userId = UserHandle.getUserId(mAppEntry.info.uid); - mState.invalidatePackage(pkgName, userId); - final AppEntry newEnt = mState.getEntry(pkgName, userId); - if (newEnt != null) { - mAppEntry = newEnt; - } - mAppActionButtonPreferenceController.checkForceStop(mAppEntry, mPackageInfo); } public static void startAppInfoFragment(Class fragment, int title, Bundle args, @@ -565,74 +480,6 @@ public class AppInfoDashboardFragment extends DashboardFragment .launch(); } - void handleUninstallButtonClick() { - if (mAppEntry == null) { - setIntentAndFinish(true, true); - return; - } - final String packageName = mAppEntry.info.packageName; - if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { - stopListeningToPackageRemove(); - final Activity activity = getActivity(); - final Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class); - uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, - mPackageName); - mMetricsFeatureProvider.action( - activity, MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN); - activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN); - return; - } - final EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(), - packageName, mUserId); - final boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem || - RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId); - if (admin != null && !uninstallBlockedBySystem) { - RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin); - } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { - // If the system app has an update and this is the only user on the device, - // then offer to downgrade the app, otherwise only offer to disable the - // app for this user. - if (mUpdatedSysApp && isSingleUser()) { - showDialogInner(DLG_SPECIAL_DISABLE, 0); - } else { - showDialogInner(DLG_DISABLE, 0); - } - } else { - mMetricsFeatureProvider.action( - getActivity(), - MetricsEvent.ACTION_SETTINGS_ENABLE_APP); - new DisableChanger(this, mAppEntry.info, - PackageManager.COMPONENT_ENABLED_STATE_ENABLED) - .execute((Object) null); - } - } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { - uninstallPkg(packageName, true, false); - } else { - uninstallPkg(packageName, false, false); - } - } - - void handleForceStopButtonClick() { - if (mAppEntry == null) { - setIntentAndFinish(true, true); - return; - } - if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) { - RestrictedLockUtils.sendShowAdminSupportDetailsIntent( - getActivity(), mAppsControlDisallowedAdmin); - } else { - showDialogInner(DLG_FORCE_STOP, 0); - //forceStopPackage(mAppInfo.packageName); - } - } - - /** Returns whether there is only one user on this device, not including the system-only user */ - private boolean isSingleUser() { - final int userCount = mUserManager.getUserCount(); - return userCount == 1 || (mUserManager.isSplitSystemUser() && userCount == 2); - } - private void onPackageRemoved() { getActivity().finishActivity(SUB_INFO_FRAGMENT); getActivity().finishAndRemoveTask(); @@ -659,26 +506,6 @@ public class AppInfoDashboardFragment extends DashboardFragment return count; } - private static class DisableChanger extends AsyncTask { - final PackageManager mPm; - final WeakReference mActivity; - final ApplicationInfo mInfo; - final int mState; - - DisableChanger(AppInfoDashboardFragment activity, ApplicationInfo info, int state) { - mPm = activity.mPm; - mActivity = new WeakReference(activity); - mInfo = info; - mState = state; - } - - @Override - protected Object doInBackground(Object... params) { - mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); - return null; - } - } - private String getPackageName() { if (mPackageName != null) { return mPackageName; diff --git a/src/com/android/settings/fuelgauge/ButtonActionDialogFragment.java b/src/com/android/settings/applications/appinfo/ButtonActionDialogFragment.java similarity index 96% rename from src/com/android/settings/fuelgauge/ButtonActionDialogFragment.java rename to src/com/android/settings/applications/appinfo/ButtonActionDialogFragment.java index f4784a97824..a3f1bab78c5 100644 --- a/src/com/android/settings/fuelgauge/ButtonActionDialogFragment.java +++ b/src/com/android/settings/applications/appinfo/ButtonActionDialogFragment.java @@ -1,6 +1,5 @@ -package com.android.settings.fuelgauge; +package com.android.settings.applications.appinfo; -import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; @@ -26,7 +25,7 @@ public class ButtonActionDialogFragment extends InstrumentedDialogFragment imple /** * Interface to handle the dialog click */ - interface AppButtonsDialogListener { + public interface AppButtonsDialogListener { void handleDialogClick(int type); } diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index c25463a1f86..af1de4856f0 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -20,7 +20,6 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.LoaderManager; -import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.Loader; @@ -28,7 +27,6 @@ import android.content.pm.PackageManager; import android.os.BatteryStats; import android.os.Bundle; import android.os.UserHandle; -import android.os.UserManager; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import android.text.TextUtils; @@ -43,6 +41,8 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.LayoutPreference; +import com.android.settings.applications.appinfo.AppButtonsPreferenceController; +import com.android.settings.applications.appinfo.ButtonActionDialogFragment; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; @@ -112,9 +112,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements private AppButtonsPreferenceController mAppButtonsPreferenceController; private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController; - private DevicePolicyManager mDpm; - private UserManager mUserManager; - private PackageManager mPackageManager; private List mAnomalies; private String mPackageName; @@ -203,9 +200,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements super.onAttach(activity); mState = ApplicationsState.getInstance(getActivity().getApplication()); - mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); - mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); - mPackageManager = activity.getPackageManager(); mBatteryUtils = BatteryUtils.getInstance(getContext()); } @@ -332,8 +326,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements controllers.add(new BatteryOptimizationPreferenceController( (SettingsActivity) getActivity(), this, packageName)); mAppButtonsPreferenceController = new AppButtonsPreferenceController( - (SettingsActivity) getActivity(), this, getLifecycle(), packageName, mState, mDpm, - mUserManager, mPackageManager, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); + (SettingsActivity) getActivity(), this, getLifecycle(), packageName, mState, + REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); controllers.add(mAppButtonsPreferenceController); return controllers; diff --git a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor index c2b3da98a1f..a2be681ce5c 100644 --- a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor +++ b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor @@ -1,4 +1,4 @@ -com.android.settings.applications.appinfo.AppActionButtonPreferenceController +com.android.settings.applications.appinfo.AppButtonsPreferenceController com.android.settings.applications.appinfo.AppBatteryPreferenceController com.android.settings.applications.appinfo.AppHeaderViewPreferenceController com.android.settings.applications.appinfo.AppMemoryPreferenceController diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java deleted file mode 100644 index cf423f7a635..00000000000 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * 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.appinfo; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.os.Bundle; -import android.os.Handler; -import android.os.UserHandle; -import android.os.UserManager; -import androidx.preference.PreferenceScreen; - -import com.android.settings.R; -import com.android.settings.testutils.FakeFeatureFactory; -import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.widget.ActionButtonPreference; -import com.android.settings.widget.ActionButtonPreferenceTest; -import com.android.settingslib.Utils; -import com.android.settingslib.applications.AppUtils; -import com.android.settingslib.applications.ApplicationsState; -import com.android.settingslib.applications.instantapps.InstantAppDataProvider; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.util.ReflectionHelpers; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -@RunWith(SettingsRobolectricTestRunner.class) -public class AppActionButtonPreferenceControllerTest { - - @Mock - private UserManager mUserManager; - @Mock - private DevicePolicyManager mDevicePolicyManager; - @Mock - private AppInfoDashboardFragment mFragment; - @Mock - private ApplicationInfo mAppInfo; - @Mock - private PackageManager mPackageManager; - - private Context mContext; - private AppActionButtonPreferenceController mController; - private FakeFeatureFactory mFeatureFactory; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mFeatureFactory = FakeFeatureFactory.setupForTest(); - mContext = spy(RuntimeEnvironment.application); - mController = spy(new AppActionButtonPreferenceController(mContext, mFragment, "Package1")); - mController.mActionButtons = ActionButtonPreferenceTest.createMock(); - ReflectionHelpers.setField(mController, "mUserManager", mUserManager); - ReflectionHelpers.setField(mController, "mDpm", mDevicePolicyManager); - ReflectionHelpers.setField(mController, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - ReflectionHelpers.setField(mController, "mPm", mPackageManager); - when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - final PackageInfo packageInfo = mock(PackageInfo.class); - packageInfo.applicationInfo = mAppInfo; - when(mFragment.getPackageInfo()).thenReturn(packageInfo); - } - - @Test - public void getAvailabilityStatus_notInstantApp_shouldReturnAvailable() { - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE); - } - - @Test - public void getAvailabilityStatus_isInstantApp_shouldReturnDisabled() { - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); - } - - @Test - public void displayPreference_shouldInitializeForceStopButton() { - final PreferenceScreen screen = mock(PreferenceScreen.class); - final ActionButtonPreference preference = spy(new ActionButtonPreference(mContext)); - when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference); - - mController.displayPreference(screen); - - verify(preference).setButton2Positive(false); - verify(preference).setButton2Text(R.string.force_stop); - verify(preference).setButton2Enabled(false); - } - - @Test - public void refreshUi_shouldRefreshButton() { - final PackageInfo packageInfo = mock(PackageInfo.class); - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - doNothing().when(mController).checkForceStop(appEntry, packageInfo); - doNothing().when(mController).initUninstallButtons(appEntry, packageInfo); - when(mFragment.getAppEntry()).thenReturn(appEntry); - when(mFragment.getPackageInfo()).thenReturn(packageInfo); - - mController.refreshUi(); - - verify(mController).checkForceStop(appEntry, packageInfo); - verify(mController).initUninstallButtons(appEntry, packageInfo); - } - - @Test - public void initUninstallButtonForUserApp_shouldSetNegativeButton() { - final ApplicationInfo info = new ApplicationInfo(); - info.flags = ApplicationInfo.FLAG_INSTALLED; - info.enabled = true; - final PackageInfo packageInfo = mock(PackageInfo.class); - packageInfo.applicationInfo = info; - when(mFragment.getPackageInfo()).thenReturn(packageInfo); - - assertThat(mController.initUninstallButtonForUserApp()).isTrue(); - verify(mController.mActionButtons).setButton1Positive(false); - } - - // Tests that we don't show the uninstall button for instant apps" - @Test - public void initUninstallButtonForUserApp_instantApps_noUninstallButton() { - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - final ApplicationInfo info = new ApplicationInfo(); - info.flags = ApplicationInfo.FLAG_INSTALLED; - info.enabled = true; - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - appEntry.info = info; - final PackageInfo packageInfo = mock(PackageInfo.class); - packageInfo.applicationInfo = info; - when(mFragment.getPackageInfo()).thenReturn(packageInfo); - - assertThat(mController.initUninstallButtonForUserApp()).isFalse(); - verify(mController.mActionButtons).setButton1Visible(false); - } - - @Test - public void initUninstallButtonForUserApp_notInstalledForCurrentUser_shouldDisableButton() { - final ApplicationInfo info = new ApplicationInfo(); - info.enabled = true; - final PackageInfo packageInfo = mock(PackageInfo.class); - packageInfo.applicationInfo = info; - when(mFragment.getPackageInfo()).thenReturn(packageInfo); - final int userID1 = 1; - final int userID2 = 2; - final List userInfos = new ArrayList<>(); - userInfos.add(new UserInfo(userID1, "User1", UserInfo.FLAG_PRIMARY)); - userInfos.add(new UserInfo(userID2, "User2", UserInfo.FLAG_GUEST)); - when(mUserManager.getUsers(true)).thenReturn(userInfos); - - assertThat(mController.initUninstallButtonForUserApp()).isFalse(); - } - - // Tests that we don't show the force stop button for instant apps (they aren't allowed to run - // when they aren't in the foreground). - @Test - public void checkForceStop_instantApps_shouldNotShowForceStop() { - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - final PackageInfo packageInfo = mock(PackageInfo.class); - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - - mController.checkForceStop(appEntry, packageInfo); - - verify(mController.mActionButtons).setButton2Visible(false); - } - - @Test - public void checkForceStop_isStateProtected_shouldDisableForceStop() { - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - final String packageName = "Package1"; - final PackageInfo packageInfo = new PackageInfo(); - packageInfo.packageName = packageName; - final ApplicationInfo appInfo = new ApplicationInfo(); - appInfo.uid = 42; - appInfo.sourceDir = "source"; - final ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry( - mContext, appInfo, 0); - when(mPackageManager.isPackageStateProtected(packageName, 0)).thenReturn(true); - - mController.checkForceStop(appEntry, packageInfo); - - verify(mController.mActionButtons).setButton2Enabled(false); - } - - @Test - public void checkForceStop_hasActiveAdmin_shouldDisableForceStop() { - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - final String packageName = "Package1"; - final PackageInfo packageInfo = new PackageInfo(); - packageInfo.packageName = packageName; - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - when(mDevicePolicyManager.packageHasActiveAdmins(packageName)).thenReturn(true); - - mController.checkForceStop(appEntry, packageInfo); - - verify(mController.mActionButtons).setButton2Enabled(false); - } - - @Test - public void checkForceStop_appRunning_shouldEnableForceStop() { - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - final PackageInfo packageInfo = mock(PackageInfo.class); - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - - mController.checkForceStop(appEntry, packageInfo); - - verify(mController.mActionButtons).setButton2Enabled(true); - } - - @Test - public void checkForceStop_appStopped_shouldQueryPackageRestart() { - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - final PackageInfo packageInfo = mock(PackageInfo.class); - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - info.flags = ApplicationInfo.FLAG_STOPPED; - info.packageName = "com.android.setting"; - - mController.checkForceStop(appEntry, packageInfo); - - verify(mContext).sendOrderedBroadcastAsUser(argThat(intent -> intent != null - && intent.getAction().equals(Intent.ACTION_QUERY_PACKAGE_RESTART)), - any(UserHandle.class), nullable(String.class), any(BroadcastReceiver.class), - nullable(Handler.class), anyInt(), nullable(String.class), nullable(Bundle.class)); - } - - @Test - public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = true; - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - appEntry.info = info; - final HashSet homePackages = new HashSet<>(); - homePackages.add(info.packageName); - ReflectionHelpers.setField(mController, "mHomePackages", homePackages); - - assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isFalse(); - verify(mController.mActionButtons).setButton1Text(R.string.disable_text); - } - - @Test - @Config(shadows = ShadowUtils.class) - public void handleDisableable_appIsEnabled_buttonShouldWork() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = true; - info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - appEntry.info = info; - when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()) - .thenReturn(new HashSet<>()); - - assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isTrue(); - verify(mController.mActionButtons).setButton1Text(R.string.disable_text); - } - - @Test - @Config(shadows = ShadowUtils.class) - public void handleDisableable_appIsDisabled_buttonShouldShowEnable() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = false; - info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - appEntry.info = info; - when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()) - .thenReturn(new HashSet<>()); - - assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isTrue(); - verify(mController.mActionButtons).setButton1Text(R.string.enable_text); - verify(mController.mActionButtons).setButton1Positive(true); - } - - @Test - @Config(shadows = ShadowUtils.class) - public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = true; - info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); - appEntry.info = info; - final HashSet packages = new HashSet<>(); - packages.add(info.packageName); - when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()) - .thenReturn(packages); - - assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isFalse(); - verify(mController.mActionButtons).setButton1Text(R.string.disable_text); - } - - @Implements(Utils.class) - public static class ShadowUtils { - @Implementation - public static boolean isSystemPackage(Resources resources, PackageManager pm, - PackageInfo pkg) { - return false; - } - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java similarity index 93% rename from tests/robotests/src/com/android/settings/fuelgauge/AppButtonsPreferenceControllerTest.java rename to tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java index 551cb3e0bb0..24579ef961f 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AppButtonsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings.fuelgauge; +package com.android.settings.applications.appinfo; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; @@ -44,6 +44,8 @@ import android.os.UserManager; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.applications.appinfo.AppButtonsPreferenceController; +import com.android.settings.applications.appinfo.ButtonActionDialogFragment; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.widget.ActionButtonPreference; import com.android.settings.widget.ActionButtonPreferenceTest; @@ -107,6 +109,7 @@ public class AppButtonsPreferenceControllerTest { MockitoAnnotations.initMocks(this); FakeFeatureFactory.setupForTest(); + doReturn(mDpm).when(mSettingsActivity).getSystemService(Context.DEVICE_POLICY_SERVICE); doReturn(mUserManager).when(mSettingsActivity).getSystemService(Context.USER_SERVICE); doReturn(mPackageManger).when(mSettingsActivity).getPackageManager(); doReturn(mAm).when(mSettingsActivity).getSystemService(Context.ACTIVITY_SERVICE); @@ -115,8 +118,7 @@ public class AppButtonsPreferenceControllerTest { when(mSettingsActivity.getResources().getString(anyInt())).thenReturn(RESOURCE_STRING); mController = spy(new AppButtonsPreferenceController(mSettingsActivity, mFragment, - mLifecycle, PACKAGE_NAME, mState, mDpm, mUserManager, mPackageManger, - REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN)); + mLifecycle, PACKAGE_NAME, mState, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN)); doReturn(false).when(mController).isFallbackPackage(anyString()); mAppEntry.info = mAppInfo; @@ -334,6 +336,15 @@ public class AppButtonsPreferenceControllerTest { assertThat(controllable).isTrue(); } + @Test + public void handleActivityResult_packageUninstalled_shouldFinishPrefernecePanel() { + doReturn(false).when(mController).refreshUi(); + + mController.handleActivityResult(REQUEST_UNINSTALL, 0, mock(Intent.class)); + + verify(mSettingsActivity).finishPreferencePanel(anyInt(), any(Intent.class)); + } + @Test public void refreshUi_packageNull_shouldNotCrash() { mController.mPackageName = null; @@ -344,7 +355,7 @@ public class AppButtonsPreferenceControllerTest { @Test public void onPackageListChanged_available_shouldRefreshUi() { - doReturn(true).when(mController).isAvailable(); + doReturn(mController.AVAILABLE).when(mController).getAvailabilityStatus(); doReturn(true).when(mController).refreshUi(); mController.onPackageListChanged(); @@ -354,7 +365,7 @@ public class AppButtonsPreferenceControllerTest { @Test public void onPackageListChanged_notAvailable_shouldNotRefreshUiAndNoCrash() { - doReturn(false).when(mController).isAvailable(); + doReturn(mController.DISABLED_FOR_USER).when(mController).getAvailabilityStatus(); mController.onPackageListChanged(); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java index 3128f30f656..8314cb70f5d 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java @@ -232,15 +232,6 @@ public final class AppInfoDashboardFragmentTest { verify(mActivity).invalidateOptionsMenu(); } - @Test - public void onActivityResult_packageUninstalled_shouldFinishAndRemoveTask() { - doReturn(false).when(mFragment).refreshUi(); - - mFragment.onActivityResult(mFragment.REQUEST_UNINSTALL, 0, mock(Intent.class)); - - verify(mActivity).finishAndRemoveTask(); - } - @Test public void getPreferenceControllers_noPackageInfo_shouldReturnNull() { doNothing().when(mFragment).retrieveAppEntry(); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/ButtonActionDialogFragmentTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ButtonActionDialogFragmentTest.java similarity index 97% rename from tests/robotests/src/com/android/settings/fuelgauge/ButtonActionDialogFragmentTest.java rename to tests/robotests/src/com/android/settings/applications/appinfo/ButtonActionDialogFragmentTest.java index 83d8f7a81f6..ae6ba1d8146 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/ButtonActionDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/ButtonActionDialogFragmentTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.settings.fuelgauge; +package com.android.settings.applications.appinfo; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.anyInt; @@ -128,7 +128,7 @@ public class ButtonActionDialogFragmentTest { /** * Test fragment that used as the target fragment, it must implement the - * {@link com.android.settings.fuelgauge.ButtonActionDialogFragment.AppButtonsDialogListener} + * {@link ButtonActionDialogFragment.AppButtonsDialogListener} */ public static class TestFragment extends Fragment implements ButtonActionDialogFragment.AppButtonsDialogListener { diff --git a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java index a9b4f5dc550..ce438b06bb0 100644 --- a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java +++ b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java @@ -67,7 +67,7 @@ public class BasePreferenceControllerSignatureInspector extends CodeInspector { } assertWithMessage("All BasePreferenceController (and subclasses) constructor must either" - + "only take Context, or (Context, String). No other types are allowed") + + " only take Context, or (Context, String). No other types are allowed") .that(badClasses.toString()) .isEmpty(); From acff57fa686d903cd9f7c253286f45e0094c7894 Mon Sep 17 00:00:00 2001 From: Salvador Martinez Date: Tue, 29 May 2018 17:53:02 -0700 Subject: [PATCH 11/25] Change UI for wireless AP tether band selection We decided to use the list preference after all due to some devices not supporting various combinations of AP configurations. This change makes it so that there are three different UIs depending on the configurations which are supported. - 5.0 GHz unsupported: disable the preference and just set the value to 2.4 GHz - all supported, no dual mode: allow the user to choose EITHER 2.4 GHz or 5.0 GHz - all supported, dual mode: allow the user to choose 2.4 GHz or BOTH 2.4 GHz & 5.0 GHz with 5.0 being preferred Test: atest SettingsRoboTests Bug: 80315296 Change-Id: I888d35811a98b8cf0155a3cb96c42ff762763378 Merged-In: I888d35811a98b8cf0155a3cb96c42ff762763378 --- res/values/arrays.xml | 20 +- res/values/strings.xml | 4 +- res/xml/wifi_tether_settings.xml | 8 +- .../HotspotApBandSelectionPreference.java | 258 ------------------ .../WifiTetherApBandPreferenceController.java | 66 +++-- .../HotspotApBandSelectionPreferenceTest.java | 158 ----------- ...iTetherApBandPreferenceControllerTest.java | 66 +++-- 7 files changed, 118 insertions(+), 462 deletions(-) delete mode 100644 src/com/android/settings/widget/HotspotApBandSelectionPreference.java delete mode 100644 tests/robotests/src/com/android/settings/widget/HotspotApBandSelectionPreferenceTest.java diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 5f39738a8af..be7a0ca50b6 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -284,17 +284,27 @@ - + + 0 + 1 + + + @string/wifi_ap_choose_2G @string/wifi_ap_choose_5G - - @string/wifi_ap_2G - @string/wifi_ap_5G + + 0 + -1 - + + @string/wifi_ap_choose_2G + @string/wifi_ap_prefer_5G + + + @string/wifi_ap_choose_auto @string/wifi_ap_choose_2G diff --git a/res/values/strings.xml b/res/values/strings.xml index 1b5f12571da..30290a788c9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1990,8 +1990,10 @@ Auto 2.4 GHz Band - + 5.0 GHz Band + + 5.0 GHz Band preferred 2.4 GHz diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml index a609621e581..525870e453f 100644 --- a/res/xml/wifi_tether_settings.xml +++ b/res/xml/wifi_tether_settings.xml @@ -43,11 +43,7 @@ android:title="@string/wifi_hotspot_auto_off_title" android:summary="@string/wifi_hotspot_auto_off_summary" /> - + android:title="@string/wifi_hotspot_ap_band_title" /> \ No newline at end of file diff --git a/src/com/android/settings/widget/HotspotApBandSelectionPreference.java b/src/com/android/settings/widget/HotspotApBandSelectionPreference.java deleted file mode 100644 index 0a2405e4136..00000000000 --- a/src/com/android/settings/widget/HotspotApBandSelectionPreference.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.widget; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.net.wifi.WifiConfiguration; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.VisibleForTesting; -import android.util.AttributeSet; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.LinearLayout; - -import com.android.settings.R; -import com.android.settingslib.CustomDialogPreference; - -import java.util.ArrayList; - -public class HotspotApBandSelectionPreference extends CustomDialogPreference implements - CompoundButton.OnCheckedChangeListener, DialogInterface.OnShowListener { - private static final int UNSET = Integer.MIN_VALUE; - - @VisibleForTesting - static final String KEY_CHECKED_BANDS = "checked_bands"; - @VisibleForTesting - static final String KEY_HOTSPOT_SUPER_STATE = "hotspot_super_state"; - - @VisibleForTesting - CheckBox mBox2G; - @VisibleForTesting - CheckBox mBox5G; - @VisibleForTesting - ArrayList mRestoredBands; - @VisibleForTesting - boolean mShouldRestore; - - private String[] mBandEntries; - private int mExistingConfigValue = UNSET; - - public HotspotApBandSelectionPreference(Context context) { - super(context); - } - - public HotspotApBandSelectionPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public HotspotApBandSelectionPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public HotspotApBandSelectionPreference(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - SavedState myState = (SavedState) state; - - super.onRestoreInstanceState(myState.getSuperState()); - - mShouldRestore = myState.shouldRestore; - if (mShouldRestore) { - mRestoredBands = new ArrayList<>(); - if (myState.enabled2G) { - mRestoredBands.add(WifiConfiguration.AP_BAND_2GHZ); - } - if (myState.enabled5G) { - mRestoredBands.add(WifiConfiguration.AP_BAND_5GHZ); - } - } else { - mRestoredBands = null; - } - updatePositiveButton(); - } - - @Override - protected void onBindDialogView(View view) { - super.onBindDialogView(view); - final Context context = getContext(); - - // Register so we can adjust the buttons if needed once the dialog is available. - setOnShowListener(this); - - mBandEntries = context.getResources().getStringArray(R.array.wifi_ap_band_config_full); - // add a checkbox for every band entry. - addApBandViews((LinearLayout) view); - // try to update the button just in case we already missed the onShow call. - updatePositiveButton(); - // clear any saved state so it doesn't leak across multiple rotations/dialog closings - mRestoredBands = null; - mShouldRestore = false; - } - - @Override - protected Parcelable onSaveInstanceState() { - final Parcelable superState = super.onSaveInstanceState(); - - SavedState myState = new SavedState(superState); - myState.shouldRestore = getDialog() != null; - myState.enabled2G = mBox2G != null && mBox2G.isChecked(); - myState.enabled5G = mBox5G != null && mBox5G.isChecked(); - return myState; - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (!(buttonView instanceof CheckBox)) { - return; - } - updatePositiveButton(); - } - - @Override - protected void onClick(DialogInterface dialog, int which) { - // we only want to persist our enabled bands if apply is clicked - if (which == DialogInterface.BUTTON_POSITIVE) { - if (mBox2G.isChecked() || mBox5G.isChecked()) { - int wifiBand = getWifiBand(); - mExistingConfigValue = wifiBand; - callChangeListener(wifiBand); - } - } - } - - /** - * Used to set the band selection for the preference if one already exists - * @param band the band to set it to from {@link WifiConfiguration} - */ - public void setExistingConfigValue(int band) { - mExistingConfigValue = band; - } - - private void addApBandViews(LinearLayout view) { - mBox2G = view.findViewById(R.id.box_2g); - mBox2G.setText(mBandEntries[WifiConfiguration.AP_BAND_2GHZ]); - mBox2G.setChecked(restoreBandIfNeeded(WifiConfiguration.AP_BAND_2GHZ)); - mBox2G.setOnCheckedChangeListener(this); - - mBox5G = view.findViewById(R.id.box_5g); - mBox5G.setText(mBandEntries[WifiConfiguration.AP_BAND_5GHZ]); - mBox5G.setChecked(restoreBandIfNeeded(WifiConfiguration.AP_BAND_5GHZ)); - mBox5G.setOnCheckedChangeListener(this); - } - - private boolean restoreBandIfNeeded(int band) { - // Only use the provided config if we aren't restoring, restore if state available - return (isBandPreviouslySelected(band) && !mShouldRestore) - || (mShouldRestore && mRestoredBands.contains(band)); - } - - private void updatePositiveButton() { - AlertDialog dialog = (AlertDialog) getDialog(); - Button button = dialog == null ? null : dialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (button != null && mBox5G != null && mBox2G != null) { - button.setEnabled(mBox2G.isChecked() || mBox5G.isChecked()); - } - } - - @VisibleForTesting - int getWifiBand() { - final boolean checked_2g = mBox2G.isChecked(); - final boolean checked_5g = mBox5G.isChecked(); - if (checked_2g && checked_5g) { - return WifiConfiguration.AP_BAND_ANY; - } else if (checked_2g && !checked_5g) { - return WifiConfiguration.AP_BAND_2GHZ; - } else if (checked_5g && !checked_2g) { - return WifiConfiguration.AP_BAND_5GHZ; - } else { - throw new IllegalStateException("Wifi Config only supports selecting one or all bands"); - } - } - - private boolean isBandPreviouslySelected(int bandIndex) { - switch(mExistingConfigValue) { - case WifiConfiguration.AP_BAND_ANY: - return true; - case WifiConfiguration.AP_BAND_2GHZ: - return bandIndex == 0; - case WifiConfiguration.AP_BAND_5GHZ: - return bandIndex == 1; - case UNSET: - default: - return false; - } - } - - @Override - public void onShow(DialogInterface dialog) { - updatePositiveButton(); - } - - private static class SavedState extends BaseSavedState { - boolean shouldRestore; - boolean enabled2G; - boolean enabled5G; - - public SavedState(Parcelable source) { - super(source); - } - - private SavedState(Parcel in) { - super(in); - shouldRestore = in.readByte() == 1; - enabled2G = in.readByte() == 1; - enabled5G = in.readByte() == 1; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeByte((byte) (shouldRestore ? 1 : 0)); - dest.writeByte((byte) (enabled2G ? 1: 0)); - dest.writeByte((byte) (enabled5G ? 1 : 0)); - } - - @Override - public String toString() { - return "HotspotApBandSelectionPreference.SavedState{" - + Integer.toHexString(System.identityHashCode(this)) - + " shouldRestore=" + shouldRestore - + " enabled2G=" + enabled2G - + " enabled5G=" + enabled5G + "}"; - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } -} diff --git a/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java index b107777f67f..2624c736327 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java @@ -16,36 +16,31 @@ package com.android.settings.wifi.tether; -import static android.net.wifi.WifiConfiguration.AP_BAND_2GHZ; -import static android.net.wifi.WifiConfiguration.AP_BAND_5GHZ; - import android.content.Context; import android.content.res.Resources; -import android.icu.text.ListFormatter; import android.net.wifi.WifiConfiguration; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.ListPreference; import android.support.v7.preference.Preference; import android.util.Log; import com.android.settings.R; -import com.android.settings.widget.HotspotApBandSelectionPreference; public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferenceController { private static final String TAG = "WifiTetherApBandPref"; private static final String PREF_KEY = "wifi_tether_network_ap_band"; - public static final String[] BAND_VALUES = - {String.valueOf(AP_BAND_2GHZ), String.valueOf(AP_BAND_5GHZ)}; - private final String[] mBandEntries; - private final String[] mBandSummaries; + private String[] mBandEntries; + private String[] mBandSummaries; private int mBandIndex; + private boolean isDualMode; public WifiTetherApBandPreferenceController(Context context, OnTetherConfigUpdateListener listener) { super(context, listener); - Resources res = mContext.getResources(); - mBandEntries = res.getStringArray(R.array.wifi_ap_band_config_full); - mBandSummaries = res.getStringArray(R.array.wifi_ap_band_summary_full); + isDualMode = mWifiManager.isDualModeSupported(); + updatePreferenceEntries(); } @Override @@ -55,7 +50,7 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen mBandIndex = 0; Log.d(TAG, "Updating band index to 0 because no config"); } else if (is5GhzBandSupported()) { - mBandIndex = config.apBand; + mBandIndex = validateSelection(config.apBand); Log.d(TAG, "Updating band index to " + mBandIndex); } else { config.apBand = 0; @@ -63,21 +58,23 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen mBandIndex = config.apBand; Log.d(TAG, "5Ghz not supported, updating band index to " + mBandIndex); } - HotspotApBandSelectionPreference preference = - (HotspotApBandSelectionPreference) mPreference; + ListPreference preference = + (ListPreference) mPreference; + preference.setEntries(mBandSummaries); + preference.setEntryValues(mBandEntries); if (!is5GhzBandSupported()) { preference.setEnabled(false); preference.setSummary(R.string.wifi_ap_choose_2G); } else { - preference.setExistingConfigValue(config.apBand); + preference.setValue(Integer.toString(config.apBand)); preference.setSummary(getConfigSummary()); } } String getConfigSummary() { if (mBandIndex == WifiConfiguration.AP_BAND_ANY) { - return ListFormatter.getInstance().format((Object[]) mBandSummaries); + return mContext.getString(R.string.wifi_ap_prefer_5G); } return mBandSummaries[mBandIndex]; } @@ -89,13 +86,46 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - mBandIndex = (Integer) newValue; + mBandIndex = validateSelection(Integer.parseInt((String) newValue)); Log.d(TAG, "Band preference changed, updating band index to " + mBandIndex); preference.setSummary(getConfigSummary()); mListener.onTetherConfigUpdated(); return true; } + private int validateSelection(int band) { + // Reset the band to 2.4 GHz if we get a weird config back to avoid a crash. + final boolean isDualMode = mWifiManager.isDualModeSupported(); + + // unsupported states: + // 1: no dual mode means we can't have AP_BAND_ANY - default to 5GHZ + // 2: no 5 GHZ support means we can't have AP_BAND_5GHZ - default to 2GHZ + // 3: With Dual mode support we can't have AP_BAND_5GHZ - default to ANY + if (!isDualMode && WifiConfiguration.AP_BAND_ANY == band) { + return WifiConfiguration.AP_BAND_5GHZ; + } else if (!mWifiManager.is5GHzBandSupported() && WifiConfiguration.AP_BAND_5GHZ == band) { + return WifiConfiguration.AP_BAND_2GHZ; + } else if (isDualMode && WifiConfiguration.AP_BAND_5GHZ == band) { + return WifiConfiguration.AP_BAND_ANY; + } + + return band; + } + + @VisibleForTesting + void updatePreferenceEntries() { + Resources res = mContext.getResources(); + int entriesRes = R.array.wifi_ap_band_config_full; + int summariesRes = R.array.wifi_ap_band_summary_full; + // change the list options if this is a dual mode device + if (isDualMode) { + entriesRes = R.array.wifi_ap_band_dual_mode; + summariesRes = R.array.wifi_ap_band_dual_mode_summary; + } + mBandEntries = res.getStringArray(entriesRes); + mBandSummaries = res.getStringArray(summariesRes); + } + private boolean is5GhzBandSupported() { final String countryCode = mWifiManager.getCountryCode(); if (!mWifiManager.isDualBandSupported() || countryCode == null) { diff --git a/tests/robotests/src/com/android/settings/widget/HotspotApBandSelectionPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/HotspotApBandSelectionPreferenceTest.java deleted file mode 100644 index 0ffda3baa96..00000000000 --- a/tests/robotests/src/com/android/settings/widget/HotspotApBandSelectionPreferenceTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.widget; - -import static com.android.settingslib.CustomDialogPreference.CustomPreferenceDialogFragment; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.AlertDialog; -import android.content.Context; -import android.net.wifi.WifiConfiguration; -import android.os.Bundle; -import android.os.Parcelable; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.LinearLayout; - -import com.android.settings.R; -import com.android.settings.testutils.SettingsRobolectricTestRunner; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.util.ReflectionHelpers; - -import java.util.List; - -@RunWith(SettingsRobolectricTestRunner.class) -public class HotspotApBandSelectionPreferenceTest { - private HotspotApBandSelectionPreference mPreference; - private Context mContext; - private Button mSaveButton; - private View mLayout; - - @Before - public void setUp() { - mContext = RuntimeEnvironment.application; - mSaveButton = spy(new Button(mContext)); - - final CustomPreferenceDialogFragment fragment = mock(CustomPreferenceDialogFragment.class); - final AlertDialog dialog = mock(AlertDialog.class); - when(fragment.getDialog()).thenReturn(dialog); - when(dialog.getButton(anyInt())).thenReturn(mSaveButton); - - mPreference = new HotspotApBandSelectionPreference(mContext); - ReflectionHelpers.setField(mPreference, "mFragment", fragment); - - final LayoutInflater inflater = LayoutInflater.from(mContext); - mLayout = inflater.inflate(R.layout.hotspot_ap_band_selection_dialog, - new LinearLayout(mContext), false); - } - - @Test - public void getWifiBand_updatesBandPresetConfigProvided() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - // check that the boxes are set correctly when a pre-existing config is set - assertThat(mPreference.getWifiBand()).isEqualTo(WifiConfiguration.AP_BAND_ANY); - } - - @Test - public void getWifiBand_updatesBandWhenBoxesToggled() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - assertThat(mPreference.getWifiBand()).isEqualTo(WifiConfiguration.AP_BAND_ANY); - - // make sure we have the expected box then toggle it - mPreference.mBox2G.setChecked(false); - - // check that band is updated - assertThat(mPreference.getWifiBand()).isEqualTo(WifiConfiguration.AP_BAND_5GHZ); - } - - @Test - public void onSaveInstanceState_skipWhenDialogGone() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_2GHZ); - mPreference.onBindDialogView(mLayout); - // remove the fragment to make the dialog unavailable - ReflectionHelpers.setField(mPreference, "mFragment", null); - - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - // state should only be saved when the dialog is available - Parcelable parcelable = mPreference.onSaveInstanceState(); - mPreference.onRestoreInstanceState(parcelable); - assertThat(mPreference.mShouldRestore).isFalse(); - } - - @Test - public void onSaveInstanceState_doesNotCrashWhenViewGone() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_2GHZ); - mPreference.onBindDialogView(mLayout); - // When the device dozes the view and dialog can become null - mPreference.mBox5G = null; - mPreference.mBox2G = null; - ReflectionHelpers.setField(mPreference, "mFragment", null); - - // make sure it does not crash and state is not restored - Parcelable parcelable = mPreference.onSaveInstanceState(); - mPreference.onRestoreInstanceState(parcelable); - assertThat(mPreference.mShouldRestore).isFalse(); - } - - @Test - public void onSaveInstanceState_presentWhenDialogPresent() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_2GHZ); - mPreference.onBindDialogView(mLayout); - - Parcelable parcelable = mPreference.onSaveInstanceState(); - mPreference.onRestoreInstanceState(parcelable); - assertThat(mPreference.mShouldRestore).isTrue(); - } - - @Test - public void positiveButton_updatedCorrectly() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - // button is enabled whole time so far since we have a pre-existing selection - verify(mSaveButton, never()).setEnabled(false); - - // clear all boxes and make sure it stays enabled until empty - mPreference.mBox2G.setChecked(false); - mPreference.mBox5G.setChecked(false); - - // button should be disabled now - verify(mSaveButton, times(1)).setEnabled(false); - } -} diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java index d9c867cf2a6..8df62c3c02e 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java @@ -18,6 +18,7 @@ package com.android.settings.wifi.tether; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,16 +27,15 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; +import android.support.v7.preference.ListPreference; import android.support.v7.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.widget.HotspotApBandSelectionPreference; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @@ -43,8 +43,9 @@ import org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) public class WifiTetherApBandPreferenceControllerTest { - private static final String ALL_BANDS = "2.4 GHz and 5.0 GHz"; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private static final String ALL_BANDS = "5.0 GHz Band preferred"; + private static final String TWO_GHZ_STRING = "2.4 GHz Band"; + private static final String FIVE_GHZ_STRING = "5.0 GHz Band"; private Context mContext; @Mock private ConnectivityManager mConnectivityManager; @@ -56,12 +57,13 @@ public class WifiTetherApBandPreferenceControllerTest { private PreferenceScreen mScreen; private WifiTetherApBandPreferenceController mController; - private HotspotApBandSelectionPreference mPreference; + private ListPreference mPreference; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mPreference = new HotspotApBandSelectionPreference(RuntimeEnvironment.application); + mContext = spy(RuntimeEnvironment.application); + mPreference = new ListPreference(RuntimeEnvironment.application); when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager); when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) .thenReturn(mConnectivityManager); @@ -71,6 +73,7 @@ public class WifiTetherApBandPreferenceControllerTest { WifiConfiguration config = new WifiConfiguration(); config.apBand = WifiConfiguration.AP_BAND_ANY; when(mWifiManager.getWifiApConfiguration()).thenReturn(new WifiConfiguration()); + when(mWifiManager.isDualModeSupported()).thenReturn(false); mController = new WifiTetherApBandPreferenceController(mContext, mListener); } @@ -79,9 +82,10 @@ public class WifiTetherApBandPreferenceControllerTest { public void display_5GhzSupported_shouldDisplayFullList() { when(mWifiManager.getCountryCode()).thenReturn("US"); when(mWifiManager.isDualBandSupported()).thenReturn(true); + when(mWifiManager.isDualModeSupported()).thenReturn(true); mController.displayPreference(mScreen); - mController.onPreferenceChange(mPreference, -1); + mController.onPreferenceChange(mPreference, "-1"); assertThat(mPreference.getSummary()).isEqualTo(ALL_BANDS); } @@ -110,24 +114,54 @@ public class WifiTetherApBandPreferenceControllerTest { } @Test - public void changePreference_shouldUpdateValue() { + public void changePreference_noDualModeWith5G_shouldUpdateValue() { when(mWifiManager.is5GHzBandSupported()).thenReturn(true); mController.displayPreference(mScreen); + // -1 is WifiConfiguration.AP_BAND_ANY, for 'Auto' option. This should be prevented from + // being set since it is invalid for this configuration + mController.onPreferenceChange(mPreference, "-1"); + assertThat(mController.getBandIndex()).isEqualTo(1); + assertThat(mPreference.getSummary()).isEqualTo(FIVE_GHZ_STRING); + verify(mListener, times(1)).onTetherConfigUpdated(); + + // set to 5 Ghz + mController.onPreferenceChange(mPreference, "1"); + assertThat(mController.getBandIndex()).isEqualTo(1); + assertThat(mPreference.getSummary()).isEqualTo(FIVE_GHZ_STRING); + verify(mListener, times(2)).onTetherConfigUpdated(); + + // set to 2 Ghz + mController.onPreferenceChange(mPreference, "0"); + assertThat(mController.getBandIndex()).isEqualTo(0); + assertThat(mPreference.getSummary()).isEqualTo(TWO_GHZ_STRING); + verify(mListener, times(3)).onTetherConfigUpdated(); + } + + @Test + public void changePreference_dualModeWith5G_shouldUpdateValue() { + when(mWifiManager.is5GHzBandSupported()).thenReturn(true); + when(mWifiManager.isDualModeSupported()).thenReturn(true); + + mController.displayPreference(mScreen); + // -1 is WifiConfiguration.AP_BAND_ANY, for 'Auto' option. - mController.onPreferenceChange(mPreference, -1); + mController.onPreferenceChange(mPreference, "-1"); assertThat(mController.getBandIndex()).isEqualTo(-1); assertThat(mPreference.getSummary()).isEqualTo(ALL_BANDS); + verify(mListener, times(1)).onTetherConfigUpdated(); - mController.onPreferenceChange(mPreference, 1); - assertThat(mController.getBandIndex()).isEqualTo(1); - assertThat(mPreference.getSummary()).isEqualTo("5.0 GHz"); + // should revert to the default for 5 Ghz only since this is not supported with this config + mController.onPreferenceChange(mPreference, "1"); + assertThat(mController.getBandIndex()).isEqualTo(-1); + assertThat(mPreference.getSummary()).isEqualTo(ALL_BANDS); + verify(mListener, times(2)).onTetherConfigUpdated(); - mController.onPreferenceChange(mPreference, 0); + // set to 2 Ghz + mController.onPreferenceChange(mPreference, "0"); assertThat(mController.getBandIndex()).isEqualTo(0); - assertThat(mPreference.getSummary()).isEqualTo("2.4 GHz"); - + assertThat(mPreference.getSummary()).isEqualTo(TWO_GHZ_STRING); verify(mListener, times(3)).onTetherConfigUpdated(); } @@ -136,7 +170,7 @@ public class WifiTetherApBandPreferenceControllerTest { // Set controller band index to 1 and verify is set. when(mWifiManager.is5GHzBandSupported()).thenReturn(true); mController.displayPreference(mScreen); - mController.onPreferenceChange(mPreference, 1); + mController.onPreferenceChange(mPreference, "1"); assertThat(mController.getBandIndex()).isEqualTo(1); // Disable 5Ghz band From 06d264de197c10835a2e6e80e31b267bf52872fd Mon Sep 17 00:00:00 2001 From: Doris Ling Date: Tue, 12 Jun 2018 16:33:05 -0700 Subject: [PATCH 12/25] Fix crash in ChooseLockGenericFragment. - in onActivityResult(), the intent data can be null. Check for non null intent data before trying to read the extra string from the intent. Change-Id: I14c42725a7885a84688ae39fde63e30ad0536001 Fixes: 109675331 Test: make RunSettingsRoboTests --- .../settings/password/ChooseLockGeneric.java | 11 +++-- .../password/ChooseLockGenericTest.java | 42 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index a2d4a3ab3ed..39380ea2111 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -39,6 +39,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import androidx.annotation.StringRes; +import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import android.text.TextUtils; @@ -124,7 +125,8 @@ public class ChooseLockGeneric extends SettingsActivity { */ public static final String EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS = "choose_lock_generic_extras"; - private static final int CONFIRM_EXISTING_REQUEST = 100; + @VisibleForTesting + static final int CONFIRM_EXISTING_REQUEST = 100; private static final int ENABLE_ENCRYPTION_REQUEST = 101; private static final int CHOOSE_LOCK_REQUEST = 102; private static final int CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST = 103; @@ -329,7 +331,9 @@ public class ChooseLockGeneric extends SettingsActivity { mWaitingForConfirmation = false; if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) { mPasswordConfirmed = true; - mUserPassword = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); + mUserPassword = data != null + ? data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD) + : null; updatePreferencesOrFinish(false /* isRecreatingActivity */); if (mForChangeCredRequiredForBoot) { if (!TextUtils.isEmpty(mUserPassword)) { @@ -394,7 +398,8 @@ public class ChooseLockGeneric extends SettingsActivity { } } - private void updatePreferencesOrFinish(boolean isRecreatingActivity) { + @VisibleForTesting + void updatePreferencesOrFinish(boolean isRecreatingActivity) { Intent intent = getActivity().getIntent(); int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1); if (quality == -1) { diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java new file mode 100644 index 00000000000..c66373b669b --- /dev/null +++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java @@ -0,0 +1,42 @@ +/* + * 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.password; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; + +import android.app.Activity; +import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(SettingsRobolectricTestRunner.class) +public class ChooseLockGenericTest { + + @Test + public void onActivityResult_nullIntentData_shouldNotCrash() { + ChooseLockGenericFragment fragment = spy(new ChooseLockGenericFragment()); + doNothing().when(fragment).updatePreferencesOrFinish(anyBoolean()); + + fragment.onActivityResult( + fragment.CONFIRM_EXISTING_REQUEST, Activity.RESULT_OK, null /* data */); + // no crash + } + +} From 6741ae41a79bb133a9740663e70fd20c977ee933 Mon Sep 17 00:00:00 2001 From: hughchen Date: Wed, 13 Jun 2018 11:09:41 +0800 Subject: [PATCH 13/25] Increase the character limit Increase the character limit to "CHAR LIMIT=NONE" for linguist request, the "CHAR LIMIT=30" is not enough for translated. Bug: 110086224 Test: Build pass Change-Id: Ie6ffe96d94b1fd4ea1137d2b956ff1a6711a103a --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 51ec5901793..e6c800bda02 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -289,7 +289,7 @@ Tap to pair with %1$s. Received files - + Files received via Bluetooth From 71ae12a1de816ab19c7959d99884fefd26266abe Mon Sep 17 00:00:00 2001 From: Ivan Chiang Date: Tue, 12 Jun 2018 13:46:13 +0800 Subject: [PATCH 14/25] Apply the correct theme to DockPreference Set the preference context to apply correct theme to preference. Bug: 110016611 Test: m -j SettingsRoboTests RunSettingsRoboTests Change-Id: Ifb680fdc0ff40f86886f91320b4187e9873efbf4 --- .../connecteddevice/ConnectedDeviceGroupController.java | 6 ++++-- .../connecteddevice/SavedDeviceGroupController.java | 5 ++++- .../android/settings/connecteddevice/dock/DockUpdater.java | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java index aa0b6cd54fd..654ea722335 100644 --- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java +++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java @@ -76,9 +76,11 @@ public class ConnectedDeviceGroupController extends BasePreferenceController mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY); mPreferenceGroup.setVisible(false); - mBluetoothDeviceUpdater.setPrefContext(screen.getContext()); + final Context context = screen.getContext(); + mBluetoothDeviceUpdater.setPrefContext(context); mBluetoothDeviceUpdater.forceUpdate(); - mConnectedUsbDeviceUpdater.initUsbPreference(screen.getContext()); + mConnectedUsbDeviceUpdater.initUsbPreference(context); + mConnectedDockUpdater.setPreferenceContext(context); mConnectedDockUpdater.forceUpdate(); } } diff --git a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java index 4bc9cdd637f..6f9b7ac2c04 100644 --- a/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java +++ b/src/com/android/settings/connecteddevice/SavedDeviceGroupController.java @@ -75,8 +75,11 @@ public class SavedDeviceGroupController extends BasePreferenceController if (isAvailable()) { mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY); mPreferenceGroup.setVisible(false); - mBluetoothDeviceUpdater.setPrefContext(screen.getContext()); + + final Context context = screen.getContext(); + mBluetoothDeviceUpdater.setPrefContext(context); mBluetoothDeviceUpdater.forceUpdate(); + mSavedDockUpdater.setPreferenceContext(context); mSavedDockUpdater.forceUpdate(); } } diff --git a/src/com/android/settings/connecteddevice/dock/DockUpdater.java b/src/com/android/settings/connecteddevice/dock/DockUpdater.java index 19ee7324179..99ac12475bc 100644 --- a/src/com/android/settings/connecteddevice/dock/DockUpdater.java +++ b/src/com/android/settings/connecteddevice/dock/DockUpdater.java @@ -15,6 +15,7 @@ */ package com.android.settings.connecteddevice.dock; +import android.annotation.NonNull; import android.content.Context; /** @@ -40,4 +41,10 @@ public interface DockUpdater { */ default void forceUpdate() { } + + /** + * Set the context to generate the {@link Preference}, so it could get the correct theme. + */ + default void setPreferenceContext(@NonNull Context preferenceContext) { + } } From b98d538bbe8ccabc8d6ba90ff6f5f56d02cf36e0 Mon Sep 17 00:00:00 2001 From: yuemingw Date: Wed, 13 Jun 2018 13:33:59 +0100 Subject: [PATCH 15/25] Remove internal ApnSetting. Remove com.android.internal.telephony.dataconnection.ApnSetting and use android.telephony.data.ApnSetting instead. Bug: 77511388 Test: existing test Change-Id: Idb66b7af22c0531d4ffc324075849abc08667a87 Merged-In: I8ca1fb399026a8ef4ba9afa18678d986daa50606 --- src/com/android/settings/ApnSettings.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/ApnSettings.java b/src/com/android/settings/ApnSettings.java index d554301063c..35d2b9f5ad0 100755 --- a/src/com/android/settings/ApnSettings.java +++ b/src/com/android/settings/ApnSettings.java @@ -44,6 +44,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.Log; import android.view.Menu; @@ -56,7 +57,7 @@ import android.widget.Toast; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.dataconnection.ApnSetting; +import com.android.internal.telephony.dataconnection.ApnSettingUtils; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.UiccController; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -330,7 +331,8 @@ public class ApnSettings extends RestrictedSettingsFragment implements ArrayList mvnoList, IccRecords r, String mvnoType, String mvnoMatchData) { if (r != null && !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData)) { - if (ApnSetting.mvnoMatches(r, mvnoType, mvnoMatchData)) { + if (ApnSettingUtils.mvnoMatches(r, ApnSetting.getMvnoTypeIntFromString(mvnoType), + mvnoMatchData)) { mvnoList.add(pref); // Since adding to mvno list, save mvno info mMvnoType = mvnoType; From 55dfd8a86bf47af46ce2af4199630557a1ac1abb Mon Sep 17 00:00:00 2001 From: Beverly Date: Thu, 7 Jun 2018 10:43:32 -0400 Subject: [PATCH 16/25] Add help uri for prevent ringing gesture Test: build Bug: 79270169 Change-Id: Iea5598f26649854521202363cad6ee297cef3057 (cherry picked from commit 486d1facfab222c686e03c344619aaf19280ea2a) --- res/values/strings.xml | 3 +++ .../settings/gestures/PreventRingingGestureSettings.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 30290a788c9..cd2b78362ae 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9928,6 +9928,9 @@ + + + Update Do Not Disturb diff --git a/src/com/android/settings/gestures/PreventRingingGestureSettings.java b/src/com/android/settings/gestures/PreventRingingGestureSettings.java index 09570dfb4f5..241e5c0bf5d 100644 --- a/src/com/android/settings/gestures/PreventRingingGestureSettings.java +++ b/src/com/android/settings/gestures/PreventRingingGestureSettings.java @@ -58,7 +58,7 @@ public class PreventRingingGestureSettings extends DashboardFragment { @Override public int getHelpResource() { - return 0; + return R.string.help_uri_prevent_ringing_gesture; } public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = From b81953228c19ac65a07b1cfbc7d9712072921723 Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Mon, 11 Jun 2018 14:30:09 -0700 Subject: [PATCH 17/25] Remove reference to preference_category_material_settings This layout in settingslib has been migrated to the support library's preference_category_material, so we can just reference that. Bug: 110037570 Test: manual (format of category headers should look normal in Settings timezone picker) Change-Id: I32df326e1d4afd4d7ae6f694c38554f3d0f54e84 --- .../android/settings/datetime/timezone/BaseTimeZoneAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java index 1a868b8f296..253bd2f1512 100644 --- a/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java +++ b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java @@ -78,7 +78,7 @@ public class BaseTimeZoneAdapter LayoutInflater inflater = LayoutInflater.from(parent.getContext()); switch(viewType) { case TYPE_HEADER: { - final View view = inflater.inflate(R.layout.preference_category_material_settings, + final View view = inflater.inflate(R.layout.preference_category_material, parent, false); return new HeaderViewHolder(view); } From bbf104b17d74e3e90201ea15b369ad8830020b06 Mon Sep 17 00:00:00 2001 From: Andre Garivay Date: Tue, 12 Jun 2018 15:43:35 -0700 Subject: [PATCH 18/25] Added Keywords for Device Theme Added 'dark theme' as keyword for Device Theme setting. Change-Id: Ib7b1d322f3451c6502d9d8c3cf28eb56bae08e58 Fixes: 110103985 Test: UI --- res/values/strings.xml | 3 +++ res/xml/display_settings.xml | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 51ec5901793..3247a7cc65f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6999,6 +6999,9 @@ serial number, hardware version android security patch level, baseband version, kernel version + + dark theme + bug diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml index 683abd33f67..87d422fd6f7 100644 --- a/res/xml/display_settings.xml +++ b/res/xml/display_settings.xml @@ -141,7 +141,8 @@ android:title="@string/device_theme" android:entries="@array/systemui_theme_entries" android:entryValues="@array/systemui_theme_values" - settings:controller="com.android.settings.display.SystemUiThemePreferenceController" /> + settings:controller="com.android.settings.display.SystemUiThemePreferenceController" + settings:keywords="@string/keywords_systemui_theme" /> Date: Wed, 13 Jun 2018 13:14:46 -0700 Subject: [PATCH 19/25] Use FIELD_ANOMALY_TYPE instead of FIELD_CONTEXT We did it for general anomaly however not the whitelisted one. This CL fixes it. Change-Id: I6603ffbaf1d097b360c86406ae0675bfe8a20410 Fixes: 109804858 Test: RunSettingsRoboTests --- .../fuelgauge/batterytip/AnomalyDetectionJobService.java | 2 +- .../fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java index f7793aa8e61..2dc35f17a74 100644 --- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java +++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java @@ -157,7 +157,7 @@ public class AnomalyDetectionJobService extends JobService { metricsFeatureProvider.action(context, MetricsProto.MetricsEvent.ACTION_ANOMALY_IGNORED, packageName, - Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, + Pair.create(MetricsProto.MetricsEvent.FIELD_ANOMALY_TYPE, anomalyInfo.anomalyType), Pair.create(MetricsProto.MetricsEvent.FIELD_APP_VERSION_CODE, versionCode)); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java index b55bf6b956c..adee4333169 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java @@ -178,7 +178,7 @@ public class AnomalyDetectionJobServiceTest { verify(mFeatureFactory.metricsFeatureProvider).action(mContext, MetricsProto.MetricsEvent.ACTION_ANOMALY_IGNORED, SYSTEM_PACKAGE, - Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, ANOMALY_TYPE), + Pair.create(MetricsProto.MetricsEvent.FIELD_ANOMALY_TYPE, ANOMALY_TYPE), Pair.create(MetricsProto.MetricsEvent.FIELD_APP_VERSION_CODE, VERSION_CODE)); } From 6776da1cadaf73d19fc859e7e19574313ee8cc2c Mon Sep 17 00:00:00 2001 From: Doris Ling Date: Thu, 31 May 2018 14:56:28 -0700 Subject: [PATCH 20/25] Do not turn on NFC automatically when airplane mode is off. - besides checking the value for AIRPLANE_MODE_TOGGLEABLE_RADIOS, also check the value for AIRPLANE_MODE_RADIOS to determine if we should turn off NFC when airplane mode is on. - when user turns off airplane mode, only re-enable the NFC preference and do not enable NFC by default. - remove listening to airplane mode directly from the android beam preference controller, as it is already listening to Nfc state, which handles the beam state already. Change-Id: Id41fef15fb2de873729d20d7f53b1a88b164cf95 Fixes: 80217047 Test: make RunSettingsRoboTests --- .../nfc/AndroidBeamPreferenceController.java | 16 --- .../android/settings/nfc/BaseNfcEnabler.java | 2 +- .../settings/nfc/NfcAirplaneModeObserver.java | 11 +- src/com/android/settings/nfc/NfcEnabler.java | 17 ++- .../settings/nfc/NfcPreferenceController.java | 19 +++- .../nfc/NfcAirplaneModeObserverTest.java | 54 +++++++-- .../android/settings/nfc/NfcEnablerTest.java | 106 ++++++++++++++++++ .../nfc/NfcPreferenceControllerTest.java | 60 ++++++++-- 8 files changed, 235 insertions(+), 50 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java diff --git a/src/com/android/settings/nfc/AndroidBeamPreferenceController.java b/src/com/android/settings/nfc/AndroidBeamPreferenceController.java index 12ab1b756dc..f99c47c188a 100644 --- a/src/com/android/settings/nfc/AndroidBeamPreferenceController.java +++ b/src/com/android/settings/nfc/AndroidBeamPreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.nfc; import android.content.Context; import android.nfc.NfcAdapter; -import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; @@ -26,15 +25,12 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; -import java.util.List; - public class AndroidBeamPreferenceController extends BasePreferenceController implements LifecycleObserver, OnResume, OnPause { public static final String KEY_ANDROID_BEAM_SETTINGS = "android_beam_settings"; private final NfcAdapter mNfcAdapter; private AndroidBeamEnabler mAndroidBeamEnabler; - private NfcAirplaneModeObserver mAirplaneModeObserver; public AndroidBeamPreferenceController(Context context, String key) { super(context, key); @@ -52,12 +48,6 @@ public class AndroidBeamPreferenceController extends BasePreferenceController final RestrictedPreference restrictedPreference = (RestrictedPreference) screen.findPreference(getPreferenceKey()); mAndroidBeamEnabler = new AndroidBeamEnabler(mContext, restrictedPreference); - - // Manually set dependencies for NFC when not toggleable. - if (!NfcPreferenceController.isToggleableInAirplaneMode(mContext)) { - mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter, - (Preference) restrictedPreference); - } } @Override @@ -70,9 +60,6 @@ public class AndroidBeamPreferenceController extends BasePreferenceController @Override public void onResume() { - if (mAirplaneModeObserver != null) { - mAirplaneModeObserver.register(); - } if (mAndroidBeamEnabler != null) { mAndroidBeamEnabler.resume(); } @@ -80,9 +67,6 @@ public class AndroidBeamPreferenceController extends BasePreferenceController @Override public void onPause() { - if (mAirplaneModeObserver != null) { - mAirplaneModeObserver.unregister(); - } if (mAndroidBeamEnabler != null) { mAndroidBeamEnabler.pause(); } diff --git a/src/com/android/settings/nfc/BaseNfcEnabler.java b/src/com/android/settings/nfc/BaseNfcEnabler.java index 88bafb98fb9..64c6d15c7ad 100644 --- a/src/com/android/settings/nfc/BaseNfcEnabler.java +++ b/src/com/android/settings/nfc/BaseNfcEnabler.java @@ -28,7 +28,7 @@ import androidx.preference.Preference; * preference. It will receive intent and update state to ensure preference show correct state. */ public abstract class BaseNfcEnabler { - private final Context mContext; + protected final Context mContext; protected final NfcAdapter mNfcAdapter; private final IntentFilter mIntentFilter; diff --git a/src/com/android/settings/nfc/NfcAirplaneModeObserver.java b/src/com/android/settings/nfc/NfcAirplaneModeObserver.java index d0ce045296f..65ac655bd8e 100644 --- a/src/com/android/settings/nfc/NfcAirplaneModeObserver.java +++ b/src/com/android/settings/nfc/NfcAirplaneModeObserver.java @@ -70,12 +70,13 @@ public class NfcAirplaneModeObserver extends ContentObserver { } mAirplaneMode = airplaneMode; - boolean toggleable = mAirplaneMode != 1; - if (toggleable) { - mNfcAdapter.enable(); - } else { + if (mAirplaneMode == 1) { + // airplane mode is on, need to turn off NFC, and check if user can toggle it mNfcAdapter.disable(); + mPreference.setEnabled(NfcPreferenceController.isToggleableInAirplaneMode(mContext)); + } else { + // airplane mode is off, no restriction + mPreference.setEnabled(true); } - mPreference.setEnabled(toggleable); } } diff --git a/src/com/android/settings/nfc/NfcEnabler.java b/src/com/android/settings/nfc/NfcEnabler.java index 507a0532378..777e7d114bd 100644 --- a/src/com/android/settings/nfc/NfcEnabler.java +++ b/src/com/android/settings/nfc/NfcEnabler.java @@ -18,8 +18,10 @@ package com.android.settings.nfc; import android.content.Context; import android.nfc.NfcAdapter; -import androidx.preference.SwitchPreference; +import android.provider.Settings; +import androidx.annotation.VisibleForTesting; +import androidx.preference.SwitchPreference; /** * NfcEnabler is a helper to manage the Nfc on/off checkbox preference. It turns on/off Nfc @@ -38,7 +40,7 @@ public class NfcEnabler extends BaseNfcEnabler { switch (newState) { case NfcAdapter.STATE_OFF: mPreference.setChecked(false); - mPreference.setEnabled(true); + mPreference.setEnabled(isToggleable()); break; case NfcAdapter.STATE_ON: mPreference.setChecked(true); @@ -54,4 +56,15 @@ public class NfcEnabler extends BaseNfcEnabler { break; } } + + @VisibleForTesting + boolean isToggleable() { + if (NfcPreferenceController.isToggleableInAirplaneMode(mContext) + || !NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)) { + return true; + } + final int airplaneMode = Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); + return airplaneMode != 1; + } } diff --git a/src/com/android/settings/nfc/NfcPreferenceController.java b/src/com/android/settings/nfc/NfcPreferenceController.java index e1fa1b3e6ce..0f68a9ce47c 100644 --- a/src/com/android/settings/nfc/NfcPreferenceController.java +++ b/src/com/android/settings/nfc/NfcPreferenceController.java @@ -20,7 +20,7 @@ import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.provider.Settings; -import androidx.preference.Preference; +import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; @@ -37,7 +37,8 @@ public class NfcPreferenceController extends TogglePreferenceController public static final String KEY_TOGGLE_NFC = "toggle_nfc"; private final NfcAdapter mNfcAdapter; private NfcEnabler mNfcEnabler; - private NfcAirplaneModeObserver mAirplaneModeObserver; + @VisibleForTesting + NfcAirplaneModeObserver mAirplaneModeObserver; public NfcPreferenceController(Context context, String key) { super(context, key); @@ -57,10 +58,10 @@ public class NfcPreferenceController extends TogglePreferenceController mNfcEnabler = new NfcEnabler(mContext, switchPreference); - // Manually set dependencies for NFC when not toggleable. - if (!isToggleableInAirplaneMode(mContext)) { - mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, - mNfcAdapter, (Preference) switchPreference); + // Listen to airplane mode updates if NFC should be turned off when airplane mode is on + if (shouldTurnOffNFCInAirplaneMode(mContext) || isToggleableInAirplaneMode(mContext)) { + mAirplaneModeObserver = + new NfcAirplaneModeObserver(mContext, mNfcAdapter, switchPreference); } } @@ -125,6 +126,12 @@ public class NfcPreferenceController extends TogglePreferenceController } } + public static boolean shouldTurnOffNFCInAirplaneMode(Context context) { + final String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS); + return airplaneModeRadios != null && airplaneModeRadios.contains(Settings.Global.RADIO_NFC); + } + public static boolean isToggleableInAirplaneMode(Context context) { final String toggleable = Settings.Global.getString(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); diff --git a/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java b/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java index 5efa94aaa98..c5e38a6fe44 100644 --- a/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java +++ b/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java @@ -18,11 +18,12 @@ package com.android.settings.nfc; import static com.google.common.truth.Truth.assertThat; +import android.content.ContentResolver; import android.content.Context; import android.nfc.NfcAdapter; import android.provider.Settings; -import androidx.preference.Preference; +import android.provider.Settings.Global; import androidx.preference.SwitchPreference; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -52,8 +53,8 @@ public class NfcAirplaneModeObserverTest { mNfcPreference = new SwitchPreference(RuntimeEnvironment.application); - mNfcAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter, - (Preference) mNfcPreference); + mNfcAirplaneModeObserver = + new NfcAirplaneModeObserver(mContext, mNfcAdapter, mNfcPreference); } @Test @@ -67,20 +68,51 @@ public class NfcAirplaneModeObserverTest { NfcAirplaneModeObserver.AIRPLANE_MODE_URI); assertThat(mNfcAdapter.isEnabled()).isFalse(); + } + + @Test + public void NfcAirplaneModeObserver_airplaneModeOnNfcToggleable_shouldEnablePreference() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 0); + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + + assertThat(mNfcPreference.isEnabled()).isTrue(); + } + + @Test + public void NfcAirplaneModeObserver_airplaneModeOnNfcNotToggleable_shouldDisablePreference() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 0); + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Global.RADIO_WIFI); + + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + assertThat(mNfcPreference.isEnabled()).isFalse(); } @Test - public void NfcAirplaneModeObserver_airplaneOff_shouldEnableNfc() { - ReflectionHelpers.setField(mNfcAirplaneModeObserver, - "mAirplaneMode", 1); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0); + public void NfcAirplaneModeObserver_airplaneModeOff_shouldEnablePreference() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 1); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); - mNfcAirplaneModeObserver.onChange(false, - NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); - assertThat(mNfcAdapter.isEnabled()).isTrue(); assertThat(mNfcPreference.isEnabled()).isTrue(); } + + @Test + public void NfcAirplaneModeObserver_airplaneModeOff_shouldNotEnableNfcAutomatically() { + ReflectionHelpers.setField(mNfcAirplaneModeObserver, "mAirplaneMode", 1); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); + + mNfcAirplaneModeObserver.onChange(false, NfcAirplaneModeObserver.AIRPLANE_MODE_URI); + + assertThat(mNfcAdapter.isEnabled()).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java new file mode 100644 index 00000000000..a10c27a874b --- /dev/null +++ b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java @@ -0,0 +1,106 @@ +/* + * 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.nfc; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.ContentResolver; +import android.content.Context; +import android.nfc.NfcAdapter; +import android.provider.Settings; +import androidx.preference.SwitchPreference; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class NfcEnablerTest { + + @Mock + private SwitchPreference mNfcPreference; + + private Context mContext; + private NfcEnabler mNfcEnabler; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mNfcEnabler = spy(new NfcEnabler(mContext, mNfcPreference)); + } + + @Test + public void isToggleable_AirplaneModeOff_shouldReturnTrue() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + assertThat(mNfcEnabler.isToggleable()).isTrue(); + } + + @Test + public void isToggleable_AirplaneModeOnNfcNotInAirplaneModeRadio_shouldReturnTrue() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, Settings.Global.AIRPLANE_MODE_RADIOS, ""); + + assertThat(mNfcEnabler.isToggleable()).isTrue(); + } + + @Test + public void isToggleable_AirplaneModeOnNfcToggleable_shouldReturnTrue() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + assertThat(mNfcEnabler.isToggleable()).isTrue(); + } + + @Test + public void isToggleable_AirplaneModeOnNfcNotToggleable_shouldReturnFalse() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + Settings.Global.putString(contentResolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, ""); + + assertThat(mNfcEnabler.isToggleable()).isFalse(); + } + + @Test + public void handleNfcStateChanged_stateOff_shouldCheckIfPreferenceEnableState() { + mNfcEnabler.handleNfcStateChanged(NfcAdapter.STATE_OFF); + + verify(mNfcEnabler).isToggleable(); + } +} diff --git a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java index eaf6425f43f..758f72c0998 100644 --- a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java @@ -75,15 +75,6 @@ public class NfcPreferenceControllerTest { mNfcPreference = new SwitchPreference(RuntimeEnvironment.application); when(mScreen.findPreference(mNfcController.getPreferenceKey())).thenReturn(mNfcPreference); - - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, - Settings.Global.RADIO_NFC); - - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, - 0); - mNfcController.displayPreference(mScreen); } @Test @@ -102,6 +93,7 @@ public class NfcPreferenceControllerTest { @Test public void isNfcEnable_nfcStateNotTurning_shouldReturnTrue() { + mNfcController.displayPreference(mScreen); when(mNfcAdapter.getAdapterState()).thenReturn(NfcAdapter.STATE_ON); mNfcController.onResume(); assertThat(mNfcPreference.isEnabled()).isTrue(); @@ -113,6 +105,7 @@ public class NfcPreferenceControllerTest { @Test public void isNfcEnable_nfcStateTurning_shouldReturnFalse() { + mNfcController.displayPreference(mScreen); when(mNfcAdapter.getAdapterState()).thenReturn(NfcAdapter.STATE_TURNING_ON); mNfcController.onResume(); assertThat(mNfcPreference.isEnabled()).isFalse(); @@ -124,6 +117,7 @@ public class NfcPreferenceControllerTest { @Test public void isNfcChecked_nfcStateOn_shouldReturnTrue() { + mNfcController.displayPreference(mScreen); when(mNfcAdapter.getAdapterState()).thenReturn(NfcAdapter.STATE_ON); mNfcController.onResume(); assertThat(mNfcPreference.isChecked()).isTrue(); @@ -205,4 +199,52 @@ public class NfcPreferenceControllerTest { assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isFalse(); } + + @Test + public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosContainsNfc_shouldReturnTrue() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + + assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isTrue(); + } + + @Test + public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosWithoutNfc_shouldReturnFalse() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, ""); + + assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isFalse(); + } + + @Test + public void displayPreference_airplaneModeRadiosContainsNfc_shouldCreateAirplaneModeObserver() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); + + mNfcController.displayPreference(mScreen); + + assertThat(mNfcController.mAirplaneModeObserver).isNotNull(); + } + + @Test + public void displayPreference_nfcToggleableInAirplaneMode_shouldCreateAirplaneModeObserver() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); + + mNfcController.displayPreference(mScreen); + + assertThat(mNfcController.mAirplaneModeObserver).isNotNull(); + } + + @Test + public void displayPreference_nfcNotAffectByAirplaneMode_shouldNotCreateAirplaneModeObserver() { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, ""); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS, ""); + + mNfcController.displayPreference(mScreen); + + assertThat(mNfcController.mAirplaneModeObserver).isNull(); + } } From 6641ba8feb10027b3fb703aa78d79d5126b76cda Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Wed, 13 Jun 2018 14:45:16 -0700 Subject: [PATCH 21/25] Update battery saver seekbar This CL adds ticks to seekbar to make it increase by 5% or multiple of it. Change-Id: I25a592d75a32adaddccb4b5a7c7f18a0f771d99a Fixes: 73763634 Test: RunSettingsRoboTests --- res/xml/battery_saver_settings.xml | 4 ++-- .../AutoBatterySeekBarPreferenceController.java | 7 ++++--- .../AutoBatterySeekBarPreferenceControllerTest.java | 9 ++++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml index 0460459d961..eb4954f8ca8 100644 --- a/res/xml/battery_saver_settings.xml +++ b/res/xml/battery_saver_settings.xml @@ -29,8 +29,8 @@ + android:max="15" + android:min="1" /> 0) { if (!(preference instanceof SeekBarPreference)) { Log.e(TAG, "Unexpected preference class: " + preference.getClass()); @@ -127,7 +128,7 @@ public class AutoBatterySeekBarPreferenceController extends BasePreferenceContro preference.setTitle(mContext.getString(R.string.battery_saver_seekbar_title, Utils.formatPercentage(level))); SeekBarPreference seekBarPreference = (SeekBarPreference) preference; - seekBarPreference.setProgress(level); + seekBarPreference.setProgress(level / INTERVAL); seekBarPreference.setSeekBarContentDescription( mContext.getString(R.string.battery_saver_turn_on_automatically_title)); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java index 9457f091dbe..e1cf196a9d9 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batterysaver; import static com.google.common.truth.Truth.assertThat; import androidx.lifecycle.LifecycleOwner; + import android.content.Context; import android.provider.Settings; @@ -39,6 +40,7 @@ public class AutoBatterySeekBarPreferenceControllerTest { private static final int TRIGGER_LEVEL = 20; private static final int DEFAULT_LEVEL = 15; + private static final int INTERVAL = 5; private AutoBatterySeekBarPreferenceController mController; private Context mContext; @@ -85,15 +87,16 @@ public class AutoBatterySeekBarPreferenceControllerTest { assertThat(mPreference.isVisible()).isTrue(); assertThat(mPreference.getTitle()).isEqualTo("At 20%"); - assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL); + assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL / INTERVAL); } + @Test public void testOnPreferenceChange_updateValue() { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); - mController.onPreferenceChange(mPreference, TRIGGER_LEVEL); + mController.onPreferenceChange(mPreference, TRIGGER_LEVEL / INTERVAL); assertThat(Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isEqualTo(TRIGGER_LEVEL); @@ -106,7 +109,7 @@ public class AutoBatterySeekBarPreferenceControllerTest { mController.updateState(mPreference); - assertThat(mPreference.getMax()).isEqualTo(50); + assertThat(mPreference.getMax()).isEqualTo(50 / INTERVAL); } @Test From 275da6a87863ce0251990d5277dfd7cb47b547f2 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 14 Jun 2018 10:25:58 -0700 Subject: [PATCH 22/25] Change display size preview for app icons. The new UI intentionally does not look like the app drawer from launcher so user is clear this is a preview screen. Change-Id: Iba87fab72e77921c1f1014fca24476bd4af2d19c Fixes: 70043399 Test: visual --- res/layout/screen_zoom_preview_2.xml | 3 ++ res/layout/screen_zoom_preview_app_icon.xml | 51 +++++++++---------- res/values/dimens.xml | 1 - .../android/settings/display/AppGridView.java | 23 ++++++--- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/res/layout/screen_zoom_preview_2.xml b/res/layout/screen_zoom_preview_2.xml index b61f92ae547..2cfd9b7172a 100644 --- a/res/layout/screen_zoom_preview_2.xml +++ b/res/layout/screen_zoom_preview_2.xml @@ -17,4 +17,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" + android:padding="32dp" + android:numColumns="3" + android:gravity="center" android:importantForAccessibility="noHideDescendants" /> diff --git a/res/layout/screen_zoom_preview_app_icon.xml b/res/layout/screen_zoom_preview_app_icon.xml index 7d7d983ff40..087afb832bc 100644 --- a/res/layout/screen_zoom_preview_app_icon.xml +++ b/res/layout/screen_zoom_preview_app_icon.xml @@ -14,33 +14,28 @@ limitations under the License. --> - + - + + - - - - - - \ No newline at end of file + android:ellipsize="end" + android:gravity="center_horizontal|top" + android:singleLine="true" + android:textAppearance="@android:style/TextAppearance.Material.Caption" /> + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 08160ba3154..30d1e6c36e5 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -251,7 +251,6 @@ 8dp - 88dp 10dp 32sp 16sp diff --git a/src/com/android/settings/display/AppGridView.java b/src/com/android/settings/display/AppGridView.java index 00275375280..d48eb09e22f 100644 --- a/src/com/android/settings/display/AppGridView.java +++ b/src/com/android/settings/display/AppGridView.java @@ -22,7 +22,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.UserHandle; -import androidx.annotation.VisibleForTesting; import android.util.AttributeSet; import android.util.IconDrawableFactory; import android.view.View; @@ -37,28 +36,32 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import androidx.annotation.VisibleForTesting; + public class AppGridView extends GridView { public AppGridView(Context context) { - this(context, null); + super(context); + init(context); } public AppGridView(Context context, AttributeSet attrs) { - this(context, attrs, 0); + super(context, attrs); + init(context); } public AppGridView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); + super(context, attrs, defStyleAttr); + init(context); } public AppGridView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleResId) { super(context, attrs, defStyleAttr, defStyleResId); - setNumColumns(AUTO_FIT); + init(context); - final int columnWidth = getResources().getDimensionPixelSize( - R.dimen.screen_zoom_preview_app_icon_width); - setColumnWidth(columnWidth); + } + private void init(Context context) { setAdapter(new AppsAdapter(context, R.layout.screen_zoom_preview_app_icon, android.R.id.text1, android.R.id.icon1)); } @@ -105,6 +108,7 @@ public class AppGridView extends GridView { } private void loadAllApps() { + final int needAppCount = 6; final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); @@ -117,6 +121,9 @@ public class AppGridView extends GridView { if (label != null) { results.add(new ActivityEntry(info, label.toString(), iconFactory)); } + if (results.size() >= needAppCount) { + break; + } } Collections.sort(results); From a83f90ab6d1119ac3bcc92273d19f54fa6b66854 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 14 Jun 2018 10:37:45 -0700 Subject: [PATCH 23/25] Turn on DataUsageSummaryActivity Test: rebuild Change-Id: I984c751b2be00b514740a3121ac2f73b277aa139 --- AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4fa39649718..eae5eaac7e7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2362,7 +2362,6 @@ android:name="Settings$DataUsageSummaryActivity" android:label="@string/data_usage_summary_title" android:icon="@drawable/ic_settings_data_usage" - android:enabled="false" android:taskAffinity="com.android.settings" android:parentActivityName="Settings"> From a177e51b184e6888ee6a52c0e2f0f34720978fbf Mon Sep 17 00:00:00 2001 From: Doris Ling Date: Thu, 14 Jun 2018 11:26:40 -0700 Subject: [PATCH 24/25] Fix incorrect default browser summary. - move the logic to retrieve browser candidates with unique package name from default browser picker to the controller, so that both uses the same logic to get the candidate list. When there is only 1 package that can handle the web but it has multiple resolved activities, it should be considered as the only app, and hence be set as default. Change-Id: I945bcef3f7de586b19f65ceada3abf7addb0b26e Fixes: 109882811 Test: make RunSettingsRoboTests --- .../defaultapps/DefaultBrowserPicker.java | 22 ++---- .../DefaultBrowserPreferenceController.java | 32 +++++++-- .../defaultapps/DefaultBrowserPickerTest.java | 46 ------------ ...efaultBrowserPreferenceControllerTest.java | 71 ++++++++++++++++++- 4 files changed, 100 insertions(+), 71 deletions(-) diff --git a/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java b/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java index c243970bad0..1cb7985c4c1 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java +++ b/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java @@ -17,11 +17,9 @@ package com.android.settings.applications.defaultapps; import android.content.Context; -import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.util.ArraySet; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; @@ -29,7 +27,6 @@ import com.android.settingslib.applications.DefaultAppInfo; import java.util.ArrayList; import java.util.List; -import java.util.Set; /** * Fragment for choosing default browser. @@ -61,24 +58,13 @@ public class DefaultBrowserPicker extends DefaultAppPickerFragment { final List candidates = new ArrayList<>(); final Context context = getContext(); // Resolve that intent and check that the handleAllWebDataURI boolean is set - final List list = mPm.queryIntentActivitiesAsUser( - DefaultBrowserPreferenceController.BROWSE_PROBE, PackageManager.MATCH_ALL, mUserId); + final List list = + DefaultBrowserPreferenceController.getCandidates(mPm, mUserId); - final int count = list.size(); - final Set addedPackages = new ArraySet<>(); - for (int i = 0; i < count; i++) { - ResolveInfo info = list.get(i); - if (info.activityInfo == null || !info.handleAllWebDataURI) { - continue; - } - final String packageName = info.activityInfo.packageName; - if (addedPackages.contains(packageName)) { - continue; - } + for (ResolveInfo info : list) { try { candidates.add(new DefaultAppInfo(context, mPm, - mPm.getApplicationInfoAsUser(packageName, 0, mUserId))); - addedPackages.add(packageName); + mPm.getApplicationInfoAsUser(info.activityInfo.packageName, 0, mUserId))); } catch (PackageManager.NameNotFoundException e) { // Skip unknown packages. } diff --git a/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceController.java index 5395be9919c..d4e86ff16fe 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceController.java +++ b/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceController.java @@ -26,12 +26,15 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import androidx.preference.Preference; import android.text.TextUtils; +import android.util.ArraySet; import android.util.IconDrawableFactory; import android.util.Log; import com.android.settingslib.applications.DefaultAppInfo; +import java.util.ArrayList; import java.util.List; +import java.util.Set; public class DefaultBrowserPreferenceController extends DefaultAppPreferenceController { @@ -48,7 +51,7 @@ public class DefaultBrowserPreferenceController extends DefaultAppPreferenceCont @Override public boolean isAvailable() { - final List candidates = getCandidates(); + final List candidates = getCandidates(mPackageManager, mUserId); return candidates != null && !candidates.isEmpty(); } @@ -103,14 +106,31 @@ public class DefaultBrowserPreferenceController extends DefaultAppPreferenceCont return getOnlyAppIcon(); } - private List getCandidates() { - return mPackageManager.queryIntentActivitiesAsUser(BROWSE_PROBE, PackageManager.MATCH_ALL, - mUserId); + static List getCandidates(PackageManager packageManager, int userId) { + final List candidates = new ArrayList<>(); + // Resolve that intent and check that the handleAllWebDataURI boolean is set + final List list = packageManager.queryIntentActivitiesAsUser( + BROWSE_PROBE, PackageManager.MATCH_ALL, userId); + if (list != null) { + final Set addedPackages = new ArraySet<>(); + for (ResolveInfo info : list) { + if (info.activityInfo == null || !info.handleAllWebDataURI) { + continue; + } + final String packageName = info.activityInfo.packageName; + if (addedPackages.contains(packageName)) { + continue; + } + candidates.add(info); + addedPackages.add(packageName); + } + } + return candidates; } private String getOnlyAppLabel() { // Resolve that intent and check that the handleAllWebDataURI boolean is set - final List list = getCandidates(); + final List list = getCandidates(mPackageManager, mUserId); if (list != null && list.size() == 1) { final ResolveInfo info = list.get(0); final String label = info.loadLabel(mPackageManager).toString(); @@ -123,7 +143,7 @@ public class DefaultBrowserPreferenceController extends DefaultAppPreferenceCont } private Drawable getOnlyAppIcon() { - final List list = getCandidates(); + final List list = getCandidates(mPackageManager, mUserId); if (list != null && list.size() == 1) { final ResolveInfo info = list.get(0); final ComponentInfo cn = info.getComponentInfo(); diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java index bdf249d1f5e..3aa83a8ca1e 100644 --- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java +++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java @@ -16,8 +16,6 @@ package com.android.settings.applications.defaultapps; -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; @@ -25,20 +23,11 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; import android.os.UserManager; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settingslib.applications.DefaultAppInfo; - -import java.util.ArrayList; -import java.util.List; import org.junit.Before; import org.junit.Test; @@ -85,39 +74,4 @@ public class DefaultBrowserPickerTest { mPicker.getDefaultKey(); verify(mPackageManager).getDefaultBrowserPackageNameAsUser(anyInt()); } - - @Test - public void getCandidates_shouldNotIncludeDuplicatePackageName() throws NameNotFoundException { - final List resolveInfos = new ArrayList<>(); - final String PACKAGE_ONE = "com.first.package"; - final String PACKAGE_TWO = "com.second.package"; - resolveInfos.add(createResolveInfo(PACKAGE_ONE)); - resolveInfos.add(createResolveInfo(PACKAGE_TWO)); - resolveInfos.add(createResolveInfo(PACKAGE_ONE)); - resolveInfos.add(createResolveInfo(PACKAGE_TWO)); - when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) - .thenReturn(resolveInfos); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_ONE), anyInt(), anyInt())) - .thenReturn(createApplicationInfo(PACKAGE_ONE)); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_TWO), anyInt(), anyInt())) - .thenReturn(createApplicationInfo(PACKAGE_TWO)); - - final List defaultBrowserInfo = mPicker.getCandidates(); - - assertThat(defaultBrowserInfo.size()).isEqualTo(2); - } - - private ResolveInfo createResolveInfo(String packageName) { - final ResolveInfo info = new ResolveInfo(); - info.handleAllWebDataURI = true; - info.activityInfo = new ActivityInfo(); - info.activityInfo.packageName = packageName; - return info; - } - - private ApplicationInfo createApplicationInfo(String packageName) { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = packageName; - return info; - } } diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceControllerTest.java index 6bf6ac4189e..d4c4115b6a4 100644 --- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPreferenceControllerTest.java @@ -17,15 +17,21 @@ package com.android.settings.applications.defaultapps; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.os.UserManager; import androidx.preference.Preference; @@ -33,6 +39,8 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import java.util.ArrayList; +import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,8 +81,11 @@ public class DefaultBrowserPreferenceControllerTest { @Test public void isAvailable_hasBrowser_shouldReturnTrue() { + final ResolveInfo info = new ResolveInfo(); + info.activityInfo = new ActivityInfo(); + info.handleAllWebDataURI = true; when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) - .thenReturn(Collections.singletonList(new ResolveInfo())); + .thenReturn(Collections.singletonList(info)); assertThat(mController.isAvailable()).isTrue(); } @@ -88,6 +99,28 @@ public class DefaultBrowserPreferenceControllerTest { verify(pref).setSummary(R.string.app_list_preference_none); } + @Test + public void getDefaultAppLabel_hasAppWithMultipleResolvedInfo_shouldReturnLabel() + throws NameNotFoundException { + DefaultBrowserPreferenceController spyController = spy(mController); + doReturn(null).when(spyController).getDefaultAppIcon(); + final List resolveInfos = new ArrayList<>(); + final CharSequence PACKAGE_NAME = "com.test.package"; + final ResolveInfo info1 = spy(createResolveInfo(PACKAGE_NAME.toString())); + when(info1.loadLabel(mPackageManager)).thenReturn(PACKAGE_NAME); + resolveInfos.add(info1); + resolveInfos.add(createResolveInfo(PACKAGE_NAME.toString())); + when(mPackageManager.getDefaultBrowserPackageNameAsUser(anyInt())).thenReturn(null); + when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) + .thenReturn(resolveInfos); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME.toString()), anyInt(), anyInt())) + .thenReturn(createApplicationInfo(PACKAGE_NAME.toString())); + final Preference pref = mock(Preference.class); + + assertThat(spyController.getDefaultAppLabel()).isEqualTo(PACKAGE_NAME); + } + @Test public void getDefaultApp_shouldGetDefaultBrowserPackage() { mController.getDefaultAppInfo(); @@ -103,4 +136,40 @@ public class DefaultBrowserPreferenceControllerTest { assertThat(mController.isBrowserDefault("pkg", 0)).isTrue(); } + + @Test + public void getCandidates_shouldNotIncludeDuplicatePackageName() throws NameNotFoundException { + final List resolveInfos = new ArrayList<>(); + final String PACKAGE_ONE = "com.first.package"; + final String PACKAGE_TWO = "com.second.package"; + resolveInfos.add(createResolveInfo(PACKAGE_ONE)); + resolveInfos.add(createResolveInfo(PACKAGE_TWO)); + resolveInfos.add(createResolveInfo(PACKAGE_ONE)); + resolveInfos.add(createResolveInfo(PACKAGE_TWO)); + when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) + .thenReturn(resolveInfos); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_ONE), anyInt(), anyInt())) + .thenReturn(createApplicationInfo(PACKAGE_ONE)); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_TWO), anyInt(), anyInt())) + .thenReturn(createApplicationInfo(PACKAGE_TWO)); + + final List defaultBrowserInfo = + mController.getCandidates(mPackageManager, 0 /* userId */); + + assertThat(defaultBrowserInfo.size()).isEqualTo(2); + } + + private ResolveInfo createResolveInfo(String packageName) { + final ResolveInfo info = new ResolveInfo(); + info.handleAllWebDataURI = true; + info.activityInfo = new ActivityInfo(); + info.activityInfo.packageName = packageName; + return info; + } + + private ApplicationInfo createApplicationInfo(String packageName) { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = packageName; + return info; + } } From ed3a2bd2c31488ebf56e630719bba3f37f4876bd Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 14 Jun 2018 11:14:02 -0700 Subject: [PATCH 25/25] Clean up: move speciall access stuff into single package Bug: 110207366 Test: robotests Change-Id: I17ad7a15d1e44bb7690d2b18ed6e2b6b17b46d8a --- res/xml/app_and_notification.xml | 2 +- res/xml/special_access.xml | 32 ++++-- .../applications/SpecialAccessSettings.java | 106 ------------------ .../DataSaverController.java | 11 +- .../DeviceAdministratorsController.java | 11 +- .../EnabledVrListenersController.java | 13 +-- .../HighPowerAppsController.java | 11 +- .../NotificationAccessController.java | 36 ++++++ .../PictureInPictureController.java | 36 ++++++ .../PremiumSmsController.java | 11 +- .../specialaccess/SpecialAccessSettings.java | 65 +++++++++++ .../specialaccess/ZenAccessController.java | 36 ++++++ .../SpecialAccessSettingsTest.java | 79 ------------- .../DataSaverControllerTest.java | 4 +- .../DeviceAdministratorsControllerTest.java | 4 +- .../EnabledVrListenersControllerTest.java | 23 ++-- .../HighPowerAppsControllerTest.java | 4 +- .../PremiumSmsControllerTest.java | 4 +- .../ZenAccessControllerTest.java | 56 +++++++++ 19 files changed, 295 insertions(+), 249 deletions(-) delete mode 100644 src/com/android/settings/applications/SpecialAccessSettings.java rename src/com/android/settings/applications/{ => specialaccess}/DataSaverController.java (80%) rename src/com/android/settings/applications/{ => specialaccess}/DeviceAdministratorsController.java (79%) rename src/com/android/settings/applications/{ => specialaccess}/EnabledVrListenersController.java (78%) rename src/com/android/settings/applications/{ => specialaccess}/HighPowerAppsController.java (79%) create mode 100644 src/com/android/settings/applications/specialaccess/NotificationAccessController.java create mode 100644 src/com/android/settings/applications/specialaccess/PictureInPictureController.java rename src/com/android/settings/applications/{ => specialaccess}/PremiumSmsController.java (80%) create mode 100644 src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java create mode 100644 src/com/android/settings/applications/specialaccess/ZenAccessController.java delete mode 100644 tests/robotests/src/com/android/settings/applications/SpecialAccessSettingsTest.java rename tests/robotests/src/com/android/settings/applications/{ => specialaccess}/DataSaverControllerTest.java (93%) rename tests/robotests/src/com/android/settings/applications/{ => specialaccess}/DeviceAdministratorsControllerTest.java (96%) rename tests/robotests/src/com/android/settings/applications/{ => specialaccess}/EnabledVrListenersControllerTest.java (66%) rename tests/robotests/src/com/android/settings/applications/{ => specialaccess}/HighPowerAppsControllerTest.java (93%) rename tests/robotests/src/com/android/settings/applications/{ => specialaccess}/PremiumSmsControllerTest.java (93%) create mode 100644 tests/robotests/src/com/android/settings/applications/specialaccess/ZenAccessControllerTest.java diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml index 53e575f6f9a..dd661e08872 100644 --- a/res/xml/app_and_notification.xml +++ b/res/xml/app_and_notification.xml @@ -69,7 +69,7 @@ diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml index d1eb623aebd..2c80e94065f 100644 --- a/res/xml/special_access.xml +++ b/res/xml/special_access.xml @@ -15,16 +15,17 @@ --> + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res-auto" + android:key="special_app_access_screen" + android:title="@string/special_access"> + settings:keywords="@string/keywords_ignore_optimizations" + settings:controller="com.android.settings.applications.specialaccess.HighPowerAppsController"> @@ -33,7 +34,8 @@ + android:fragment="com.android.settings.DeviceAdminSettings" + settings:controller="com.android.settings.applications.specialaccess.DeviceAdministratorsController" /> + android:fragment="com.android.settings.notification.ZenAccessSettings" + settings:controller="com.android.settings.applications.specialaccess.ZenAccessController" /> + android:fragment="com.android.settings.notification.NotificationAccessSettings" + settings:controller="com.android.settings.applications.specialaccess.NotificationAccessController" /> + settings:keywords="@string/picture_in_picture_keywords" + settings:controller="com.android.settings.applications.specialaccess.PictureInPictureController" /> + android:fragment="com.android.settings.applications.PremiumSmsAccess" + settings:controller="com.android.settings.applications.specialaccess.PremiumSmsController" /> + android:fragment="com.android.settings.datausage.UnrestrictedDataAccess" + settings:controller="com.android.settings.applications.specialaccess.DataSaverController" /> + settings:keywords="@string/keywords_vr_listener" + settings:controller="com.android.settings.applications.specialaccess.EnabledVrListenersController"> diff --git a/src/com/android/settings/applications/SpecialAccessSettings.java b/src/com/android/settings/applications/SpecialAccessSettings.java deleted file mode 100644 index 7679b1faf9b..00000000000 --- a/src/com/android/settings/applications/SpecialAccessSettings.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2016 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 android.app.ActivityManager; -import android.content.Context; -import android.os.Bundle; -import androidx.annotation.NonNull; -import android.provider.SearchIndexableResource; -import androidx.preference.Preference; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settings.R; -import com.android.settings.dashboard.DashboardFragment; -import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settings.search.Indexable; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.search.SearchIndexable; - -import java.util.ArrayList; -import java.util.List; - -@SearchIndexable -public class SpecialAccessSettings extends DashboardFragment { - - private static final String TAG = "SpecialAccessSettings"; - private static final String[] DISABLED_FEATURES_LOW_RAM = - new String[]{"notification_access", "zen_access", "enabled_vr_listeners", - "picture_in_picture"}; - - @Override - protected String getLogTag() { - return TAG; - } - - @Override - protected int getPreferenceScreenResId() { - return R.xml.special_access; - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - if (ActivityManager.isLowRamDeviceStatic()) { - for (String disabledFeature : DISABLED_FEATURES_LOW_RAM) { - Preference pref = findPreference(disabledFeature); - if (pref != null) { - removePreference(disabledFeature); - } - } - } - } - - @Override - protected List createPreferenceControllers(Context context) { - return buildPreferenceControllers(context); - } - - private static List buildPreferenceControllers( - @NonNull Context context) { - final List controllers = new ArrayList<>(); - controllers.add(new HighPowerAppsController(context)); - controllers.add(new DeviceAdministratorsController(context)); - controllers.add(new PremiumSmsController(context)); - controllers.add(new DataSaverController(context)); - controllers.add(new EnabledVrListenersController(context)); - return controllers; - } - - @Override - public int getMetricsCategory() { - return MetricsEvent.SPECIAL_ACCESS; - } - - public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider() { - @Override - public List getXmlResourcesToIndex(Context context, - boolean enabled) { - final ArrayList result = new ArrayList<>(); - - final SearchIndexableResource sir = new SearchIndexableResource(context); - sir.xmlResId = R.xml.special_access; - result.add(sir); - return result; - } - - @Override - public List createPreferenceControllers( - Context context) { - return buildPreferenceControllers(context); - } - }; -} diff --git a/src/com/android/settings/applications/DataSaverController.java b/src/com/android/settings/applications/specialaccess/DataSaverController.java similarity index 80% rename from src/com/android/settings/applications/DataSaverController.java rename to src/com/android/settings/applications/specialaccess/DataSaverController.java index afe7cd64cd3..56687d7aa4f 100644 --- a/src/com/android/settings/applications/DataSaverController.java +++ b/src/com/android/settings/applications/specialaccess/DataSaverController.java @@ -15,20 +15,17 @@ */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import android.content.Context; -import androidx.annotation.VisibleForTesting; -import com.android.settings.core.BasePreferenceController; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; public class DataSaverController extends BasePreferenceController { - @VisibleForTesting static final String KEY_DATA_SAVER = "data_saver"; - - public DataSaverController(Context context) { - super(context, KEY_DATA_SAVER); + public DataSaverController(Context context, String key) { + super(context, key); } @AvailabilityStatus diff --git a/src/com/android/settings/applications/DeviceAdministratorsController.java b/src/com/android/settings/applications/specialaccess/DeviceAdministratorsController.java similarity index 79% rename from src/com/android/settings/applications/DeviceAdministratorsController.java rename to src/com/android/settings/applications/specialaccess/DeviceAdministratorsController.java index ec1d556a611..bdb99ef113a 100644 --- a/src/com/android/settings/applications/DeviceAdministratorsController.java +++ b/src/com/android/settings/applications/specialaccess/DeviceAdministratorsController.java @@ -14,20 +14,17 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import android.content.Context; -import androidx.annotation.VisibleForTesting; -import com.android.settings.core.BasePreferenceController; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; public class DeviceAdministratorsController extends BasePreferenceController { - @VisibleForTesting static final String KEY_DEVICE_ADMIN = "device_administrators"; - - public DeviceAdministratorsController(Context context) { - super(context, KEY_DEVICE_ADMIN); + public DeviceAdministratorsController(Context context, String key) { + super(context, key); } @AvailabilityStatus diff --git a/src/com/android/settings/applications/EnabledVrListenersController.java b/src/com/android/settings/applications/specialaccess/EnabledVrListenersController.java similarity index 78% rename from src/com/android/settings/applications/EnabledVrListenersController.java rename to src/com/android/settings/applications/specialaccess/EnabledVrListenersController.java index 7b33529d7eb..5967b0d4507 100644 --- a/src/com/android/settings/applications/EnabledVrListenersController.java +++ b/src/com/android/settings/applications/specialaccess/EnabledVrListenersController.java @@ -14,25 +14,24 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; +import android.app.ActivityManager; import android.content.Context; -import androidx.annotation.VisibleForTesting; -import com.android.settings.core.BasePreferenceController; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; public class EnabledVrListenersController extends BasePreferenceController { - @VisibleForTesting static final String KEY_ENABLED_VR_LISTENERS = "enabled_vr_listeners"; - - public EnabledVrListenersController(Context context) { - super(context, KEY_ENABLED_VR_LISTENERS); + public EnabledVrListenersController(Context context, String key) { + super(context, key); } @AvailabilityStatus public int getAvailabilityStatus() { return mContext.getResources().getBoolean(R.bool.config_show_enabled_vr_listeners) + && !ActivityManager.isLowRamDeviceStatic() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } diff --git a/src/com/android/settings/applications/HighPowerAppsController.java b/src/com/android/settings/applications/specialaccess/HighPowerAppsController.java similarity index 79% rename from src/com/android/settings/applications/HighPowerAppsController.java rename to src/com/android/settings/applications/specialaccess/HighPowerAppsController.java index 39b84516315..b893b887607 100644 --- a/src/com/android/settings/applications/HighPowerAppsController.java +++ b/src/com/android/settings/applications/specialaccess/HighPowerAppsController.java @@ -14,20 +14,17 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import android.content.Context; -import androidx.annotation.VisibleForTesting; -import com.android.settings.core.BasePreferenceController; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; public class HighPowerAppsController extends BasePreferenceController { - @VisibleForTesting static final String KEY_HIGH_POWER_APPS = "high_power_apps"; - - public HighPowerAppsController(Context context) { - super(context, KEY_HIGH_POWER_APPS); + public HighPowerAppsController(Context context, String key) { + super(context, key); } @AvailabilityStatus diff --git a/src/com/android/settings/applications/specialaccess/NotificationAccessController.java b/src/com/android/settings/applications/specialaccess/NotificationAccessController.java new file mode 100644 index 00000000000..773cd7df576 --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/NotificationAccessController.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 android.app.ActivityManager; +import android.content.Context; + +import com.android.settings.core.BasePreferenceController; + +public class NotificationAccessController extends BasePreferenceController { + + public NotificationAccessController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return !ActivityManager.isLowRamDeviceStatic() + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; + } +} diff --git a/src/com/android/settings/applications/specialaccess/PictureInPictureController.java b/src/com/android/settings/applications/specialaccess/PictureInPictureController.java new file mode 100644 index 00000000000..6666605c1cc --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/PictureInPictureController.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 android.app.ActivityManager; +import android.content.Context; + +import com.android.settings.core.BasePreferenceController; + +public class PictureInPictureController extends BasePreferenceController { + + public PictureInPictureController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return !ActivityManager.isLowRamDeviceStatic() + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; + } +} diff --git a/src/com/android/settings/applications/PremiumSmsController.java b/src/com/android/settings/applications/specialaccess/PremiumSmsController.java similarity index 80% rename from src/com/android/settings/applications/PremiumSmsController.java rename to src/com/android/settings/applications/specialaccess/PremiumSmsController.java index eeb5d86fd90..0e8c198f3b2 100644 --- a/src/com/android/settings/applications/PremiumSmsController.java +++ b/src/com/android/settings/applications/specialaccess/PremiumSmsController.java @@ -14,20 +14,17 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import android.content.Context; -import androidx.annotation.VisibleForTesting; -import com.android.settings.core.BasePreferenceController; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; public class PremiumSmsController extends BasePreferenceController { - @VisibleForTesting static final String KEY_PREMIUM_SMS = "premium_sms"; - - public PremiumSmsController(Context context) { - super(context, KEY_PREMIUM_SMS); + public PremiumSmsController(Context context, String key) { + super(context, key); } @AvailabilityStatus diff --git a/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java new file mode 100644 index 00000000000..80cadcc4396 --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/SpecialAccessSettings.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 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 android.content.Context; +import android.provider.SearchIndexableResource; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.List; + +@SearchIndexable +public class SpecialAccessSettings extends DashboardFragment { + + private static final String TAG = "SpecialAccessSettings"; + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.special_access; + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.SPECIAL_ACCESS; + } + + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex(Context context, + boolean enabled) { + final ArrayList result = new ArrayList<>(); + + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.special_access; + result.add(sir); + return result; + } + }; +} diff --git a/src/com/android/settings/applications/specialaccess/ZenAccessController.java b/src/com/android/settings/applications/specialaccess/ZenAccessController.java new file mode 100644 index 00000000000..41344a3ebad --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/ZenAccessController.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 android.app.ActivityManager; +import android.content.Context; + +import com.android.settings.core.BasePreferenceController; + +public class ZenAccessController extends BasePreferenceController { + + public ZenAccessController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return !ActivityManager.isLowRamDeviceStatic() + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; + } +} diff --git a/tests/robotests/src/com/android/settings/applications/SpecialAccessSettingsTest.java b/tests/robotests/src/com/android/settings/applications/SpecialAccessSettingsTest.java deleted file mode 100644 index fd71af7ac1b..00000000000 --- a/tests/robotests/src/com/android/settings/applications/SpecialAccessSettingsTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; - -import android.content.Context; -import android.provider.SearchIndexableResource; - -import com.android.settings.R; -import com.android.settings.testutils.SettingsRobolectricTestRunner; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -import java.util.List; - -@RunWith(SettingsRobolectricTestRunner.class) -public class SpecialAccessSettingsTest { - - private Context mContext; - private SpecialAccessSettings mFragment; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application); - mFragment = new SpecialAccessSettings() { - @Override - public Context getContext() { - return mContext; - } - }; - } - - @Test - public void testSearchIndexProvider_shouldIndexResource() { - final List indexRes = - SpecialAccessSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(mContext, - true /* enabled */); - final List niks = - SpecialAccessSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); - - assertThat(indexRes).isNotNull(); - assertThat(indexRes.get(0).xmlResId).isEqualTo(R.xml.special_access); - assertThat(niks).isEmpty(); - } - - @Test - @Config(qualifiers = "mcc999") - public void testSearchIndexProvider_ifElementsAreNotShown_shouldNotBeIndexed() { - final List niks = - SpecialAccessSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); - - assertThat(niks).contains(HighPowerAppsController.KEY_HIGH_POWER_APPS); - assertThat(niks).contains(DeviceAdministratorsController.KEY_DEVICE_ADMIN); - assertThat(niks).contains(PremiumSmsController.KEY_PREMIUM_SMS); - assertThat(niks).contains(DataSaverController.KEY_DATA_SAVER); - assertThat(niks).contains(EnabledVrListenersController.KEY_ENABLED_VR_LISTENERS); - } -} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/applications/DataSaverControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java similarity index 93% rename from tests/robotests/src/com/android/settings/applications/DataSaverControllerTest.java rename to tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java index 22140bb5c4d..c632d08bec0 100644 --- a/tests/robotests/src/com/android/settings/applications/DataSaverControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; @@ -40,7 +40,7 @@ public class DataSaverControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application.getApplicationContext()); - mController = new DataSaverController(mContext); + mController = new DataSaverController(mContext, "key"); } @Test diff --git a/tests/robotests/src/com/android/settings/applications/DeviceAdministratorsControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/DeviceAdministratorsControllerTest.java similarity index 96% rename from tests/robotests/src/com/android/settings/applications/DeviceAdministratorsControllerTest.java rename to tests/robotests/src/com/android/settings/applications/specialaccess/DeviceAdministratorsControllerTest.java index ad7e615ce78..efa784683f3 100644 --- a/tests/robotests/src/com/android/settings/applications/DeviceAdministratorsControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/DeviceAdministratorsControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; @@ -40,7 +40,7 @@ public class DeviceAdministratorsControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application.getApplicationContext()); - mController = new DeviceAdministratorsController(mContext); + mController = new DeviceAdministratorsController(mContext, "key"); } @Test diff --git a/tests/robotests/src/com/android/settings/applications/EnabledVrListenersControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/EnabledVrListenersControllerTest.java similarity index 66% rename from tests/robotests/src/com/android/settings/applications/EnabledVrListenersControllerTest.java rename to tests/robotests/src/com/android/settings/applications/specialaccess/EnabledVrListenersControllerTest.java index b36472031f7..7963fd4a20a 100644 --- a/tests/robotests/src/com/android/settings/applications/EnabledVrListenersControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/EnabledVrListenersControllerTest.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; import android.content.Context; @@ -26,31 +25,39 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivityManager; @RunWith(SettingsRobolectricTestRunner.class) public class EnabledVrListenersControllerTest { private Context mContext; private EnabledVrListenersController mController; + private ShadowActivityManager mActivityManager; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application.getApplicationContext()); - mController = new EnabledVrListenersController(mContext); + mContext = RuntimeEnvironment.application; + mController = new EnabledVrListenersController(mContext, "key"); + mActivityManager = Shadow.extract(mContext.getSystemService(Context.ACTIVITY_SERVICE)); } @Test - public void testEnabledVrListeners_byDefault_shouldBeShown() { + public void isAvailable_byDefault_true() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_lowMemory_false() { + mActivityManager.setIsLowRamDevice(true); assertThat(mController.isAvailable()).isTrue(); } @Test @Config(qualifiers = "mcc999") - public void testEnabledVrListeners_ifDisabled_shouldNotBeShown() { + public void isAvailable_disabled_false() { assertThat(mController.isAvailable()).isFalse(); } } diff --git a/tests/robotests/src/com/android/settings/applications/HighPowerAppsControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/HighPowerAppsControllerTest.java similarity index 93% rename from tests/robotests/src/com/android/settings/applications/HighPowerAppsControllerTest.java rename to tests/robotests/src/com/android/settings/applications/specialaccess/HighPowerAppsControllerTest.java index 6dfaa7c59eb..4713be22512 100644 --- a/tests/robotests/src/com/android/settings/applications/HighPowerAppsControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/HighPowerAppsControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; @@ -40,7 +40,7 @@ public class HighPowerAppsControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application.getApplicationContext()); - mController = new HighPowerAppsController(mContext); + mController = new HighPowerAppsController(mContext, "key"); } @Test diff --git a/tests/robotests/src/com/android/settings/applications/PremiumSmsControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/PremiumSmsControllerTest.java similarity index 93% rename from tests/robotests/src/com/android/settings/applications/PremiumSmsControllerTest.java rename to tests/robotests/src/com/android/settings/applications/specialaccess/PremiumSmsControllerTest.java index cad4100adea..8f16da7022e 100644 --- a/tests/robotests/src/com/android/settings/applications/PremiumSmsControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/PremiumSmsControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.specialaccess; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; @@ -40,7 +40,7 @@ public class PremiumSmsControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application.getApplicationContext()); - mController = new PremiumSmsController(mContext); + mController = new PremiumSmsController(mContext, "key"); } @Test diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/ZenAccessControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/ZenAccessControllerTest.java new file mode 100644 index 00000000000..205de10f1c8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/ZenAccessControllerTest.java @@ -0,0 +1,56 @@ +/* + * 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.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivityManager; + +@RunWith(SettingsRobolectricTestRunner.class) +public class ZenAccessControllerTest { + + private Context mContext; + private ZenAccessController mController; + private ShadowActivityManager mActivityManager; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mController = new ZenAccessController(mContext, "key"); + mActivityManager = Shadow.extract(mContext.getSystemService(Context.ACTIVITY_SERVICE)); + } + + @Test + public void isAvailable_byDefault_true() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_lowMemory_false() { + mActivityManager.setIsLowRamDevice(true); + assertThat(mController.isAvailable()).isTrue(); + } +}