From 3dba0abc614fe201c12a87238d0c24277d27b835 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 6 Sep 2018 12:21:30 -0700 Subject: [PATCH 01/11] Move safety & regulatory manual closer to legal settings Bug: 113788849 Test: visual Change-Id: Ie9a706bad9a4c65ff06c2350171ad55f46dd8386 Merged-In: Ie9a706bad9a4c65ff06c2350171ad55f46dd8386 (cherry picked from commit f5ccd335672ce751cfa5e84fee8c158c0d0ca833) --- res/xml/my_device_info.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml index 3dac8b879f2..3d499ab4600 100644 --- a/res/xml/my_device_info.xml +++ b/res/xml/my_device_info.xml @@ -71,10 +71,18 @@ + + + + + - - - - - - \ No newline at end of file + From 0f52a07037e3795b9173af1397086afa3c2e1913 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Tue, 19 Jun 2018 16:52:07 -0700 Subject: [PATCH 02/11] Update PBAP info for all pairing dialogs. DO NOT MERGE This is a bug from ag/4210612, in which it only update PBAP info for USER_ENTRY_DIALOG. So in other kind of dialogs it never upload correct PBAP info to bluetooth backend. This CL fix it by updating PBAP for all dialogs. Change-Id: Ia39eee1acaece555e8e5a305ec2c803294d7efbd Bug: 109842273 Bug: 72872376 Test: RunSettingsRoboTests (cherry picked from commit 7015e20a554fd7f8f7ded7359a5ecc13a82a0464) (cherry picked from commit 77b62b24c6d386f7913bfe16e1ab121404c3b50a) --- .../bluetooth/BluetoothPairingController.java | 22 ++++--- .../BluetoothPairingControllerTest.java | 66 +++++++++++++++++++ 2 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java diff --git a/src/com/android/settings/bluetooth/BluetoothPairingController.java b/src/com/android/settings/bluetooth/BluetoothPairingController.java index 5120cc0ff9b..4b9ffdad4eb 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingController.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingController.java @@ -23,12 +23,16 @@ import android.text.Editable; import android.util.Log; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; + import com.android.settings.R; import com.android.settings.bluetooth.BluetoothPairingDialogFragment.BluetoothPairingDialogListener; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfile; + import java.util.Locale; +import android.support.annotation.VisibleForTesting; + /** * A controller used by {@link BluetoothPairingDialog} to manage connection state while we try to * pair with a bluetooth device. It includes methods that allow the @@ -50,8 +54,10 @@ public class BluetoothPairingController implements OnCheckedChangeListener, // Bluetooth dependencies for the connection we are trying to establish private LocalBluetoothManager mBluetoothManager; - private BluetoothDevice mDevice; - private int mType; + @VisibleForTesting + BluetoothDevice mDevice; + @VisibleForTesting + int mType; private String mUserInput; private String mPasskeyFormatted; private int mPasskey; @@ -82,7 +88,6 @@ public class BluetoothPairingController implements OnCheckedChangeListener, mDeviceName = mBluetoothManager.getCachedDeviceManager().getName(mDevice); mPbapClientProfile = mBluetoothManager.getProfileManager().getPbapClientProfile(); mPasskeyFormatted = formatKey(mPasskey); - } @Override @@ -96,12 +101,13 @@ public class BluetoothPairingController implements OnCheckedChangeListener, @Override public void onDialogPositiveClick(BluetoothPairingDialogFragment dialog) { + if (mPbapAllowed) { + mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); + } else { + mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); + } + if (getDialogType() == USER_ENTRY_DIALOG) { - if (mPbapAllowed) { - mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); - } else { - mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); - } onPair(mUserInput); } else { onPair(null); diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java new file mode 100644 index 00000000000..b28a8b2b825 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java @@ -0,0 +1,66 @@ +/* + * 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.bluetooth; + +import static android.bluetooth.BluetoothDevice.PAIRING_VARIANT_CONSENT; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.content.Intent; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowBluetoothPan; + +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; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothPan.class, ShadowBluetoothAdapter.class}) +public class BluetoothPairingControllerTest { + @Mock + private BluetoothDevice mBluetoothDevice; + private Context mContext; + private BluetoothPairingController mBluetoothPairingController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + final Intent intent = new Intent(); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mBluetoothPairingController = spy(new BluetoothPairingController(intent, mContext)); + } + + @Test + public void onDialogPositiveClick_confirmationDialog_setPBAP() { + mBluetoothPairingController.mType = PAIRING_VARIANT_CONSENT; + mBluetoothPairingController.onCheckedChanged(null, true); + + mBluetoothPairingController.onDialogPositiveClick(null); + + verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); + } +} From 38a574173e1652447eefacfa0c03ff2d797aaaea Mon Sep 17 00:00:00 2001 From: cosmohsieh Date: Wed, 28 Nov 2018 15:00:43 +0800 Subject: [PATCH 03/11] Rename wifi/NetworkRequestTimeoutDialogFragment to wifi/NetworkRequestErrorDialogFragment for extending usage. ErrorDialogFragment contains two kinds of error dialog (timeout and onAbort). Bug: 117399926 Test: RunSettingsRoboTests Change-Id: Iea82a53baba298de66ae988c75d9fb6daca60160 --- res/values/strings.xml | 4 +- .../wifi/NetworkRequestDialogFragment.java | 22 +- .../NetworkRequestErrorDialogFragment.java | 86 +++++ .../NetworkRequestTimeoutDialogFragment.java | 66 ---- .../NetworkRequestDialogFragmentTest.java | 332 +++++++++--------- ...NetworkRequestErrorDialogFragmentTest.java | 117 ++++++ ...tworkRequestTimeoutDialogFragmentTest.java | 90 ----- 7 files changed, 392 insertions(+), 325 deletions(-) create mode 100644 src/com/android/settings/wifi/NetworkRequestErrorDialogFragment.java delete mode 100644 src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragment.java create mode 100644 tests/robotests/src/com/android/settings/wifi/NetworkRequestErrorDialogFragmentTest.java delete mode 100644 tests/robotests/src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragmentTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 85f133f2a22..fcaf72a097e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10235,7 +10235,9 @@ No devices found. Make sure the device is turned on and available to connect. - Scan again + Try again + + Something came up. The application has cancelled the request to choose a device. diff --git a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java index a1d862064c2..efb3f8cd3a5 100644 --- a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java +++ b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java @@ -44,12 +44,18 @@ import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE; import com.android.settingslib.Utils; import com.android.settingslib.wifi.AccessPoint; import java.util.ArrayList; import java.util.List; +/** + * The Fragment sets up callback {@link NetworkRequestMatchCallback} with framework. To handle most + * behaviors of the callback when requesting wifi network, except for error message. When error + * happens, {@link NetworkRequestErrorDialogFragment} will be called to display error message. + */ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener, NetworkRequestMatchCallback { @@ -131,7 +137,7 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp @Override public void onCancel(@NonNull DialogInterface dialog) { super.onCancel(dialog); - // Finishes activity when user clicks back key or outside of dialog. + // Finishes the activity when user clicks back key or outside of the dialog. getActivity().finish(); } @@ -166,7 +172,7 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp switch (msg.what) { case MESSAGE_STOP_SCAN_WIFI_LIST: removeMessages(MESSAGE_STOP_SCAN_WIFI_LIST); - stopScanningAndPopTimeoutDialog(); + stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE.TIME_OUT); break; default: // Do nothing. @@ -175,14 +181,18 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp } }; - protected void stopScanningAndPopTimeoutDialog() { + protected void stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE type) { // Dismisses current dialog. dismiss(); // Throws new timeout dialog. - final NetworkRequestTimeoutDialogFragment fragment = NetworkRequestTimeoutDialogFragment + final NetworkRequestErrorDialogFragment fragment = NetworkRequestErrorDialogFragment .newInstance(); - fragment.show(getActivity().getSupportFragmentManager(), null); + final Bundle bundle = new Bundle(); + bundle.putSerializable(NetworkRequestErrorDialogFragment.DIALOG_TYPE, type); + fragment.setArguments(bundle); + fragment.show(getActivity().getSupportFragmentManager(), + NetworkRequestDialogFragment.class.getSimpleName()); } @Override @@ -239,7 +249,7 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp @Override public void onAbort() { - // TODO(b/117399926): We should have a UI notify user here. + stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE.ABORT); } @Override diff --git a/src/com/android/settings/wifi/NetworkRequestErrorDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestErrorDialogFragment.java new file mode 100644 index 00000000000..762b467b4c5 --- /dev/null +++ b/src/com/android/settings/wifi/NetworkRequestErrorDialogFragment.java @@ -0,0 +1,86 @@ +/* + * 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.wifi; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; + +/** + * The dialog shows an error message when requesting network {@link NetworkRequestDialogFragment}. + * Contains multi-error types in {@code ERROR_DIALOG_TYPE}. + */ +public class NetworkRequestErrorDialogFragment extends InstrumentedDialogFragment { + + public static final String DIALOG_TYPE = "DIALOG_ERROR_TYPE"; + + public enum ERROR_DIALOG_TYPE {TIME_OUT, ABORT} + + public static NetworkRequestErrorDialogFragment newInstance() { + return new NetworkRequestErrorDialogFragment(); + } + + private NetworkRequestErrorDialogFragment() { + super(); + } + + @Override + public void onCancel(@NonNull DialogInterface dialog) { + super.onCancel(dialog); + // Wants to finish the activity when user clicks back key or outside of the dialog. + getActivity().finish(); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Gets error type to construct dialog. Default is TIME_OUT dialog. + ERROR_DIALOG_TYPE msgType = ERROR_DIALOG_TYPE.TIME_OUT; + if (getArguments() != null) { + msgType = (ERROR_DIALOG_TYPE) getArguments().getSerializable(DIALOG_TYPE); + } + + final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + if (msgType == ERROR_DIALOG_TYPE.TIME_OUT) { + builder.setMessage(R.string.network_connection_timeout_dialog_message) + .setPositiveButton(R.string.network_connection_timeout_dialog_ok, + (dialog, which) -> startScanningDialog()) + .setNegativeButton(R.string.cancel, (dialog, which) -> getActivity().finish()); + } else { + builder.setMessage(R.string.network_connection_errorstate_dialog_message) + .setPositiveButton(R.string.okay, (dialog, which) -> getActivity().finish()); + } + return builder.create(); + } + + @Override + public int getMetricsCategory() { + return MetricsProto.MetricsEvent.WIFI_SCANNING_NEEDED_DIALOG; + } + + protected void startScanningDialog() { + final NetworkRequestDialogFragment fragment = NetworkRequestDialogFragment.newInstance(); + fragment.show(getActivity().getSupportFragmentManager(), + NetworkRequestErrorDialogFragment.class.getSimpleName()); + } +} diff --git a/src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragment.java deleted file mode 100644 index 08f285b0b8c..00000000000 --- a/src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragment.java +++ /dev/null @@ -1,66 +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.wifi; - -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import androidx.appcompat.app.AlertDialog; -import com.android.internal.logging.nano.MetricsProto; -import com.android.settings.R; -import com.android.settings.core.instrumentation.InstrumentedDialogFragment; - -public class NetworkRequestTimeoutDialogFragment extends InstrumentedDialogFragment implements - DialogInterface.OnClickListener { - - public static NetworkRequestTimeoutDialogFragment newInstance() { - NetworkRequestTimeoutDialogFragment fragment = new NetworkRequestTimeoutDialogFragment(); - return fragment; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) - .setMessage(R.string.network_connection_timeout_dialog_message) - .setPositiveButton(R.string.network_connection_timeout_dialog_ok, this) - .setNegativeButton(R.string.cancel, null); - return builder.create(); - } - - @Override - public int getMetricsCategory() { - return MetricsProto.MetricsEvent.WIFI_SCANNING_NEEDED_DIALOG; - } - - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - startScanningDialog(); - break; - case DialogInterface.BUTTON_NEGATIVE: - default: - // Do nothing. - break; - } - } - - protected void startScanningDialog() { - NetworkRequestDialogFragment fragment = NetworkRequestDialogFragment.newInstance(); - fragment.show(getActivity().getSupportFragmentManager(), null); - } -} diff --git a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java index 2e806ddfed0..8cf02b82964 100644 --- a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java @@ -17,6 +17,7 @@ package com.android.settings.wifi; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -32,213 +33,220 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; import android.os.Bundle; import android.widget.Button; + import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; + import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE; import com.android.settingslib.wifi.AccessPoint; + import java.util.ArrayList; import java.util.List; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; + import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; + import org.robolectric.shadows.ShadowLooper; @RunWith(SettingsRobolectricTestRunner.class) @Config(shadows = {SettingsShadowResourcesImpl.class, ShadowAlertDialogCompat.class}) public class NetworkRequestDialogFragmentTest { - final String KEY_SSID = "key_ssid"; + final String KEY_SSID = "key_ssid"; - private FragmentActivity mActivity; - private NetworkRequestDialogFragment networkRequestDialogFragment; - private Context mContext; + private FragmentActivity mActivity; + private NetworkRequestDialogFragment networkRequestDialogFragment; + private Context mContext; - @Before - public void setUp() { - mActivity = Robolectric.setupActivity(FragmentActivity.class); - networkRequestDialogFragment = spy(NetworkRequestDialogFragment.newInstance()); - mContext = spy(RuntimeEnvironment.application); - } - - @Test - public void display_shouldShowTheDialog() { - networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); - AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - assertThat(alertDialog).isNotNull(); - assertThat(alertDialog.isShowing()).isTrue(); - } - - @Test - public void clickPositiveButton_shouldCloseTheDialog() { - networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); - AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - assertThat(alertDialog.isShowing()).isTrue(); - - Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - assertThat(positiveButton).isNotNull(); - - positiveButton.performClick(); - assertThat(alertDialog.isShowing()).isFalse(); - } - - @Test - public void onResumeAndWaitTimeout_shouldCallTimeoutDialog() { - FakeNetworkRequestDialogFragment fakeFragment = new FakeNetworkRequestDialogFragment(); - FakeNetworkRequestDialogFragment spyFakeFragment = spy(fakeFragment); - spyFakeFragment.show(mActivity.getSupportFragmentManager(), null); - - assertThat(fakeFragment.bCalledStopAndPop).isFalse(); - - ShadowLooper.getShadowMainLooper().runToEndOfTasks(); - - assertThat(fakeFragment.bCalledStopAndPop).isTrue(); - } - - class FakeNetworkRequestDialogFragment extends NetworkRequestDialogFragment { - boolean bCalledStopAndPop = false; - - @Override - public void stopScanningAndPopTimeoutDialog() { - bCalledStopAndPop = true; + @Before + public void setUp() { + mActivity = Robolectric.setupActivity(FragmentActivity.class); + networkRequestDialogFragment = spy(NetworkRequestDialogFragment.newInstance()); + mContext = spy(RuntimeEnvironment.application); } - } - @Test - public void onResume_shouldRegisterCallback() { - when(networkRequestDialogFragment.getContext()).thenReturn(mContext); - Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); - when(mContext.getApplicationContext()).thenReturn(applicationContext); - WifiManager wifiManager = mock(WifiManager.class); - when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); + @Test + public void display_shouldShowTheDialog() { + networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); + AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(alertDialog).isNotNull(); + assertThat(alertDialog.isShowing()).isTrue(); + } - networkRequestDialogFragment.onResume(); + @Test + public void clickPositiveButton_shouldCloseTheDialog() { + networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); + AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(alertDialog.isShowing()).isTrue(); - verify(wifiManager).registerNetworkRequestMatchCallback(any(), any()); - } + Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + assertThat(positiveButton).isNotNull(); - @Test - public void onPause_shouldUnRegisterCallback() { - when(networkRequestDialogFragment.getContext()).thenReturn(mContext); - Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); - when(mContext.getApplicationContext()).thenReturn(applicationContext); - WifiManager wifiManager = mock(WifiManager.class); - when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); + positiveButton.performClick(); + assertThat(alertDialog.isShowing()).isFalse(); + } - networkRequestDialogFragment.onPause(); + @Test + public void onResumeAndWaitTimeout_shouldCallTimeoutDialog() { + FakeNetworkRequestDialogFragment fakeFragment = new FakeNetworkRequestDialogFragment(); + FakeNetworkRequestDialogFragment spyFakeFragment = spy(fakeFragment); + spyFakeFragment.show(mActivity.getSupportFragmentManager(), null); - verify(wifiManager).unregisterNetworkRequestMatchCallback(networkRequestDialogFragment); - } + assertThat(fakeFragment.bCalledStopAndPop).isFalse(); - @Test - public void updateAccessPointList_onUserSelectionConnectSuccess_updateCorrectly() { - List accessPointList = spy(new ArrayList<>()); - Bundle bundle = new Bundle(); - bundle.putString(KEY_SSID, "Test AP 1"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 2"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 3"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 4"); - accessPointList.add(new AccessPoint(mContext, bundle)); + ShadowLooper.getShadowMainLooper().runToEndOfTasks(); - when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); - networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); + assertThat(fakeFragment.bCalledStopAndPop).isTrue(); + } - // Test if config would update list. - WifiConfiguration config = new WifiConfiguration(); - config.SSID = "Test AP 3"; - networkRequestDialogFragment.onUserSelectionConnectSuccess(config); + class FakeNetworkRequestDialogFragment extends NetworkRequestDialogFragment { + boolean bCalledStopAndPop = false; - AccessPoint verifyAccessPoint = new AccessPoint(mContext, config); - verify(accessPointList, times(1)).set(2, verifyAccessPoint); - } + @Override + public void stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE type) { + bCalledStopAndPop = true; + } + } - @Test - public void updateAccessPointList_onUserSelectionConnectFailure_updateCorrectly() { - List accessPointList = spy(new ArrayList<>()); - Bundle bundle = new Bundle(); - bundle.putString(KEY_SSID, "Test AP 1"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 2"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 3"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 4"); - accessPointList.add(new AccessPoint(mContext, bundle)); + @Test + public void onResume_shouldRegisterCallback() { + when(networkRequestDialogFragment.getContext()).thenReturn(mContext); + Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); + when(mContext.getApplicationContext()).thenReturn(applicationContext); + WifiManager wifiManager = mock(WifiManager.class); + when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); - when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); - networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); + networkRequestDialogFragment.onResume(); - // Test if config would update list. - WifiConfiguration config = new WifiConfiguration(); - config.SSID = "Test AP 3"; - networkRequestDialogFragment.onUserSelectionConnectFailure(config); + verify(wifiManager).registerNetworkRequestMatchCallback(any(), any()); + } - AccessPoint verifyAccessPoint = new AccessPoint(mContext, config); - verify(accessPointList, times(1)).set(2, verifyAccessPoint); - } + @Test + public void onPause_shouldUnRegisterCallback() { + when(networkRequestDialogFragment.getContext()).thenReturn(mContext); + Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); + when(mContext.getApplicationContext()).thenReturn(applicationContext); + WifiManager wifiManager = mock(WifiManager.class); + when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); - @Test - public void onUserSelectionCallbackRegistration_shouldCallSelect() { - List accessPointList = spy(new ArrayList<>()); - Bundle bundle = new Bundle(); - bundle.putString(KEY_SSID, "Test AP 1"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 2"); - accessPointList.add(new AccessPoint(mContext, bundle)); - bundle.putString(KEY_SSID, "Test AP 3"); - AccessPoint clickedAccessPoint = new AccessPoint(mContext, bundle); - accessPointList.add(clickedAccessPoint); - bundle.putString(KEY_SSID, "Test AP 4"); - accessPointList.add(new AccessPoint(mContext, bundle)); - when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); + networkRequestDialogFragment.onPause(); - NetworkRequestUserSelectionCallback selectionCallback = mock( - NetworkRequestUserSelectionCallback.class); - AlertDialog dialog = mock(AlertDialog.class); - networkRequestDialogFragment.onUserSelectionCallbackRegistration(selectionCallback); + verify(wifiManager).unregisterNetworkRequestMatchCallback(networkRequestDialogFragment); + } - networkRequestDialogFragment.onClick(dialog, 2); + @Test + public void updateAccessPointList_onUserSelectionConnectSuccess_updateCorrectly() { + List accessPointList = spy(new ArrayList<>()); + Bundle bundle = new Bundle(); + bundle.putString(KEY_SSID, "Test AP 1"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 2"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 3"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 4"); + accessPointList.add(new AccessPoint(mContext, bundle)); - verify(selectionCallback, times(1)).select(clickedAccessPoint.getConfig()); - } + when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); + networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); - @Test - public void onMatch_shouldUpdatedList() { - // Prepares WifiManager. - when(networkRequestDialogFragment.getContext()).thenReturn(mContext); - Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); - when(mContext.getApplicationContext()).thenReturn(applicationContext); - WifiManager wifiManager = mock(WifiManager.class); - when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); + // Test if config would update list. + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "Test AP 3"; + networkRequestDialogFragment.onUserSelectionConnectSuccess(config); - List wifiConfigurationList = new ArrayList<>(); - WifiConfiguration config = new WifiConfiguration(); - final String SSID_AP1 = "Test AP 1"; - config.SSID = SSID_AP1; - wifiConfigurationList.add(config); - config = new WifiConfiguration(); - final String SSID_AP2 = "Test AP 2"; - config.SSID = SSID_AP2; - wifiConfigurationList.add(config); + AccessPoint verifyAccessPoint = new AccessPoint(mContext, config); + verify(accessPointList, times(1)).set(2, verifyAccessPoint); + } - // Prepares callback converted data. - List scanResults = new ArrayList<>(); - when(wifiManager.getAllMatchingWifiConfigs(scanResults)).thenReturn(wifiConfigurationList); + @Test + public void updateAccessPointList_onUserSelectionConnectFailure_updateCorrectly() { + List accessPointList = spy(new ArrayList<>()); + Bundle bundle = new Bundle(); + bundle.putString(KEY_SSID, "Test AP 1"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 2"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 3"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 4"); + accessPointList.add(new AccessPoint(mContext, bundle)); - networkRequestDialogFragment.onMatch(scanResults); + when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); + networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null); - List accessPointList = networkRequestDialogFragment.getAccessPointList(); - assertThat(accessPointList).isNotEmpty(); - assertThat(accessPointList.size()).isEqualTo(2); - assertThat(accessPointList.get(0).getSsid()).isEqualTo(SSID_AP1); - assertThat(accessPointList.get(1).getSsid()).isEqualTo(SSID_AP2); - } + // Test if config would update list. + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "Test AP 3"; + networkRequestDialogFragment.onUserSelectionConnectFailure(config); + + AccessPoint verifyAccessPoint = new AccessPoint(mContext, config); + verify(accessPointList, times(1)).set(2, verifyAccessPoint); + } + + @Test + public void onUserSelectionCallbackRegistration_shouldCallSelect() { + List accessPointList = spy(new ArrayList<>()); + Bundle bundle = new Bundle(); + bundle.putString(KEY_SSID, "Test AP 1"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 2"); + accessPointList.add(new AccessPoint(mContext, bundle)); + bundle.putString(KEY_SSID, "Test AP 3"); + AccessPoint clickedAccessPoint = new AccessPoint(mContext, bundle); + accessPointList.add(clickedAccessPoint); + bundle.putString(KEY_SSID, "Test AP 4"); + accessPointList.add(new AccessPoint(mContext, bundle)); + when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList); + + NetworkRequestUserSelectionCallback selectionCallback = mock( + NetworkRequestUserSelectionCallback.class); + AlertDialog dialog = mock(AlertDialog.class); + networkRequestDialogFragment.onUserSelectionCallbackRegistration(selectionCallback); + + networkRequestDialogFragment.onClick(dialog, 2); + + verify(selectionCallback, times(1)).select(clickedAccessPoint.getConfig()); + } + + @Test + public void onMatch_shouldUpdatedList() { + // Prepares WifiManager. + when(networkRequestDialogFragment.getContext()).thenReturn(mContext); + Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext()); + when(mContext.getApplicationContext()).thenReturn(applicationContext); + WifiManager wifiManager = mock(WifiManager.class); + when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager); + + List wifiConfigurationList = new ArrayList<>(); + WifiConfiguration config = new WifiConfiguration(); + final String SSID_AP1 = "Test AP 1"; + config.SSID = SSID_AP1; + wifiConfigurationList.add(config); + config = new WifiConfiguration(); + final String SSID_AP2 = "Test AP 2"; + config.SSID = SSID_AP2; + wifiConfigurationList.add(config); + + // Prepares callback converted data. + List scanResults = new ArrayList<>(); + when(wifiManager.getAllMatchingWifiConfigs(scanResults)).thenReturn(wifiConfigurationList); + + networkRequestDialogFragment.onMatch(scanResults); + + List accessPointList = networkRequestDialogFragment.getAccessPointList(); + assertThat(accessPointList).isNotEmpty(); + assertThat(accessPointList.size()).isEqualTo(2); + assertThat(accessPointList.get(0).getSsid()).isEqualTo(SSID_AP1); + assertThat(accessPointList.get(1).getSsid()).isEqualTo(SSID_AP2); + } } diff --git a/tests/robotests/src/com/android/settings/wifi/NetworkRequestErrorDialogFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/NetworkRequestErrorDialogFragmentTest.java new file mode 100644 index 00000000000..c6659a5e3cf --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/NetworkRequestErrorDialogFragmentTest.java @@ -0,0 +1,117 @@ +/* + * 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.wifi; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +import android.content.DialogInterface; +import android.os.Bundle; +import android.widget.Button; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; + +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl; +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; +import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = {SettingsShadowResourcesImpl.class, ShadowAlertDialogCompat.class}) +public class NetworkRequestErrorDialogFragmentTest { + + private FragmentActivity mActivity; + private NetworkRequestErrorDialogFragment mFragment; + + @Before + public void setUp() { + mActivity = Robolectric.setupActivity(FragmentActivity.class); + mFragment = spy(NetworkRequestErrorDialogFragment.newInstance()); + mFragment.show(mActivity.getSupportFragmentManager(), null); + } + + @Test + public void display_shouldShowTimeoutDialog() { + AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + + assertThat(alertDialog).isNotNull(); + assertThat(alertDialog.isShowing()).isTrue(); + + ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(alertDialog); + assertThat(RuntimeEnvironment.application + .getString(R.string.network_connection_timeout_dialog_message)) + .isEqualTo(shadowAlertDialog.getMessage()); + } + + @Test + public void display_shouldShowAbortDialog() { + mFragment = spy(NetworkRequestErrorDialogFragment.newInstance()); + Bundle bundle = new Bundle(); + bundle.putSerializable(NetworkRequestErrorDialogFragment.DIALOG_TYPE, + ERROR_DIALOG_TYPE.ABORT); + mFragment.setArguments(bundle); + mFragment.show(mActivity.getSupportFragmentManager(), null); + + AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + + assertThat(alertDialog).isNotNull(); + assertThat(alertDialog.isShowing()).isTrue(); + + ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(alertDialog); + assertThat(RuntimeEnvironment.application + .getString(R.string.network_connection_errorstate_dialog_message)) + .isEqualTo(shadowAlertDialog.getMessage()); + } + + @Test + public void clickPositiveButton_shouldCallStartScanningDialog() { + AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(alertDialog.isShowing()).isTrue(); + + Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + assertThat(positiveButton).isNotNull(); + + positiveButton.performClick(); + verify(mFragment, times(1)).startScanningDialog(); + } + + @Test + public void clickNegativeButton_shouldCloseTheDialog() { + AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(alertDialog.isShowing()).isTrue(); + + Button negativeButton = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE); + assertThat(negativeButton).isNotNull(); + + negativeButton.performClick(); + assertThat(alertDialog.isShowing()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragmentTest.java deleted file mode 100644 index ed28e60c0a1..00000000000 --- a/tests/robotests/src/com/android/settings/wifi/NetworkRequestTimeoutDialogFragmentTest.java +++ /dev/null @@ -1,90 +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.wifi; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.internal.verification.VerificationModeFactory.times; - -import android.content.DialogInterface; -import android.widget.Button; -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.FragmentActivity; -import com.android.settings.R; -import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl; -import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.Robolectric; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; - -@RunWith(SettingsRobolectricTestRunner.class) -@Config(shadows = {SettingsShadowResourcesImpl.class, ShadowAlertDialogCompat.class}) -public class NetworkRequestTimeoutDialogFragmentTest { - - private FragmentActivity mActivity; - private NetworkRequestTimeoutDialogFragment mFragment; - - @Before - public void setUp() { - mActivity = Robolectric.setupActivity(FragmentActivity.class); - mFragment = spy(NetworkRequestTimeoutDialogFragment.newInstance()); - mFragment.show(mActivity.getSupportFragmentManager(), null); - } - - @Test - public void display_shouldShowTheDialog() { - AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - - assertThat(alertDialog).isNotNull(); - assertThat(alertDialog.isShowing()).isTrue(); - - ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(alertDialog); - assertThat(RuntimeEnvironment.application - .getString(R.string.network_connection_timeout_dialog_message)) - .isEqualTo(shadowAlertDialog.getMessage()); - } - - @Test - public void clickPositiveButton_shouldCallStartScanningDialog() { - AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - assertThat(alertDialog.isShowing()).isTrue(); - - Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - assertThat(positiveButton).isNotNull(); - - positiveButton.performClick(); - verify(mFragment, times(1)).startScanningDialog(); - } - - @Test - public void clickNegativeButton_shouldCloseTheDialog() { - AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - assertThat(alertDialog.isShowing()).isTrue(); - - Button negativeButton = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE); - assertThat(negativeButton).isNotNull(); - - negativeButton.performClick(); - assertThat(alertDialog.isShowing()).isFalse(); - } -} From d9c4044722698a2a10576d09a4674b1ea6495ec7 Mon Sep 17 00:00:00 2001 From: Leon Liao Date: Fri, 7 Dec 2018 11:08:43 +0800 Subject: [PATCH 04/11] Fix content timeout item misorder in Display category in A11y Settings A11y Content timeout item is disordered by program while device supports HWC accelerated color transform. Add code to fix its order with present resource order. Bug: 119283926 Test: atest AccessibilitySettingsTest Test: make RunSettingsRoboTests Change-Id: I5e8bff2347353d2c4fe9aa550d7efaa613bf2a90 --- .../android/settings/accessibility/AccessibilitySettings.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index d6590fa041f..76be66db123 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -667,6 +667,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleInversionPreference.getOrder() + 1); mToggleDisableAnimationsPreference.setOrder( mToggleLargePointerIconPreference.getOrder() + 1); + findPreference(ACCESSIBILITY_CONTENT_TIMEOUT_PREFERENCE).setOrder( + mToggleDisableAnimationsPreference.getOrder() + 1); mToggleInversionPreference.setSummary(R.string.summary_empty); displayCategory.addPreference(mToggleInversionPreference); displayCategory.addPreference(mDisplayDaltonizerPreferenceScreen); From b33f74192c998a8712ff34da257b4b415cbebdc1 Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Fri, 7 Dec 2018 15:40:00 -0800 Subject: [PATCH 05/11] Add a helper class for listening to subscription and airplane mode changes For the new multi subscriptions UX, in several places we need to listen for both subscription changes (eg a sim card was inserted or removed), as well as airplane mode changing state. This combines those in one helper class that can be reused. Bug: 116349402 Test: make RunSettingsRoboTests Change-Id: Ibcbb669ae291a917f89735dbda043742efaa58c9 --- .../network/SubscriptionsChangeListener.java | 97 +++++++++++++ .../SubscriptionsChangeListenerTest.java | 129 ++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/com/android/settings/network/SubscriptionsChangeListener.java create mode 100644 tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java diff --git a/src/com/android/settings/network/SubscriptionsChangeListener.java b/src/com/android/settings/network/SubscriptionsChangeListener.java new file mode 100644 index 00000000000..c3bb22bbb62 --- /dev/null +++ b/src/com/android/settings/network/SubscriptionsChangeListener.java @@ -0,0 +1,97 @@ +/* + * 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.network; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; + +import com.android.internal.telephony.TelephonyIntents; + +/** Helper class for listening to changes in availability of telephony subscriptions */ +public class SubscriptionsChangeListener extends ContentObserver { + + public interface SubscriptionsChangeListenerClient { + void onAirplaneModeChanged(boolean airplaneModeEnabled); + void onSubscriptionsChanged(); + } + + private Context mContext; + private SubscriptionsChangeListenerClient mClient; + private SubscriptionManager mSubscriptionManager; + private OnSubscriptionsChangedListener mSubscriptionsChangedListener; + private Uri mAirplaneModeSettingUri; + private BroadcastReceiver mBroadcastReceiver; + + public SubscriptionsChangeListener(Context context, SubscriptionsChangeListenerClient client) { + super(new Handler()); + mContext = context; + mClient = client; + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + mSubscriptionsChangedListener = new OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + subscriptionsChangedCallback(); + } + }; + mAirplaneModeSettingUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON); + mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + subscriptionsChangedCallback(); + } + }; + } + + public void start() { + mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionsChangedListener); + mContext.getContentResolver() + .registerContentObserver(mAirplaneModeSettingUri, false, this); + final IntentFilter radioTechnologyChangedFilter = new IntentFilter( + TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, radioTechnologyChangedFilter); + } + + public void stop() { + mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener); + mContext.getContentResolver().unregisterContentObserver(this); + mContext.unregisterReceiver(mBroadcastReceiver); + } + + public boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) != 0; + } + + private void subscriptionsChangedCallback() { + mClient.onSubscriptionsChanged(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri.equals(mAirplaneModeSettingUri)) { + mClient.onAirplaneModeChanged(isAirplaneModeOn()); + } + } +} diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java new file mode 100644 index 00000000000..88ea2eac522 --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java @@ -0,0 +1,129 @@ +/* + * 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.network; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.AdditionalMatchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.Uri; +import android.provider.Settings; +import android.telephony.SubscriptionManager; + +import com.android.settings.network.SubscriptionsChangeListener.SubscriptionsChangeListenerClient; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class SubscriptionsChangeListenerTest { + + @Mock + private SubscriptionsChangeListenerClient mClient; + @Mock + private SubscriptionManager mSubscriptionManager; + + private Context mContext; + private SubscriptionsChangeListener mListener; + private Uri mAirplaneModeUri; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); + + mAirplaneModeUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON); + } + + private void initListener(boolean alsoStart) { + mListener = new SubscriptionsChangeListener(mContext, mClient); + if (alsoStart) { + mListener.start(); + } + } + + @Test + public void whenStartNotCalled_noListeningWasSetup() { + final ContentResolver contentResolver = mock(ContentResolver.class); + when(mContext.getContentResolver()).thenReturn(contentResolver); + initListener(false); + verify(contentResolver, never()).registerContentObserver(any(Uri.class), anyBoolean(), + any(ContentObserver.class)); + verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(any()); + verify(mContext, never()).registerReceiver(any(), any()); + } + + @Test + public void onSubscriptionsChangedEvent_subscriptionManagerFires_eventDeliveredToUs() { + initListener(true); + final ArgumentCaptor captor = + ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class); + verify(mSubscriptionManager).addOnSubscriptionsChangedListener(captor.capture()); + captor.getValue().onSubscriptionsChanged(); + verify(mClient).onSubscriptionsChanged(); + } + + @Test + public void onSubscriptionsChangedEvent_radioTechnologyChangedBroadcast_eventDeliveredToUs() { + initListener(true); + final ArgumentCaptor broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any()); + broadcastReceiverCaptor.getValue().onReceive(mContext, null); + verify(mClient).onSubscriptionsChanged(); + } + + @Test + public void onAirplaneModeChangedEvent_becameTrue_eventFires() { + initListener(true); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + mListener.onChange(false, mAirplaneModeUri); + verify(mClient).onAirplaneModeChanged(true); + assertThat(mListener.isAirplaneModeOn()).isTrue(); + } + + @Test + public void onAirplaneModeChangedEvent_becameFalse_eventFires() { + initListener(true); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); + mListener.onChange(false, mAirplaneModeUri); + verify(mClient).onAirplaneModeChanged(false); + assertThat(mListener.isAirplaneModeOn()).isFalse(); + } + +} From f301f8b216459a4903bb10bb008331aebacd2758 Mon Sep 17 00:00:00 2001 From: Stanley Wang Date: Mon, 3 Dec 2018 18:51:03 +0800 Subject: [PATCH 06/11] Fix carrier-specific Wi-Fi settings appears in search - even on non-carrier phone The preference should be removed from the search result when the availability status is false. Change-Id: I979bc70ec7672b137b96a7e02db2e9ba29fbb7a8 Fixes: 112550245 Test: manual and robotests --- res/xml/wifi_configure_settings.xml | 3 +- .../CellularFallbackPreferenceController.java | 54 +++++-------------- .../settings/wifi/ConfigureWifiSettings.java | 1 - ...lularFallbackPreferenceControllerTest.java | 3 +- 4 files changed, 17 insertions(+), 44 deletions(-) diff --git a/res/xml/wifi_configure_settings.xml b/res/xml/wifi_configure_settings.xml index 8ec320a053c..15ab1aa16ba 100644 --- a/res/xml/wifi_configure_settings.xml +++ b/res/xml/wifi_configure_settings.xml @@ -41,7 +41,8 @@ + android:summary="@string/wifi_cellular_data_fallback_summary" + settings:controller="com.android.settings.wifi.CellularFallbackPreferenceController" /> Date: Mon, 10 Dec 2018 07:05:20 +0000 Subject: [PATCH 07/11] Revert "Revert "Schematize vold system properties"" This reverts commit 49e81e0eeac13b1edcd4c8f51c48dafae6336d11. Reason for revert: Fixed breakage Change-Id: I414b753a218d9baf3d24530ff14df7644eb148e0 Merged-In: Id2a696c621015d46015d5de0e7aef987e9e01b54 --- src/com/android/settings/CryptKeeper.java | 11 +++++------ src/com/android/settings/MasterClear.java | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java index 1f331d7a635..ca54b07dcd5 100644 --- a/src/com/android/settings/CryptKeeper.java +++ b/src/com/android/settings/CryptKeeper.java @@ -33,11 +33,11 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; +import android.sysprop.VoldProperties; import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.text.Editable; @@ -400,7 +400,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList super.onCreate(savedInstanceState); // If we are not encrypted or encrypting, get out quickly. - final String state = SystemProperties.get("vold.decrypt"); + final String state = VoldProperties.decrypt().orElse(""); if (!isDebugView() && ("".equals(state) || DECRYPT_STATE.equals(state))) { disableCryptKeeperComponent(this); // Typically CryptKeeper is launched as the home app. We didn't @@ -468,7 +468,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList return; } - final String progress = SystemProperties.get("vold.encrypt_progress"); + final String progress = VoldProperties.encrypt_progress().orElse(""); if (!"".equals(progress) || isDebugView(FORCE_VIEW_PROGRESS)) { setContentView(R.layout.crypt_keeper_progress); encryptionProgressInit(); @@ -636,7 +636,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } private void updateProgress() { - final String state = SystemProperties.get("vold.encrypt_progress"); + final String state = VoldProperties.encrypt_progress().orElse(""); if ("error_partially_encrypted".equals(state)) { showFactoryReset(false); @@ -657,8 +657,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList // Now try to get status as time remaining and replace as appropriate Log.v(TAG, "Encryption progress: " + progress); try { - final String timeProperty = SystemProperties.get("vold.encrypt_time_remaining"); - int time = Integer.parseInt(timeProperty); + int time = VoldProperties.encrypt_time_remaining().get(); if (time >= 0) { // Round up to multiple of 10 - this way display is less jerky time = (time + 9) / 10 * 10; diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java index a7da41692fa..182081c6335 100644 --- a/src/com/android/settings/MasterClear.java +++ b/src/com/android/settings/MasterClear.java @@ -39,6 +39,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import androidx.annotation.VisibleForTesting; +import android.sysprop.VoldProperties; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; import android.util.Log; @@ -404,7 +405,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL } private boolean isExtStorageEncrypted() { - String state = SystemProperties.get("vold.decrypt"); + String state = VoldProperties.decrypt().orElse(""); return !"".equals(state); } From fce1835ef5bd6e73a62ed9e4a230f49a567a0645 Mon Sep 17 00:00:00 2001 From: Phil Weaver Date: Tue, 4 Dec 2018 09:09:49 -0800 Subject: [PATCH 08/11] Clarify text shown in a11y service warning dialog Replacing the text generated from the service's capabilities (which could change) with a generic message based on the maximum capabilities (full control) a service can obtain. Bug: 110715236 Test: atest SettingsRoboTests Change-Id: Iecb548c77c9031626d4da24741ec1f753d0cc94f --- ...e_accessibility_service_dialog_content.xml | 9 +-- res/values/strings.xml | 11 ++-- .../AccessibilityServiceWarning.java | 61 ++----------------- 3 files changed, 12 insertions(+), 69 deletions(-) diff --git a/res/layout/enable_accessibility_service_dialog_content.xml b/res/layout/enable_accessibility_service_dialog_content.xml index f212eb16725..e16dc657a30 100644 --- a/res/layout/enable_accessibility_service_dialog_content.xml +++ b/res/layout/enable_accessibility_service_dialog_content.xml @@ -38,20 +38,13 @@ android:textAppearance="?android:attr/textAppearanceMedium"/> - - diff --git a/res/values/strings.xml b/res/values/strings.xml index fb8ab201498..4700d3fee37 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4818,11 +4818,12 @@ example="TalkBack">%1$s affects data encryption, you need to confirm your password. - - Observe your actions - - Receive notifications when you\u2019re - interacting with an app. + + %1$s is requesting full control of this device. The service + can read the screen and act on behalf of users with accessibility needs. + This level of control is not appropriate for most apps. + Stop %1$s? diff --git a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java b/src/com/android/settings/accessibility/AccessibilityServiceWarning.java index 6360d421b5f..e0f17473b35 100644 --- a/src/com/android/settings/accessibility/AccessibilityServiceWarning.java +++ b/src/com/android/settings/accessibility/AccessibilityServiceWarning.java @@ -43,8 +43,8 @@ import java.util.List; import java.util.Locale; /** - * Utility class for creating the dialog that asks users for explicit permission to grant - * all of the requested capabilities to an accessibility service before the service is enabled + * Utility class for creating the dialog that asks users for explicit permission for an + * accessibility service to access user data before the service is enabled */ public class AccessibilityServiceWarning { public static Dialog createCapabilitiesDialog(Activity parentActivity, @@ -117,60 +117,9 @@ public class AccessibilityServiceWarning { encryptionWarningView.setVisibility(View.GONE); } - TextView capabilitiesHeaderView = (TextView) content.findViewById( - R.id.capabilities_header); - capabilitiesHeaderView.setText(context.getString(R.string.capabilities_list_title, - getServiceName(context, info))); - - LinearLayout capabilitiesView = (LinearLayout) content.findViewById(R.id.capabilities); - - // This capability is implicit for all services. - View capabilityView = inflater.inflate( - com.android.internal.R.layout.app_permission_item_old, null); - - ImageView imageView = (ImageView) capabilityView.findViewById( - com.android.internal.R.id.perm_icon); - imageView.setImageDrawable(context.getDrawable( - com.android.internal.R.drawable.ic_text_dot)); - - TextView labelView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_group); - labelView.setText(context.getString( - R.string.capability_title_receiveAccessibilityEvents)); - - TextView descriptionView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_list); - descriptionView.setText( - context.getString(R.string.capability_desc_receiveAccessibilityEvents)); - - List capabilities = - info.getCapabilityInfos(context); - - capabilitiesView.addView(capabilityView); - - // Service-specific capabilities. - final int capabilityCount = capabilities.size(); - for (int i = 0; i < capabilityCount; i++) { - AccessibilityServiceInfo.CapabilityInfo capability = capabilities.get(i); - - capabilityView = inflater.inflate( - com.android.internal.R.layout.app_permission_item_old, null); - - imageView = (ImageView) capabilityView.findViewById( - com.android.internal.R.id.perm_icon); - imageView.setImageDrawable(context.getDrawable( - com.android.internal.R.drawable.ic_text_dot)); - - labelView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_group); - labelView.setText(context.getString(capability.titleResId)); - - descriptionView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_list); - descriptionView.setText(context.getString(capability.descResId)); - - capabilitiesView.addView(capabilityView); - } + TextView serviceWarningTextView = content.findViewById(R.id.accessibility_service_warning); + serviceWarningTextView.setText(context.getString(R.string.accessibility_service_warning, + getServiceName(context, info))); return content; } From 43f0d13e1d19ba9b70d8900bc0fd9a8e7894c201 Mon Sep 17 00:00:00 2001 From: Johnson Lu Date: Wed, 5 Dec 2018 13:45:13 +0800 Subject: [PATCH 09/11] Implement host flow of WiFi sharing feature Add share button and authentication screen to confirm credentials (pin, pattern or password) for the current user of the device. Bug: 120517242 Test: make RunSettingsRoboTests Change-Id: Id79833db0d582c401f1371ff60429e42ce1990e0 --- res/drawable/ic_qrcode_24dp.xml | 39 ++++++++++++ res/drawable/ic_scan_24dp.xml | 33 +++++++++++ res/values/strings.xml | 4 ++ .../android/settings/core/FeatureFlags.java | 1 + .../WifiDetailPreferenceController.java | 59 ++++++++++++++++++- .../details/WifiNetworkDetailsFragment.java | 14 +++++ .../WifiDetailPreferenceControllerTest.java | 6 ++ 7 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 res/drawable/ic_qrcode_24dp.xml create mode 100644 res/drawable/ic_scan_24dp.xml diff --git a/res/drawable/ic_qrcode_24dp.xml b/res/drawable/ic_qrcode_24dp.xml new file mode 100644 index 00000000000..ff7806f8107 --- /dev/null +++ b/res/drawable/ic_qrcode_24dp.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/res/drawable/ic_scan_24dp.xml b/res/drawable/ic_scan_24dp.xml new file mode 100644 index 00000000000..bcef8e32db0 --- /dev/null +++ b/res/drawable/ic_scan_24dp.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 4aa09515ca0..856dbb21eba 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -653,6 +653,8 @@ Done Apply + + Share Settings @@ -3757,6 +3759,8 @@ To use face authentication, set pattern To use face authentication, set PIN + + Your Wi\u2011Fi name and password for \"%1$s\" will be shared. diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java index daa1e7b1b08..b35077894f2 100644 --- a/src/com/android/settings/core/FeatureFlags.java +++ b/src/com/android/settings/core/FeatureFlags.java @@ -26,4 +26,5 @@ public class FeatureFlags { public static final String MOBILE_NETWORK_V2 = "settings_mobile_network_v2"; public static final String WIFI_MAC_RANDOMIZATION = "settings_wifi_mac_randomization"; public static final String NETWORK_INTERNET_V2 = "settings_network_and_internet_v2"; + public static final String WIFI_SHARING = "settings_wifi_sharing"; } diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java index a676bfa6a9e..e1179f820fa 100644 --- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import android.app.Activity; +import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -40,6 +41,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; import android.text.TextUtils; +import android.util.FeatureFlagUtils; import android.util.Log; import android.widget.ImageView; import android.widget.Toast; @@ -54,8 +56,11 @@ import androidx.preference.PreferenceScreen; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.Utils; +import com.android.settings.core.FeatureFlags; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.EntityHeaderController; +import com.android.settings.wifi.dpp.WifiDppConfiguratorActivity; +import com.android.settings.wifi.dpp.WifiDppUtils; import com.android.settings.wifi.WifiDialog; import com.android.settings.wifi.WifiDialog.WifiDialogListener; import com.android.settings.wifi.WifiUtils; @@ -280,7 +285,10 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController .setButton1Icon(R.drawable.ic_settings_delete) .setButton1OnClickListener(view -> forgetNetwork()) .setButton2Text(R.string.wifi_sign_in_button_text) - .setButton2OnClickListener(view -> signIntoNetwork()); + .setButton2OnClickListener(view -> signIntoNetwork()) + .setButton3Text(R.string.share) + .setButton3Icon(R.drawable.ic_qrcode_24dp) + .setButton3OnClickListener(view -> shareNetwork()); mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF); mLinkSpeedPref = screen.findPreference(KEY_LINK_SPEED); @@ -296,7 +304,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController mIpv6Category = (PreferenceCategory) screen.findPreference(KEY_IPV6_CATEGORY); mIpv6AddressPref = screen.findPreference(KEY_IPV6_ADDRESSES_PREF); - mSecurityPref.setSummary(mAccessPoint.getSecurityString(false /* concise */)); + mSecurityPref.setSummary(mAccessPoint.getSecurityString(/* concise */ false)); } private void setupEntityHeader(PreferenceScreen screen) { @@ -425,7 +433,9 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private void updateIpLayerInfo() { mButtonsPref.setButton2Visible(canSignIntoNetwork()); - mButtonsPref.setVisible(canSignIntoNetwork() || canForgetNetwork()); + mButtonsPref.setButton3Visible(isSharingNetworkEnabled()); + mButtonsPref.setVisible( + canSignIntoNetwork() || canForgetNetwork() || isSharingNetworkEnabled()); if (mNetwork == null || mLinkProperties == null) { mIpAddressPref.setVisible(false); @@ -510,6 +520,13 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController return WifiUtils.canSignIntoNetwork(mNetworkCapabilities); } + /** + * Returns whether the user can share the network represented by this preference with QR code. + */ + private boolean isSharingNetworkEnabled() { + return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.WIFI_SHARING); + } + /** * Forgets the wifi network associated with this preference. */ @@ -528,6 +545,42 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController mFragment.getActivity().finish(); } + /** + * Show QR code to share the network represented by this preference. + */ + public void launchQRCodeGenerator() { + final Intent intent = new Intent( + WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR); + intent.putExtra(WifiDppUtils.EXTRA_WIFI_SECURITY, + mAccessPoint.getSecurityString(/* concise */ false)); + intent.putExtra(WifiDppUtils.EXTRA_WIFI_SSID, mAccessPoint.getSsidStr()); + mContext.startActivity(intent); + } + + /** + * Share the wifi network with QR code. + */ + private void shareNetwork() { + final KeyguardManager keyguardManager = (KeyguardManager) mContext.getSystemService( + Context.KEYGUARD_SERVICE); + if (keyguardManager.isKeyguardSecure()) { + // Show authentication screen to confirm credentials (pin, pattern or password) for + // the current user of the device. + final String description = String.format( + mContext.getString(R.string.wifi_sharing_message), + mAccessPoint.getSsidStr()); + final Intent intent = keyguardManager.createConfirmDeviceCredentialIntent( + mContext.getString(R.string.lockpassword_confirm_your_pattern_header), + description); + if (intent != null) { + mFragment.startActivityForResult(intent, + WifiNetworkDetailsFragment.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); + } + } else { + launchQRCodeGenerator(); + } + } + /** * Sign in to the captive portal found on this wifi network associated with this preference. */ diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java index 7f0e8ee5677..98144864620 100644 --- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java +++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java @@ -17,8 +17,10 @@ package com.android.settings.wifi.details; import static com.android.settings.wifi.WifiSettings.WIFI_DIALOG_ID; +import android.app.Activity; import android.app.Dialog; import android.content.Context; +import android.content.Intent; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Bundle; @@ -52,6 +54,8 @@ public class WifiNetworkDetailsFragment extends DashboardFragment { private static final String TAG = "WifiNetworkDetailsFrg"; + public static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1; + private AccessPoint mAccessPoint; private WifiDetailPreferenceController mWifiDetailPreferenceController; @@ -142,4 +146,14 @@ public class WifiNetworkDetailsFragment extends DashboardFragment { return controllers; } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS + && resultCode == Activity.RESULT_OK) { + mWifiDetailPreferenceController.launchQRCodeGenerator(); + } + } } diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index 39215c16d3b..eab9e51a094 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -865,6 +865,12 @@ public class WifiDetailPreferenceControllerTest { when(pref.setButton2Visible(anyBoolean())).thenReturn(pref); when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + when(pref.setButton3Text(anyInt())).thenReturn(pref); + when(pref.setButton3Icon(anyInt())).thenReturn(pref); + when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton3Visible(anyBoolean())).thenReturn(pref); + when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + return pref; } } From 48903ccefef95bab6d9d794bf57e3ff818733aff Mon Sep 17 00:00:00 2001 From: Beverly Date: Tue, 4 Dec 2018 12:32:31 -0500 Subject: [PATCH 10/11] Add edit and delete buttons to zen rules Test: atest ZenModeScheduleRuleSettingsTest ZenModeEventRuleSettingsTest Test: manual Bug: 111475013 Fixes: 120787343 Fixes: 120758763 Change-Id: If81ce2972e6887fb2a85d4b7262e81b26162e19c --- res/values/strings.xml | 5 +- res/xml/zen_mode_event_rule_settings.xml | 7 +- res/xml/zen_mode_schedule_rule_settings.xml | 7 +- ...tomaticRuleHeaderPreferenceController.java | 24 ---- .../ZenModeAutomationSettings.java | 12 ++ .../ZenModeEventRuleSettings.java | 35 +++-- .../notification/ZenModeRuleSettingsBase.java | 8 +- .../ZenModeScheduleRuleSettings.java | 4 +- .../notification/ZenModeSettingsBase.java | 9 +- .../ZenRuleButtonsPreferenceController.java | 124 ++++++++++++++++++ 10 files changed, 194 insertions(+), 41 deletions(-) create mode 100644 src/com/android/settings/notification/ZenRuleButtonsPreferenceController.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 44d43c3b9c5..e2a5857cf11 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7123,6 +7123,9 @@ Delete + + Edit + Schedules @@ -7893,7 +7896,7 @@ %d others - + Allow text messages diff --git a/res/xml/zen_mode_event_rule_settings.xml b/res/xml/zen_mode_event_rule_settings.xml index c871fe16dc0..a70b46ce30f 100644 --- a/res/xml/zen_mode_event_rule_settings.xml +++ b/res/xml/zen_mode_event_rule_settings.xml @@ -17,12 +17,17 @@ + android:layout="@layout/settings_entity_header" + settings:allowDividerBelow="true" /> + + + android:layout="@layout/settings_entity_header" + settings:allowDividerBelow="true"/> + + createPreferenceControllers(Context context) { ZenServiceListing serviceListing = new ZenServiceListing(getContext(), CONFIG); diff --git a/src/com/android/settings/notification/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/ZenModeEventRuleSettings.java index c11a6727b32..ee3ed1cbc40 100644 --- a/src/com/android/settings/notification/ZenModeEventRuleSettings.java +++ b/src/com/android/settings/notification/ZenModeEventRuleSettings.java @@ -27,6 +27,11 @@ import android.provider.Settings; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; +import androidx.preference.DropDownPreference; +import androidx.preference.Preference; +import androidx.preference.Preference.OnPreferenceChangeListener; +import androidx.preference.PreferenceScreen; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; @@ -38,11 +43,6 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -import androidx.preference.DropDownPreference; -import androidx.preference.Preference; -import androidx.preference.Preference.OnPreferenceChangeListener; -import androidx.preference.PreferenceScreen; - public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { private static final String KEY_CALENDAR = "calendar"; private static final String KEY_REPLY = "reply"; @@ -85,9 +85,12 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { List controllers = new ArrayList<>(); mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this, getSettingsLifecycle()); + mActionButtons = new ZenRuleButtonsPreferenceController(context, this, + getSettingsLifecycle()); mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, getSettingsLifecycle()); controllers.add(mHeader); + controllers.add(mActionButtons); controllers.add(mSwitch); return controllers; } @@ -107,8 +110,16 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { mEvent.calendarId = calendar.calendarId; } } - mCalendar.setEntries(entries.toArray(new CharSequence[entries.size()])); - mCalendar.setEntryValues(values.toArray(new CharSequence[values.size()])); + + CharSequence[] entriesArr = entries.toArray(new CharSequence[entries.size()]); + CharSequence[] valuesArr = values.toArray(new CharSequence[values.size()]); + if (!Objects.equals(mCalendar.getEntries(), entriesArr)) { + mCalendar.setEntries(entriesArr); + } + + if (!Objects.equals(mCalendar.getEntryValues(), valuesArr)) { + mCalendar.setEntryValues(valuesArr); + } } @Override @@ -159,8 +170,12 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { @Override protected void updateControlsInternal() { - mCalendar.setValue(key(mEvent)); - mReply.setValue(Integer.toString(mEvent.reply)); + if (!Objects.equals(mCalendar.getValue(), key(mEvent))) { + mCalendar.setValue(key(mEvent)); + } + if (!Objects.equals(mReply.getValue(), Integer.toString(mEvent.reply))) { + mReply.setValue(Integer.toString(mEvent.reply)); + } } @Override @@ -233,7 +248,7 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { private static String key(int userId, Long calendarId, String displayName) { return EventInfo.resolveUserId(userId) + ":" + (calendarId == null ? "" : calendarId) - + ":" + displayName; + + ":" + (displayName == null ? "" : displayName); } private static final Comparator CALENDAR_NAME = new Comparator() { diff --git a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java index 11b8084f73f..e573884181a 100644 --- a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java +++ b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java @@ -43,6 +43,7 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase { protected String mId; protected ZenAutomaticRuleHeaderPreferenceController mHeader; + protected ZenRuleButtonsPreferenceController mActionButtons; protected ZenAutomaticRuleSwitchPreferenceController mSwitch; abstract protected void onCreateInternal(); @@ -104,6 +105,10 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase { mHeader.onResume(mRule, mId); mHeader.displayPreference(screen); updatePreference(mHeader); + + mActionButtons.onResume(mRule, mId); + mActionButtons.displayPreference(screen); + updatePreference(mActionButtons); } private void updatePreference(AbstractPreferenceController controller) { @@ -147,7 +152,8 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase { private void toastAndFinish() { Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT) - .show(); + .show(); + getActivity().finish(); } diff --git a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java index 0b2e1bcc27d..8aa993f65cf 100644 --- a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java +++ b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java @@ -199,10 +199,12 @@ public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase { List controllers = new ArrayList<>(); mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this, getSettingsLifecycle()); + mActionButtons = new ZenRuleButtonsPreferenceController(context, this, + getSettingsLifecycle()); mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, getSettingsLifecycle()); - controllers.add(mHeader); + controllers.add(mActionButtons); controllers.add(mSwitch); return controllers; } diff --git a/src/com/android/settings/notification/ZenModeSettingsBase.java b/src/com/android/settings/notification/ZenModeSettingsBase.java index 2aecae4cc0f..b9a14eec1ce 100644 --- a/src/com/android/settings/notification/ZenModeSettingsBase.java +++ b/src/com/android/settings/notification/ZenModeSettingsBase.java @@ -52,9 +52,14 @@ abstract public class ZenModeSettingsBase extends RestrictedDashboardFragment { } @Override - public void onCreate(Bundle icicle) { - mContext = getActivity(); + public void onAttach(Context context) { + super.onAttach(context); + mContext = context; mBackend = ZenModeBackend.getInstance(mContext); + } + + @Override + public void onCreate(Bundle icicle) { super.onCreate(icicle); updateZenMode(false /*fireChanged*/); } diff --git a/src/com/android/settings/notification/ZenRuleButtonsPreferenceController.java b/src/com/android/settings/notification/ZenRuleButtonsPreferenceController.java new file mode 100644 index 00000000000..1d43f6dde3e --- /dev/null +++ b/src/com/android/settings/notification/ZenRuleButtonsPreferenceController.java @@ -0,0 +1,124 @@ +/* + * 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.notification; + +import android.app.AutomaticZenRule; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; + +import androidx.fragment.app.Fragment; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.SubSettingLauncher; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.ActionButtonsPreference; + +public class ZenRuleButtonsPreferenceController extends AbstractZenModePreferenceController + implements PreferenceControllerMixin { + public static final String KEY = "zen_action_buttons"; + + private AutomaticZenRule mRule; + private String mId; + private PreferenceFragmentCompat mFragment; + private ActionButtonsPreference mButtonsPref; + + + public ZenRuleButtonsPreferenceController(Context context, PreferenceFragmentCompat fragment, + Lifecycle lc) { + super(context, KEY, lc); + mFragment = fragment; + } + + + @Override + public boolean isAvailable() { + return mRule != null; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + if (isAvailable()) { + mButtonsPref = ((ActionButtonsPreference) screen.findPreference(KEY)) + .setButton1Text(R.string.zen_mode_rule_name_edit) + .setButton1Icon(R.drawable.ic_mode_edit) + .setButton1OnClickListener(new EditRuleNameClickListener()) + .setButton2Text(R.string.zen_mode_delete_rule_button) + .setButton2Icon(R.drawable.ic_settings_delete) + .setButton2OnClickListener(new DeleteRuleClickListener()); + } + } + + public class EditRuleNameClickListener implements View.OnClickListener { + public EditRuleNameClickListener() {} + + @Override + public void onClick(View v) { + ZenRuleNameDialog.show(mFragment, mRule.getName(), null, + new ZenRuleNameDialog.PositiveClickListener() { + @Override + public void onOk(String ruleName, Fragment parent) { + if (TextUtils.equals(ruleName, mRule.getName())) { + return; + } + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK); + mRule.setName(ruleName); + mRule.setModified(true); + mBackend.updateZenRule(mId, mRule); + } + }); + } + } + + public class DeleteRuleClickListener implements View.OnClickListener { + public DeleteRuleClickListener() {} + + @Override + public void onClick(View v) { + ZenDeleteRuleDialog.show(mFragment, mRule.getName(), mId, + new ZenDeleteRuleDialog.PositiveClickListener() { + @Override + public void onOk(String id) { + Bundle bundle = new Bundle(); + bundle.putString(ZenModeAutomationSettings.DELETE, id); + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_DELETE_RULE_OK); + new SubSettingLauncher(mContext) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + .setDestination(ZenModeAutomationSettings.class.getName()) + .setSourceMetricsCategory(MetricsProto.MetricsEvent + .NOTIFICATION_ZEN_MODE_AUTOMATION) + .setArguments(bundle) + .launch(); + } + }); + } + } + + protected void onResume(AutomaticZenRule rule, String id) { + mRule = rule; + mId = id; + } +} From 108faaf1ce8f5e07de02967746106074fc4addda Mon Sep 17 00:00:00 2001 From: Salvador Martinez Date: Thu, 8 Nov 2018 11:08:52 -0800 Subject: [PATCH 11/11] Create shell UI for smart battery saver This CL creates the new screen WITHOUT any of the behaviors (changing the global settings/calling power manager apis) and hooks it up to the preference in BatterySaverSettings for navigation. The logic for the preference in BatterySaver Settings is also added so that it shows the correct summary when the schedule settings are modified. Additionally, a small tweak is made to the radio preference widget to make it possible to hide the appendix view. It didn't seem to do anything except get in the way and potentially ruin your day, much like its biological counterpart. Test: Overriding power mode via adb, robotests Bug: 111450127 Change-Id: Ic681aaf565ce1caf7d00d314e14ae6c4779fe8f6 --- res/values/strings.xml | 14 +- res/xml/battery_saver_settings.xml | 6 + ...terySaverSchedulePreferenceController.java | 75 ++++++++++ .../BatterySaverScheduleSettings.java | 138 ++++++++++++++++++ .../widget/RadioButtonPreference.java | 13 ++ ...atterySeekBarPreferenceControllerTest.java | 2 +- ...SaverSchedulePreferenceControllerTest.java | 94 ++++++++++++ .../widget/RadioButtonPreferenceTest.java | 10 ++ 8 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java create mode 100644 src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 4aab4e0fceb..3285fe7da32 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5409,11 +5409,23 @@ No schedule + + Based on your routine + + + Based on percentage + + + Battery Saver turns on if your battery is likely to run out before your next typical charge + + + Will turn on at %1$s + Set a schedule - At %1$s + %1$s Turn on diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml index eb4954f8ca8..a0860182f41 100644 --- a/res/xml/battery_saver_settings.xml +++ b/res/xml/battery_saver_settings.xml @@ -20,6 +20,12 @@ android:title="@string/battery_saver" android:key="battery_saver_page"> + + getCandidates() { + Context context = getContext(); + List candidates = Lists.newArrayList(); + candidates.add(new BatterySaverScheduleCandidateInfo( + context.getText(R.string.battery_saver_auto_no_schedule), + /* summary */ null, + KEY_NO_SCHEDULE, + /* enabled */ true)); + candidates.add(new BatterySaverScheduleCandidateInfo( + context.getText(R.string.battery_saver_auto_routine), + context.getText(R.string.battery_saver_auto_routine_summary), + KEY_ROUTINE, + /* enabled */ true)); + candidates.add(new BatterySaverScheduleCandidateInfo( + context.getText(R.string.battery_saver_auto_percentage), + /* summary */ null, + KEY_PERCENTAGE, + /* enabled */ true)); + + return candidates; + } + + @Override + public void bindPreferenceExtra(RadioButtonPreference pref, String key, CandidateInfo info, + String defaultKey, String systemDefaultKey) { + final BatterySaverScheduleCandidateInfo candidateInfo = + (BatterySaverScheduleCandidateInfo) info; + final CharSequence summary = candidateInfo.getSummary(); + if (summary != null) { + pref.setSummary(summary); + pref.setAppendixVisibility(View.GONE); + } + } + + @Override + protected void addStaticPreferences(PreferenceScreen screen) { + SeekBarPreference seekbar = new SeekBarPreference(getContext()); + seekbar.setMax(MAX_SEEKBAR_VALUE); + seekbar.setMin(MIN_SEEKBAR_VALUE); + seekbar.setTitle(R.string.battery_saver_seekbar_title_placeholder); + seekbar.setKey(KEY_BATTERY_SAVER_SEEK_BAR); + screen.addPreference(seekbar); + } + + @Override + protected String getDefaultKey() { + return null; + } + + @Override + protected boolean setDefaultKey(String key) { + return false; + } + + @Override + public int getMetricsCategory() { + return 0; + } + + static class BatterySaverScheduleCandidateInfo extends CandidateInfo { + + private final CharSequence mLabel; + private final CharSequence mSummary; + private final String mKey; + + BatterySaverScheduleCandidateInfo(CharSequence label, CharSequence summary, String key, + boolean enabled) { + super(enabled); + mLabel = label; + mKey = key; + mSummary = summary; + } + + @Override + public CharSequence loadLabel() { + return mLabel; + } + + @Override + public Drawable loadIcon() { + return null; + } + + @Override + public String getKey() { + return mKey; + } + + public CharSequence getSummary() { + return mSummary; + } + } +} \ No newline at end of file diff --git a/src/com/android/settings/widget/RadioButtonPreference.java b/src/com/android/settings/widget/RadioButtonPreference.java index ed7f9053121..512fe4e5f71 100644 --- a/src/com/android/settings/widget/RadioButtonPreference.java +++ b/src/com/android/settings/widget/RadioButtonPreference.java @@ -44,6 +44,8 @@ public class RadioButtonPreference extends CheckBoxPreference { } private OnClickListener mListener = null; + private View appendix; + private int appendixVisibility = -1; public RadioButtonPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); @@ -81,6 +83,10 @@ public class RadioButtonPreference extends CheckBoxPreference { if (summaryContainer != null) { summaryContainer.setVisibility( TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE); + appendix = view.findViewById(R.id.appendix); + if (appendix != null && appendixVisibility != -1) { + appendix.setVisibility(appendixVisibility); + } } TextView title = (TextView) view.findViewById(android.R.id.title); @@ -89,4 +95,11 @@ public class RadioButtonPreference extends CheckBoxPreference { title.setMaxLines(3); } } + + public void setAppendixVisibility(int visibility) { + if (appendix != null) { + appendix.setVisibility(visibility); + } + appendixVisibility = visibility; + } } 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 5a92ce7ee2b..5d2ac62c3b6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java @@ -86,7 +86,7 @@ public class AutoBatterySeekBarPreferenceControllerTest { mController.updateState(mPreference); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("At 20%"); + assertThat(mPreference.getTitle()).isEqualTo("20%"); assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL / INTERVAL); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java new file mode 100644 index 00000000000..c4d4c87b625 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java @@ -0,0 +1,94 @@ +/* + * 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.fuelgauge.batterysaver; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.os.PowerManager; +import android.provider.Settings; +import android.provider.Settings.Global; +import androidx.preference.Preference; +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowResources; +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; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = SettingsShadowResources.class) +public class BatterySaverSchedulePreferenceControllerTest { + + private static final int TRIGGER_LEVEL = 20; + private static final int DEFAULT_LEVEL = 15; + + private BatterySaverSchedulePreferenceController mController; + private Context mContext; + private Preference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + SettingsShadowResources.overrideResource( + com.android.internal.R.integer.config_lowBatteryWarningLevel, DEFAULT_LEVEL); + mContext = RuntimeEnvironment.application; + mController = new BatterySaverSchedulePreferenceController(mContext); + mPreference = new Preference(mContext); + mController.mBatterySaverSchedulePreference = mPreference; + } + + @Test + public void testPreference_lowPowerLevelZero_percentageMode_summaryNoSchedule() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + Settings.Global.putInt(mContext.getContentResolver(), + Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE); + + mController.updateState(mPreference); + + assertThat(mPreference.getSummary()).isEqualTo("No schedule"); + } + + @Test + public void testPreference_lowPowerLevelNonZero_percentageMode_summaryPercentage() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL); + Settings.Global.putInt(mContext.getContentResolver(), + Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE); + + mController.updateState(mPreference); + + assertThat(mPreference.getSummary()).isEqualTo("Will turn on at 20%"); + } + + @Test + public void testPreference_percentageRoutine_summaryRoutine() { + // It doesn't matter what this is set to for routine mode + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL); + Settings.Global.putInt(mContext.getContentResolver(), + Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_DYNAMIC); + + mController.updateState(mPreference); + + assertThat(mPreference.getSummary()).isEqualTo("Based on your routine"); + } +} diff --git a/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java index 5d914a2f273..8a6aabf5d4a 100644 --- a/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.Application; +import android.view.LayoutInflater; import android.view.View; import androidx.preference.PreferenceViewHolder; @@ -93,4 +94,13 @@ public class RadioButtonPreferenceTest { mPreference.onBindViewHolder(preferenceViewHolder); assertEquals(View.GONE, summaryContainer.getVisibility()); } + + @Test + public void hideAppendix_shouldBeGone() { + mPreference.setAppendixVisibility(View.GONE); + View view = LayoutInflater.from(mContext).inflate(R.layout.preference_radio, null); + PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view); + mPreference.onBindViewHolder(holder); + assertThat(holder.findViewById(R.id.appendix).getVisibility()).isEqualTo(View.GONE); + } }