From c8bff87a9f1d908fc0af2646ab1912beafc9578c Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Mon, 18 Sep 2017 16:16:57 -0700 Subject: [PATCH 01/17] Turn off WifiTetherSettings page. Merged-In: If92798ddf075801e66cd97ecacff2f8df1e41985 Change-Id: Idab770509a8049907a256cce2b8f00f236c2dbdc Fixes: 65852564 Test: rerun robotests Test: new instrumentation test to check flag initial state --- src/com/android/settings/wifi/WifiApEnabler.java | 2 +- .../settings/wifi/tether/WifiTetherSettings.java | 2 +- .../wifi/tether/WifiTetherSettingsTest.java | 16 +++++++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/wifi/WifiApEnabler.java b/src/com/android/settings/wifi/WifiApEnabler.java index 675bf285e48..2a8015b074a 100644 --- a/src/com/android/settings/wifi/WifiApEnabler.java +++ b/src/com/android/settings/wifi/WifiApEnabler.java @@ -81,7 +81,7 @@ public class WifiApEnabler { mContext = context; mDataSaverBackend = dataSaverBackend; mSwitch = switchPreference; - mOriginalSummary = switchPreference.getSummary(); + mOriginalSummary = context.getText(R.string.wifi_hotspot_off_subtext); switchPreference.setPersistent(false); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 5dcdeabe252..3a1d5c60ec1 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -46,7 +46,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment implements WifiTetherBasePreferenceController.OnTetherConfigUpdateListener { public static boolean isTetherSettingPageEnabled() { - return SystemProperties.getBoolean("settings.ui.wifi.tether.enabled", true); + return SystemProperties.getBoolean("settings.ui.wifi.tether.enabled", false); } private static final IntentFilter TETHER_STATE_CHANGE_FILTER; diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java index 26a711b863e..add988cc354 100644 --- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -16,6 +16,12 @@ package com.android.settings.wifi.tether; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static com.google.common.truth.Truth.assertThat; + import android.app.Instrumentation; import android.content.Intent; import android.support.test.InstrumentationRegistry; @@ -33,11 +39,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withText; - @RunWith(AndroidJUnit4.class) @SmallTest public class WifiTetherSettingsTest { @@ -63,6 +64,11 @@ public class WifiTetherSettingsTest { mDevice.pressHome(); } + @Test + public void verifyPageIsDisabledByDefault() { + assertThat(WifiTetherSettings.isTetherSettingPageEnabled()).isFalse(); + } + @Test public void launchTetherSettings_shouldHaveAllFields() { launchWifiTetherActivity(); From c6c299bdbba614a4b792f74b9b3cb5f7471edbe0 Mon Sep 17 00:00:00 2001 From: Christine Franks Date: Fri, 25 Aug 2017 14:02:42 -0700 Subject: [PATCH 02/17] Handle night display state when timezone changes Bug: 64458884 Test: make RunSettingsRoboTests -j100 Change-Id: I1f0c073b796aa6826c236757e19c6ff6d5cb7602 --- .../SuggestionFeatureProviderImpl.java | 5 ++-- .../display/NightDisplayPreference.java | 11 ++++---- .../display/NightDisplaySettings.java | 18 ++++++------ .../SuggestionFeatureProviderImplTest.java | 28 +++++++++++++++++-- .../shadow/ShadowSecureSettings.java | 6 +++- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java index f8b5a8b6a60..5764171b750 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java @@ -161,8 +161,7 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider @VisibleForTesting boolean hasUsedNightDisplay(Context context) { final ContentResolver cr = context.getContentResolver(); - final long lastActivatedTimeMillis = Secure.getLong(cr, - Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1); - return lastActivatedTimeMillis > 0; + return Secure.getInt(cr, Secure.NIGHT_DISPLAY_AUTO_MODE, 0) != 0 + || Secure.getString(cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME) != null; } } diff --git a/src/com/android/settings/display/NightDisplayPreference.java b/src/com/android/settings/display/NightDisplayPreference.java index 38b57a2460b..b966530d9f1 100644 --- a/src/com/android/settings/display/NightDisplayPreference.java +++ b/src/com/android/settings/display/NightDisplayPreference.java @@ -22,6 +22,7 @@ import com.android.internal.app.NightDisplayController; import com.android.settings.R; import java.text.DateFormat; +import java.time.LocalTime; import java.util.Calendar; import java.util.TimeZone; @@ -58,11 +59,11 @@ public class NightDisplayPreference extends SwitchPreference mController.setListener(null); } - private String getFormattedTimeString(NightDisplayController.LocalTime localTime) { + private String getFormattedTimeString(LocalTime localTime) { final Calendar c = Calendar.getInstance(); c.setTimeZone(mTimeFormatter.getTimeZone()); - c.set(Calendar.HOUR_OF_DAY, localTime.hourOfDay); - c.set(Calendar.MINUTE, localTime.minute); + c.set(Calendar.HOUR_OF_DAY, localTime.getHour()); + c.set(Calendar.MINUTE, localTime.getMinute()); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return mTimeFormatter.format(c.getTime()); @@ -116,12 +117,12 @@ public class NightDisplayPreference extends SwitchPreference } @Override - public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) { + public void onCustomStartTimeChanged(LocalTime startTime) { updateSummary(); } @Override - public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) { + public void onCustomEndTimeChanged(LocalTime endTime) { updateSummary(); } } diff --git a/src/com/android/settings/display/NightDisplaySettings.java b/src/com/android/settings/display/NightDisplaySettings.java index 23ddf078ac4..f9568fb6f50 100644 --- a/src/com/android/settings/display/NightDisplaySettings.java +++ b/src/com/android/settings/display/NightDisplaySettings.java @@ -32,6 +32,7 @@ import com.android.settings.widget.SeekBarPreference; import com.android.settings.SettingsPreferenceFragment; import java.text.DateFormat; +import java.time.LocalTime; import java.util.Calendar; import java.util.TimeZone; @@ -144,7 +145,7 @@ public class NightDisplaySettings extends SettingsPreferenceFragment @Override public Dialog onCreateDialog(final int dialogId) { if (dialogId == DIALOG_START_TIME || dialogId == DIALOG_END_TIME) { - final NightDisplayController.LocalTime initialTime; + final LocalTime initialTime; if (dialogId == DIALOG_START_TIME) { initialTime = mController.getCustomStartTime(); } else { @@ -156,15 +157,14 @@ public class NightDisplaySettings extends SettingsPreferenceFragment return new TimePickerDialog(context, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { - final NightDisplayController.LocalTime time = - new NightDisplayController.LocalTime(hourOfDay, minute); + final LocalTime time = LocalTime.of(hourOfDay, minute); if (dialogId == DIALOG_START_TIME) { mController.setCustomStartTime(time); } else { mController.setCustomEndTime(time); } } - }, initialTime.hourOfDay, initialTime.minute, use24HourFormat); + }, initialTime.getHour(), initialTime.getMinute(), use24HourFormat); } return super.onCreateDialog(dialogId); } @@ -201,11 +201,11 @@ public class NightDisplaySettings extends SettingsPreferenceFragment mTemperaturePreference.setProgress(convertTemperature(colorTemperature)); } - private String getFormattedTimeString(NightDisplayController.LocalTime localTime) { + private String getFormattedTimeString(LocalTime localTime) { final Calendar c = Calendar.getInstance(); c.setTimeZone(mTimeFormatter.getTimeZone()); - c.set(Calendar.HOUR_OF_DAY, localTime.hourOfDay); - c.set(Calendar.MINUTE, localTime.minute); + c.set(Calendar.HOUR_OF_DAY, localTime.getHour()); + c.set(Calendar.MINUTE, localTime.getMinute()); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return mTimeFormatter.format(c.getTime()); @@ -221,12 +221,12 @@ public class NightDisplaySettings extends SettingsPreferenceFragment } @Override - public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) { + public void onCustomStartTimeChanged(LocalTime startTime) { mStartTimePreference.setSummary(getFormattedTimeString(startTime)); } @Override - public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) { + public void onCustomEndTimeChanged(LocalTime endTime) { mEndTimePreference.setSummary(getFormattedTimeString(endTime)); } diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java index 45d04a45657..8246fda9f36 100644 --- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java @@ -56,6 +56,7 @@ import com.android.settings.testutils.shadow.ShadowSecureSettings; import com.android.settingslib.drawer.Tile; import com.android.settingslib.suggestions.SuggestionParser; +import java.time.LocalDateTime; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -391,14 +392,35 @@ public class SuggestionFeatureProviderImplTest { } @Test - public void hasUsedNightDisplay_returnsTrue_ifPreviouslyActivated() { - Secure.putLong(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 1L); + public void hasUsedNightDisplay_returnsTrue_ifPreviouslyActivatedAndManual() { + Secure.putString(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, + LocalDateTime.now().toString()); + Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, 1); assertThat(mProvider.hasUsedNightDisplay(mContext)).isTrue(); } @Test public void nightDisplaySuggestion_isCompleted_ifPreviouslyActivated() { - Secure.putLong(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 1L); + Secure.putString(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, + LocalDateTime.now().toString()); + final ComponentName componentName = + new ComponentName(mContext, NightDisplaySuggestionActivity.class); + assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isTrue(); + } + + @Test + public void nightDisplaySuggestion_isCompleted_ifNonManualMode() { + Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, 1); + final ComponentName componentName = + new ComponentName(mContext, NightDisplaySuggestionActivity.class); + assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isTrue(); + } + + @Test + public void nightDisplaySuggestion_isCompleted_ifPreviouslyCleared() { + Secure.putString(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, + null); + Secure.putInt(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, 1); final ComponentName componentName = new ComponentName(mContext, NightDisplaySuggestionActivity.class); assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isTrue(); diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java index 0f61a5d816e..8b2a27bd1fa 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java @@ -36,7 +36,11 @@ public class ShadowSecureSettings { int userHandle) { final Table userTable = getUserTable(resolver); synchronized (userTable) { - userTable.put(userHandle, name, value); + if (value != null) { + userTable.put(userHandle, name, value); + } else { + userTable.remove(userHandle, name); + } return true; } } From 223484ea3cf8dead9abcda6a07d0224192be7820 Mon Sep 17 00:00:00 2001 From: jeffreyhuang Date: Fri, 15 Sep 2017 16:01:34 -0700 Subject: [PATCH 03/17] Introduce OemUnlockPreferenceController - Create new OemUnlockPreferenceController - Add new onActivityResult method in DeveloperOptionsController - Create controller inside the DashboardFragment - Port logic from DevelopmentSettings into the controller Bug: 34203528 Test: make RunSettingsRoboTests -j40 Change-Id: I0b1387b9547e7c9f2a1a0963421d0ebea55d9ff4 --- .../DeveloperOptionsPreferenceController.java | 19 +- ...evelopmentOptionsActivityRequestCodes.java | 24 ++ .../DevelopmentSettingsDashboardFragment.java | 47 +++- .../EnableOemUnlockSettingWarningDialog.java | 82 +++++++ .../development/OemUnlockDialogHost.java | 33 +++ .../OemUnlockPreferenceController.java | 220 ++++++++++++++++++ ...elopmentSettingsDashboardFragmentTest.java | 22 ++ .../OemUnlockPreferenceControllerTest.java | 201 ++++++++++++++++ 8 files changed, 642 insertions(+), 6 deletions(-) create mode 100644 src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java create mode 100644 src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java create mode 100644 src/com/android/settings/development/OemUnlockDialogHost.java create mode 100644 src/com/android/settings/development/OemUnlockPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java diff --git a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java index a7d45ebba8e..7715c254fcb 100644 --- a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java +++ b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java @@ -17,6 +17,7 @@ package com.android.settings.development; import android.content.Context; +import android.content.Intent; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.AbstractPreferenceController; @@ -34,13 +35,29 @@ public abstract class DeveloperOptionsPreferenceController extends super(context); } + /** + * Called when an activity returns to the DeveloperSettingsDashboardFragment. + * + * @param requestCode The integer request code originally supplied to + * startActivityForResult(), allowing you to identify who this + * result came from. + * @param resultCode The integer result code returned by the child activity + * through its setResult(). + * @param data An Intent, which can return result data to the caller + * (various data can be attached to Intent "extras"). + * @return true if the controller handled the activity result + */ + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + return false; + } + /** * Called when developer options is enabled */ public abstract void onDeveloperOptionsEnabled(); /** - *Called when developer options is disabled + * Called when developer options is disabled */ public abstract void onDeveloperOptionsDisabled(); } diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java new file mode 100644 index 00000000000..54d1fa39d23 --- /dev/null +++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +/** + * Interface for storing Activity request codes in development options + */ +public interface DevelopmentOptionsActivityRequestCodes { + int REQUEST_CODE_ENABLE_OEM_UNLOCK = 0; +} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 8df25c227c3..89723a8eddd 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -16,10 +16,13 @@ package com.android.settings.development; +import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.os.UserManager; import android.provider.SearchIndexableResource; +import android.support.annotation.VisibleForTesting; import android.util.Log; import android.widget.Switch; @@ -40,7 +43,7 @@ import java.util.Arrays; import java.util.List; public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFragment - implements SwitchBar.OnSwitchChangeListener { + implements SwitchBar.OnSwitchChangeListener, OemUnlockDialogHost { private static final String TAG = "DevSettingsDashboard"; @@ -103,6 +106,33 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra } } + @Override + public void onOemUnlockDialogConfirmed() { + final OemUnlockPreferenceController controller = getDevelopmentOptionsController( + OemUnlockPreferenceController.class); + controller.onOemUnlockConfirmed(); + } + + @Override + public void onOemUnlockDialogDismissed() { + final OemUnlockPreferenceController controller = getDevelopmentOptionsController( + OemUnlockPreferenceController.class); + controller.onOemUnlockDismissed(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + for (AbstractPreferenceController controller : mPreferenceControllers) { + if (controller instanceof DeveloperOptionsPreferenceController) { + if (((DeveloperOptionsPreferenceController) controller).onActivityResult( + requestCode, resultCode, data)) { + return; + } + } + } + super.onActivityResult(requestCode, resultCode, data); + } + @Override protected String getLogTag() { return TAG; @@ -121,7 +151,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override protected List getPreferenceControllers(Context context) { - mPreferenceControllers = buildPreferenceControllers(context, getLifecycle()); + mPreferenceControllers = buildPreferenceControllers(context, getActivity(), getLifecycle(), + this /* devOptionsDashboardFragment */); return mPreferenceControllers; } @@ -140,14 +171,19 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra } private static List buildPreferenceControllers(Context context, - Lifecycle lifecycle) { + Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment) { final List controllers = new ArrayList<>(); controllers.add(new StayAwakePreferenceController(context, lifecycle)); controllers.add(new BluetoothSnoopLogPreferenceController(context)); - + controllers.add(new OemUnlockPreferenceController(context, activity, fragment)); return controllers; } + @VisibleForTesting + T getDevelopmentOptionsController(Class clazz) { + return getPreferenceController(clazz); + } + /** * For Search. */ @@ -171,7 +207,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override public List getPreferenceControllers(Context context) { - return buildPreferenceControllers(context, null /* lifecycle */); + return buildPreferenceControllers(context, null /* activity */, + null /* lifecycle */, null /* devOptionsDashboardFragment */); } }; } diff --git a/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java b/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java new file mode 100644 index 00000000000..2486ef53589 --- /dev/null +++ b/src/com/android/settings/development/EnableOemUnlockSettingWarningDialog.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.Fragment; +import android.app.FragmentManager; +import android.content.DialogInterface; +import android.os.Bundle; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; + +public class EnableOemUnlockSettingWarningDialog extends InstrumentedDialogFragment implements + DialogInterface.OnClickListener, DialogInterface.OnDismissListener { + + public static final String TAG = "EnableOemUnlockDlg"; + + public static void show(Fragment host) { + final FragmentManager manager = host.getActivity().getFragmentManager(); + if (manager.findFragmentByTag(TAG) == null) { + final EnableOemUnlockSettingWarningDialog dialog = + new EnableOemUnlockSettingWarningDialog(); + dialog.setTargetFragment(host, 0 /* requestCode */); + dialog.show(manager, TAG); + } + } + + @Override + public int getMetricsCategory() { + return MetricsProto.MetricsEvent.DIALOG_ENABLE_OEM_UNLOCKING; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.confirm_enable_oem_unlock_title) + .setMessage(R.string.confirm_enable_oem_unlock_text) + .setPositiveButton(R.string.enable_text, this /* onClickListener */) + .setNegativeButton(android.R.string.cancel, this /* onClickListener */) + .create(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + final OemUnlockDialogHost host = (OemUnlockDialogHost) getTargetFragment(); + if (host == null) { + return; + } + if (which == DialogInterface.BUTTON_POSITIVE) { + host.onOemUnlockDialogConfirmed(); + } else { + host.onOemUnlockDialogDismissed(); + } + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + final OemUnlockDialogHost host = (OemUnlockDialogHost) getTargetFragment(); + if (host == null) { + return; + } + host.onOemUnlockDialogDismissed(); + } +} diff --git a/src/com/android/settings/development/OemUnlockDialogHost.java b/src/com/android/settings/development/OemUnlockDialogHost.java new file mode 100644 index 00000000000..c134e9c2ea2 --- /dev/null +++ b/src/com/android/settings/development/OemUnlockDialogHost.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +/** + * Interface for OemUnlockDialogFragment callbacks. + */ +public interface OemUnlockDialogHost { + + /** + * Called when the user presses enable on the warning dialog. + */ + void onOemUnlockDialogConfirmed(); + + /** + * Called when the user dismisses or cancels the warning dialog. + */ + void onOemUnlockDialogDismissed(); +} diff --git a/src/com/android/settings/development/OemUnlockPreferenceController.java b/src/com/android/settings/development/OemUnlockPreferenceController.java new file mode 100644 index 00000000000..7d85d2e20f5 --- /dev/null +++ b/src/com/android/settings/development/OemUnlockPreferenceController.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes + .REQUEST_CODE_ENABLE_OEM_UNLOCK; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.UserHandle; +import android.os.UserManager; +import android.service.oemlock.OemLockManager; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.telephony.TelephonyManager; + +import com.android.settings.R; +import com.android.settings.password.ChooseLockSettingsHelper; +import com.android.settingslib.RestrictedSwitchPreference; + +public class OemUnlockPreferenceController extends DeveloperOptionsPreferenceController implements + Preference.OnPreferenceChangeListener { + + private static final String PREFERENCE_KEY = "oem_unlock_enable"; + + private final OemLockManager mOemLockManager; + private final UserManager mUserManager; + private final TelephonyManager mTelephonyManager; + private final DevelopmentSettingsDashboardFragment mFragment; + private final ChooseLockSettingsHelper mChooseLockSettingsHelper; + private RestrictedSwitchPreference mPreference; + + public OemUnlockPreferenceController(Context context, Activity activity, + DevelopmentSettingsDashboardFragment fragment) { + super(context); + mOemLockManager = (OemLockManager) context.getSystemService(Context.OEM_LOCK_SERVICE); + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + mFragment = fragment; + if (activity != null || mFragment != null) { + mChooseLockSettingsHelper = new ChooseLockSettingsHelper(activity, mFragment); + } else { + mChooseLockSettingsHelper = null; + } + } + + @Override + public boolean isAvailable() { + return mOemLockManager != null; + } + + @Override + public String getPreferenceKey() { + return PREFERENCE_KEY; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPreference = (RestrictedSwitchPreference) screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean isUnlocked = (Boolean) newValue; + if (isUnlocked) { + if (!showKeyguardConfirmation(mContext.getResources(), + REQUEST_CODE_ENABLE_OEM_UNLOCK)) { + confirmEnableOemUnlock(); + } + } else { + mOemLockManager.setOemUnlockAllowedByUser(false); + } + return true; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + mPreference.setChecked(mOemLockManager.isOemUnlockAllowed()); + updateOemUnlockSettingDescription(); + // Showing mEnableOemUnlock preference as device has persistent data block. + mPreference.setDisabledByAdmin(null); + mPreference.setEnabled(enableOemUnlockPreference()); + if (mPreference.isEnabled()) { + // Check restriction, disable mEnableOemUnlock and apply policy transparency. + mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET); + } + } + + @Override + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_ENABLE_OEM_UNLOCK) { + if (resultCode == Activity.RESULT_OK) { + if (mPreference.isChecked()) { + confirmEnableOemUnlock(); + } else { + mOemLockManager.setOemUnlockAllowedByUser(false); + } + } + return true; + } + return false; + } + + @Override + public void onDeveloperOptionsEnabled() { + handleDeveloperOptionsToggled(); + } + + @Override + public void onDeveloperOptionsDisabled() { + handleDeveloperOptionsToggled(); + } + + public void onOemUnlockConfirmed() { + mOemLockManager.setOemUnlockAllowedByUser(true); + } + + public void onOemUnlockDismissed() { + if (mPreference == null) { + return; + } + updateState(mPreference); + } + + private void handleDeveloperOptionsToggled() { + if (mPreference == null) { + return; + } + + mPreference.setEnabled(enableOemUnlockPreference()); + if (mPreference.isEnabled()) { + // Check restriction, disable mEnableOemUnlock and apply policy transparency. + mPreference.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET); + } + } + + private void updateOemUnlockSettingDescription() { + int oemUnlockSummary = R.string.oem_unlock_enable_summary; + if (isBootloaderUnlocked()) { + oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_bootloader_unlocked; + } else if (isSimLockedDevice()) { + oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_sim_locked_device; + } else if (!isOemUnlockAllowedByUserAndCarrier()) { + // If the device isn't SIM-locked but OEM unlock is disallowed by some party, this + // means either some other carrier restriction is in place or the device hasn't been + // able to confirm which restrictions (SIM-lock or otherwise) apply. + oemUnlockSummary = + R.string.oem_unlock_enable_disabled_summary_connectivity_or_locked; + } + mPreference.setSummary(mContext.getResources().getString(oemUnlockSummary)); + } + + /** Returns {@code true} if the device is SIM-locked. Otherwise, returns {@code false}. */ + private boolean isSimLockedDevice() { + int phoneCount = mTelephonyManager.getPhoneCount(); + for (int i = 0; i < phoneCount; i++) { + if (mTelephonyManager.getAllowedCarriers(i).size() > 0) { + return true; + } + } + return false; + } + + /** + * Returns {@code true} if the bootloader has been unlocked. Otherwise, returns {code false}. + */ + private boolean isBootloaderUnlocked() { + return mOemLockManager.isDeviceOemUnlocked(); + } + + private boolean enableOemUnlockPreference() { + return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier(); + } + + + @VisibleForTesting + boolean showKeyguardConfirmation(Resources resources, int requestCode) { + return mChooseLockSettingsHelper.launchConfirmationActivity( + requestCode, resources.getString(R.string.oem_unlock_enable)); + } + + @VisibleForTesting + void confirmEnableOemUnlock() { + EnableOemUnlockSettingWarningDialog.show(mFragment); + } + + /** + * Returns whether OEM unlock is allowed by the user and carrier. + * + * This does not take into account any restrictions imposed by the device policy. + */ + @VisibleForTesting + boolean isOemUnlockAllowedByUserAndCarrier() { + final UserHandle userHandle = UserHandle.of(UserHandle.myUserId()); + return mOemLockManager.isOemUnlockAllowedByCarrier() + && !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, + userHandle); + } + +} diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java index a001aafcdb1..c8748deeac3 100644 --- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java @@ -17,7 +17,11 @@ package com.android.settings.development; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -161,6 +165,24 @@ public class DevelopmentSettingsDashboardFragmentTest { .isFalse(); } + @Test + public void onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed() { + final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class); + doReturn(controller).when(mDashboard).getDevelopmentOptionsController( + OemUnlockPreferenceController.class); + mDashboard.onOemUnlockDialogConfirmed(); + verify(controller).onOemUnlockConfirmed(); + } + + @Test + public void onOemUnlockDialogConfirmed_shouldCallControllerOemDismissed() { + final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class); + doReturn(controller).when(mDashboard).getDevelopmentOptionsController( + OemUnlockPreferenceController.class); + mDashboard.onOemUnlockDialogDismissed(); + verify(controller).onOemUnlockDismissed(); + } + @Implements(EnableDevelopmentSettingWarningDialog.class) public static class ShadowEnableDevelopmentSettingWarningDialog { diff --git a/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java new file mode 100644 index 00000000000..13678703bd8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes + .REQUEST_CODE_ENABLE_OEM_UNLOCK; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.os.UserManager; +import android.service.oemlock.OemLockManager; +import android.support.v7.preference.PreferenceScreen; +import android.telephony.TelephonyManager; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.RestrictedSwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class OemUnlockPreferenceControllerTest { + + @Mock + private Context mContext; + @Mock + private Activity mActivity; + @Mock + private DevelopmentSettingsDashboardFragment mFragment; + @Mock + private RestrictedSwitchPreference mPreference; + @Mock + private PreferenceScreen mPreferenceScreen; + @Mock + private OemLockManager mOemLockManager; + @Mock + private UserManager mUserManager; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private Resources mResources; + private OemUnlockPreferenceController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.OEM_LOCK_SERVICE)).thenReturn(mOemLockManager); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager); + when(mContext.getResources()).thenReturn(mResources); + mController = new OemUnlockPreferenceController(mContext, mActivity, mFragment); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( + mPreference); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void isAvailable_shouldReturnTrueWhenOemLockManagerIsNotNull() { + boolean returnValue = mController.isAvailable(); + + assertThat(returnValue).isTrue(); + } + + @Test + public void isAvailable_shouldReturnFalseWhenOemLockManagerIsNull() { + when(mContext.getSystemService(Context.OEM_LOCK_SERVICE)).thenReturn(null); + mController = new OemUnlockPreferenceController(mContext, mActivity, mFragment); + boolean returnValue = mController.isAvailable(); + + assertThat(returnValue).isFalse(); + } + + @Test + public void onPreferenceChanged_turnOnUnlock() { + mController = spy(mController); + doReturn(false).when(mController).showKeyguardConfirmation(mResources, + REQUEST_CODE_ENABLE_OEM_UNLOCK); + doNothing().when(mController).confirmEnableOemUnlock(); + mController.onPreferenceChange(null, true); + + verify(mController).confirmEnableOemUnlock(); + } + + @Test + public void onPreferenceChanged_turnOffUnlock() { + mController.onPreferenceChange(null, false); + + verify(mOemLockManager).setOemUnlockAllowedByUser(false); + } + + @Test + public void updateState_preferenceShouldBeCheckedAndShouldBeDisabled() { + mController = spy(mController); + when(mOemLockManager.isOemUnlockAllowed()).thenReturn(true); + doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier(); + when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(true); + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + verify(mPreference).setEnabled(false); + } + + @Test + public void updateState_preferenceShouldBeUncheckedAndShouldBeDisabled() { + mController = spy(mController); + when(mOemLockManager.isOemUnlockAllowed()).thenReturn(false); + doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier(); + when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(true); + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + verify(mPreference).setEnabled(false); + } + + @Test + public void updateState_preferenceShouldBeCheckedAndShouldBeEnabled() { + mController = spy(mController); + when(mOemLockManager.isOemUnlockAllowed()).thenReturn(true); + doReturn(true).when(mController).isOemUnlockAllowedByUserAndCarrier(); + when(mOemLockManager.isDeviceOemUnlocked()).thenReturn(false); + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + verify(mPreference).setEnabled(true); + } + + @Test + public void onActivityResult_shouldReturnTrue() { + final boolean result = mController.onActivityResult(REQUEST_CODE_ENABLE_OEM_UNLOCK, + Activity.RESULT_OK, null); + + assertThat(result).isTrue(); + } + + @Test + public void onActivityResult_shouldReturnFalse() { + final boolean result = mController.onActivityResult(123454, + 1434, null); + + assertThat(result).isFalse(); + } + + @Test + public void onDeveloperOptionsEnabled_preferenceShouldCheckRestriction() { + mController = spy(mController); + doReturn(false).when(mController).isOemUnlockAllowedByUserAndCarrier(); + when(mPreference.isEnabled()).thenReturn(true); + mController.onDeveloperOptionsEnabled(); + + verify(mPreference).checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET); + + } + + @Test + public void onDeveloperOptionsDisabled_preferenceShouldCheckRestriction() { + mController = spy(mController); + doReturn(false).when(mController).isOemUnlockAllowedByUserAndCarrier(); + when(mPreference.isEnabled()).thenReturn(true); + mController.onDeveloperOptionsDisabled(); + + verify(mPreference).checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET); + + } + + @Test + public void onOemUnlockConfirmed_oemManagerShouldSetUnlockAllowedByUser() { + mController.onOemUnlockConfirmed(); + + verify(mOemLockManager).setOemUnlockAllowedByUser(true); + } +} From 52f4ed8204e818885470a2e169608ecd1cc83ace Mon Sep 17 00:00:00 2001 From: jeffreyhuang Date: Mon, 18 Sep 2017 14:29:24 -0700 Subject: [PATCH 04/17] Add comments to avoid merge conflicts - These comments will be changed to controllers.add(...) - avoid merge conflicts when adding controller buildPreferenceControllers() Bug: 34203528 Test: make RunSettingsRoboTests -j40 Change-Id: I43f6b9ac41291f67626ba5d6f3ab3917c9c5e784 --- .../DevelopmentSettingsDashboardFragment.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 89723a8eddd..06623086b25 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -173,9 +173,82 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra private static List buildPreferenceControllers(Context context, Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment) { final List controllers = new ArrayList<>(); + // take bug report + // desktop backup password controllers.add(new StayAwakePreferenceController(context, lifecycle)); + // hdcp checking controllers.add(new BluetoothSnoopLogPreferenceController(context)); controllers.add(new OemUnlockPreferenceController(context, activity, fragment)); + // running services + // convert to file encryption + // picture color mode + // webview implementation + // cool color temperature + // automatic system updates + // system ui demo mode + // quick settings developer tiles + // usb debugging + // revoke usb debugging authorizations + // local terminal + // bug report shortcut + // select mock location app + // enable view attribute inspection + // select debug app + // wait for debugger + // verify apps over usb + // logger buffer sizes + // store logger data persistently on device + // telephony monitor + // camera laser sensor + // camera HAL HDR+ + // feature flags + // wireless display certification + // enable wi-fi verbose logging + // aggressive wifi to mobile handover + // always allow wifi roam scans + // mobile always active + // tethering hardware acceleration + // select usb configuration + // show bluetooth devices without names + // disable absolute volume + // enable in-band ringing + // bluetooth avrcp version + // bluetooth audio codec + // bluetooth audio sample rate + // bluetooth audio bits per sample + // bluetooth audio channel mode + // bluetooth audio ldac codec: playback quality + // show taps + // pointer location + // show surface updates + // show layout bounds + // force rtl layout direction + // window animation scale + // transition animation scale + // animator duration scale + // simulate secondary displays + // smallest width + // force gpu rendering + // show gpu view updates + // show hardware layers updates + // debug gpu overdraw + // debug non-rectangular clip operations + // force 4x msaa + // disable hw overlays + // simulate color space + // set gpu renderer + // disable usb audio routing + // strict mode enabled + // profile gpu rendering + // don't keep activities + // background process limit + // background check + // show all anrs + // show notification channel warnings + // inactive apps + // force allow apps on external + // force activities to be resizable + // reset shortcutmanager rate-limiting return controllers; } From a25e54fdf625e9e4e2fe65b3b7ca213743091d0c Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 20 Sep 2017 10:00:03 -0400 Subject: [PATCH 05/17] Sometimes there's no advanced settings Test: manual Change-Id: Iea69ee04f36be51d073bbbd2c0d643c986eb8e89 Fixes: 65964886 --- .../notification/ChannelNotificationSettings.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java index 58599799a09..66e94fa1db4 100644 --- a/src/com/android/settings/notification/ChannelNotificationSettings.java +++ b/src/com/android/settings/notification/ChannelNotificationSettings.java @@ -374,7 +374,9 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { } void updateDependents(boolean banned) { + PreferenceGroup parent; if (mShowLegacyChannelConfig) { + parent = getPreferenceScreen(); setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN)); } else { setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN)); @@ -383,12 +385,13 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight()); setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)); setVisible(mRingtone, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)); + parent = mAdvanced; } - setVisible(mAdvanced, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN)); - setVisible(mAdvanced, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) + setVisible(parent, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN)); + setVisible(parent, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && mDndVisualEffectsSuppressed)); - setVisible(mAdvanced, mVisibilityOverride, isLockScreenSecure() + setVisible(parent, mVisibilityOverride, isLockScreenSecure() &&checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)); setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE); if (mAppLink != null) { From 45424c54648a2f09ae310d44cd06c743107f1554 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 20 Sep 2017 09:01:38 -0700 Subject: [PATCH 06/17] Fix NPE when calling getSuggestionV2 in adapter. Change-Id: I3aa498c2255107ca2e33046f3df629c2ef43ec5c Fixes: 66111737 Test: robotest --- .../suggestions/SuggestionAdapter.java | 6 ++++++ .../suggestions/SuggestionAdapterTest.java | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java index 4852ed9519a..1271e151fa7 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java @@ -222,6 +222,9 @@ public class SuggestionAdapter extends RecyclerView.Adapter public Tile getSuggestion(int position) { final long itemId = getItemId(position); + if (mSuggestions == null) { + return null; + } for (Tile tile : mSuggestions) { if (Objects.hash(tile.title) == itemId) { return tile; @@ -232,6 +235,9 @@ public class SuggestionAdapter extends RecyclerView.Adapter public Suggestion getSuggestionsV2(int position) { final long itemId = getItemId(position); + if (mSuggestionsV2 == null) { + return null; + } for (Suggestion suggestion : mSuggestionsV2) { if (Objects.hash(suggestion.getId()) == itemId) { return suggestion; diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java index 1e4ad79afc5..6b804656c90 100644 --- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java @@ -203,7 +203,7 @@ public class SuggestionAdapterTest { public void onBindViewHolder_v2_itemViewShouldHandleClick() throws PendingIntent.CanceledException { final List packages = makeSuggestionsV2("pkg1"); - setupSuggestions(mActivity, null /* suggestionV1 */ , packages); + setupSuggestions(mActivity, null /* suggestionV1 */, packages); mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0); mSuggestionHolder.itemView.performClick(); @@ -233,6 +233,22 @@ public class SuggestionAdapterTest { assertThat(itemView.getChildCount()).isEqualTo(1); } + @Test + public void getSuggestionsV2_shouldReturnSuggestionWhenMatch() { + final List suggestionsV2 = makeSuggestionsV2("pkg1"); + setupSuggestions(mActivity, null /* suggestionV1 */, suggestionsV2); + + assertThat(mSuggestionAdapter.getSuggestion(0)).isNull(); + assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNotNull(); + + List suggestionsV1 = makeSuggestions("pkg1"); + setupSuggestions(mActivity, suggestionsV1, null /* suggestionV2 */); + + assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNull(); + assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull(); + + } + private void setupSuggestions(Context context, List suggestions, List suggestionsV2) { mSuggestionAdapter = new SuggestionAdapter(context, suggestions, suggestionsV2, From 79f347602186a4057b3f29cab9bed0fd229d3cb1 Mon Sep 17 00:00:00 2001 From: Eric Schwarzenbach Date: Thu, 14 Sep 2017 11:09:21 -0700 Subject: [PATCH 07/17] Add since_visible_millis to Settings action logs. This change adds a new action logging method on MetricsFeatureProvider within Settings. Uses the VisibilityLoggerMixin to calculate the elapsed time since the fragment has been visible. Adds tagged data in MetricsFeatureProvider#action() to store this elapsed time in NOTIFICATION_SINCE_VISIBLE_MILLIS in MetricsEvents. Modifies the LogWriter interface to accept variadic taggedData args in its action() methods. Annotates the old subtype #action() methods as deprecated. EventLogWriter also no longer casts the subtype values to strings before sending them to MetricsLogger, so they are correctly stored as subtype instead of package. Bug: 65371699, 65631695 Test: make RunSettingsRoboTests Change-Id: Iec6a61dd6a49ab237e2bdc4469fed45e89d9c380 --- .../core/InstrumentedPreferenceFragment.java | 4 ++ .../core/instrumentation/EventLogWriter.java | 29 +++++++++++- .../core/instrumentation/LogWriter.java | 14 ++++++ .../MetricsFeatureProvider.java | 42 ++++++++++++++--- .../SettingSuggestionsLogWriter.java | 8 ++++ .../VisibilityLoggerMixin.java | 12 +++++ .../android/settings/wifi/WifiSettings.java | 2 +- .../MetricsFeatureProviderTest.java | 47 ++++++++++++++++--- 8 files changed, 143 insertions(+), 15 deletions(-) diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java index bfb69e78a3b..a5d07156190 100644 --- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java +++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java @@ -65,4 +65,8 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc protected final Context getPrefContext() { return getPreferenceManager().getContext(); } + + protected final VisibilityLoggerMixin getVisibilityLogger() { + return mVisibilityLoggerMixin; + } } diff --git a/src/com/android/settings/core/instrumentation/EventLogWriter.java b/src/com/android/settings/core/instrumentation/EventLogWriter.java index e7628e8b6b7..3196f76b323 100644 --- a/src/com/android/settings/core/instrumentation/EventLogWriter.java +++ b/src/com/android/settings/core/instrumentation/EventLogWriter.java @@ -18,6 +18,7 @@ package com.android.settings.core.instrumentation; import android.content.Context; import android.metrics.LogMaker; +import android.util.Log; import android.util.Pair; import com.android.internal.logging.MetricsLogger; @@ -28,6 +29,8 @@ import com.android.internal.logging.nano.MetricsProto; */ public class EventLogWriter implements LogWriter { + private final MetricsLogger mMetricsLogger = new MetricsLogger(); + public void visible(Context context, int source, int category) { final LogMaker logMaker = new LogMaker(category) .setType(MetricsProto.MetricsEvent.TYPE_OPEN) @@ -39,6 +42,24 @@ public class EventLogWriter implements LogWriter { MetricsLogger.hidden(context, category); } + public void action(int category, int value, Pair... taggedData) { + if (taggedData == null || taggedData.length == 0) { + mMetricsLogger.action(category, value); + } else { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION) + .setSubtype(value); + for (Pair pair : taggedData) { + logMaker.addTaggedData(pair.first, pair.second); + } + mMetricsLogger.write(logMaker); + } + } + + public void action(int category, boolean value, Pair... taggedData) { + action(category, value ? 1 : 0, taggedData); + } + public void action(Context context, int category, Pair... taggedData) { action(context, category, "", taggedData); } @@ -52,12 +73,16 @@ public class EventLogWriter implements LogWriter { MetricsLogger.action(logMaker); } + /** @deprecated use {@link #action(int, int, Pair[])} */ + @Deprecated public void action(Context context, int category, int value) { - MetricsLogger.action(context, category, Integer.toString(value)); + MetricsLogger.action(context, category, value); } + /** @deprecated use {@link #action(int, boolean, Pair[])} */ + @Deprecated public void action(Context context, int category, boolean value) { - MetricsLogger.action(context, category, Boolean.toString(value)); + MetricsLogger.action(context, category, value); } public void action(Context context, int category, String pkg, diff --git a/src/com/android/settings/core/instrumentation/LogWriter.java b/src/com/android/settings/core/instrumentation/LogWriter.java index 584217d85cb..062d46f759f 100644 --- a/src/com/android/settings/core/instrumentation/LogWriter.java +++ b/src/com/android/settings/core/instrumentation/LogWriter.java @@ -33,6 +33,16 @@ public interface LogWriter { */ void hidden(Context context, int category); + /** + * Logs a user action. + */ + void action(int category, int value, Pair... taggedData); + + /** + * Logs a user action. + */ + void action(int category, boolean value, Pair... taggedData); + /** * Logs an user action. */ @@ -45,12 +55,16 @@ public interface LogWriter { /** * Logs an user action. + * @deprecated use {@link #action(int, int, Pair[])} */ + @Deprecated void action(Context context, int category, int value); /** * Logs an user action. + * @deprecated use {@link #action(int, boolean, Pair[])} */ + @Deprecated void action(Context context, int category, boolean value); /** diff --git a/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java index afdec558556..532ec66316f 100644 --- a/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java +++ b/src/com/android/settings/core/instrumentation/MetricsFeatureProvider.java @@ -21,7 +21,7 @@ import android.content.Intent; import android.text.TextUtils; import android.util.Pair; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.ArrayList; import java.util.List; @@ -60,18 +60,44 @@ public class MetricsFeatureProvider { } } + /** + * Logs a user action. Includes the elapsed time since the containing + * fragment has been visible. + */ + public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(category, value, + sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); + } + } + + /** + * Logs a user action. Includes the elapsed time since the containing + * fragment has been visible. + */ + public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(category, value, + sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); + } + } + public void action(Context context, int category, Pair... taggedData) { for (LogWriter writer : mLoggerWriters) { writer.action(context, category, taggedData); } } + /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */ + @Deprecated public void action(Context context, int category, int value) { for (LogWriter writer : mLoggerWriters) { writer.action(context, category, value); } } + /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */ + @Deprecated public void action(Context context, int category, boolean value) { for (LogWriter writer : mLoggerWriters) { writer.action(context, category, value); @@ -99,7 +125,7 @@ public class MetricsFeatureProvider { public int getMetricsCategory(Object object) { if (object == null || !(object instanceof Instrumentable)) { - return MetricsProto.MetricsEvent.VIEW_UNKNOWN; + return MetricsEvent.VIEW_UNKNOWN; } return ((Instrumentable) object).getMetricsCategory(); } @@ -116,15 +142,19 @@ public class MetricsFeatureProvider { // Not loggable return; } - action(context, MetricsProto.MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action, - Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); + action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action, + Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); return; } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) { // Going to a Setting internal page, skip click logging in favor of page's own // visibility logging. return; } - action(context, MetricsProto.MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(), - Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); + action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(), + Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); + } + + private Pair sinceVisibleTaggedData(long timestamp) { + return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp); } } diff --git a/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java b/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java index bbdf8c94983..697f0a3c902 100644 --- a/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java +++ b/src/com/android/settings/core/instrumentation/SettingSuggestionsLogWriter.java @@ -45,6 +45,14 @@ public class SettingSuggestionsLogWriter implements LogWriter { public void actionWithSource(Context context, int source, int category) { } + @Override + public void action(int category, int value, Pair... taggedData) { + } + + @Override + public void action(int category, boolean value, Pair... taggedData) { + } + @Override public void action(Context context, int category, int value) { } diff --git a/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java index 8de35adfd79..2fe2a3beb1d 100644 --- a/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java +++ b/src/com/android/settings/core/instrumentation/VisibilityLoggerMixin.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.os.SystemClock; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.SettingsActivity; import com.android.settings.overlay.FeatureFactory; @@ -41,6 +42,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau private MetricsFeatureProvider mMetricsFeature; private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN; + private long mVisibleTimestamp; public VisibilityLoggerMixin(int metricsCategory) { // MetricsFeature will be set during onAttach. @@ -59,6 +61,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau @Override public void onResume() { + mVisibleTimestamp = SystemClock.elapsedRealtime(); if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory); } @@ -66,6 +69,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau @Override public void onPause() { + mVisibleTimestamp = 0; if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { mMetricsFeature.hidden(null /* context */, mMetricsCategory); } @@ -85,4 +89,12 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau mSourceMetricsCategory = intent.getIntExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, MetricsProto.MetricsEvent.VIEW_UNKNOWN); } + + /** Returns elapsed time since onResume() */ + public long elapsedTimeSinceVisible() { + if (mVisibleTimestamp == 0) { + return 0; + } + return SystemClock.elapsedRealtime() - mVisibleTimestamp; + } } diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index b47391d0dd7..b143f5854de 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -1070,7 +1070,7 @@ public class WifiSettings extends RestrictedSettingsFragment protected void connect(final WifiConfiguration config, boolean isSavedNetwork) { // Log subtype if configuration is a saved network. - mMetricsFeatureProvider.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT, + mMetricsFeatureProvider.action(getVisibilityLogger(), MetricsEvent.ACTION_WIFI_CONNECT, isSavedNetwork); mWifiManager.connect(config, mConnectListener); mClickedConnect = true; diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java index ea33c83391f..ff91c4099d3 100644 --- a/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java +++ b/tests/robotests/src/com/android/settings/core/instrumentation/MetricsFeatureProviderTest.java @@ -28,6 +28,8 @@ import com.android.settings.overlay.FeatureFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @@ -42,24 +44,35 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class MetricsFeatureProviderTest { + private static int CATEGORY = 10; + private static boolean SUBTYPE_BOOLEAN = true; + private static int SUBTYPE_INTEGER = 1; + private static long ELAPSED_TIME = 1000; + + @Mock private LogWriter mockLogWriter; + @Mock private VisibilityLoggerMixin mockVisibilityLogger; - @Mock - private LogWriter mLogWriter; private Context mContext; private MetricsFeatureProvider mProvider; + @Captor + private ArgumentCaptor mPairCaptor; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mProvider = new MetricsFeatureProvider(); List writers = new ArrayList<>(); - writers.add(mLogWriter); + writers.add(mockLogWriter); ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers); + + when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME); } @Test @@ -77,7 +90,7 @@ public class MetricsFeatureProviderTest { mProvider.logDashboardStartIntent(mContext, null /* intent */, MetricsEvent.SETTINGS_GESTURES); - verifyNoMoreInteractions(mLogWriter); + verifyNoMoreInteractions(mockLogWriter); } @Test @@ -86,7 +99,7 @@ public class MetricsFeatureProviderTest { mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); - verify(mLogWriter).action( + verify(mockLogWriter).action( eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), anyString(), @@ -99,10 +112,32 @@ public class MetricsFeatureProviderTest { mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); - verify(mLogWriter).action( + verify(mockLogWriter).action( eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), anyString(), eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); } + + @Test + public void action_BooleanLogsElapsedTime() { + mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN); + verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture()); + + Pair value = mPairCaptor.getValue(); + assertThat(value.first instanceof Integer).isTrue(); + assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); + assertThat(value.second).isEqualTo(ELAPSED_TIME); + } + + @Test + public void action_IntegerLogsElapsedTime() { + mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER); + verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture()); + + Pair value = mPairCaptor.getValue(); + assertThat(value.first instanceof Integer).isTrue(); + assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); + assertThat(value.second).isEqualTo(ELAPSED_TIME); + } } From ac040e3b1fc3488155e5bb337908c491627900bc Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 12 Sep 2017 16:26:06 -0700 Subject: [PATCH 08/17] Bluetooth: remove unnecessary state tracking in BluetoothSummaryUpdater * LocalBluetoothAdapter is already a cache of BluetoothAdapter and its methods should be used directly to obtain states instead of caching them in BluetoothSummaryUpdater - Use LocalBluetoothAdapter.isEnabled() to check whether Bluetooth is enabled - Use LocalBluetoothAdapter.getBondedDevices() to get list of bonded devices * BluetoothDevice is a stable Bluetooth API and its methods should not incur large latency. We should use API methods as much as possible to avoid intermediate wrappers - Use BluetoothDevice.isConnected() to check if a device is connected * Add more logging messages in error conditions * Show status as "Not Connected" when there is a state mismatch (i.e. adapter says it is connected, but no bonded device is connected) * Updated unit tests to reflect the latest behavior Bug: 65591907 Test: make, unit test, pair with Bluetooth devices, check Settings UI Change-Id: I0fa54959c8bed8ac67a935f150785ba8197d0411 --- .../bluetooth/BluetoothSummaryUpdater.java | 68 ++----- .../BluetoothSummaryUpdaterTest.java | 179 +++++++++++------- 2 files changed, 124 insertions(+), 123 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java index 7d2cc181897..662cd701de2 100644 --- a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java +++ b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java @@ -42,31 +42,21 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu private final LocalBluetoothManager mBluetoothManager; private final LocalBluetoothAdapter mBluetoothAdapter; - private boolean mEnabled; - private int mConnectionState; - public BluetoothSummaryUpdater(Context context, OnSummaryChangeListener listener, LocalBluetoothManager bluetoothManager) { super(context, listener); mBluetoothManager = bluetoothManager; mBluetoothAdapter = mBluetoothManager != null - ? mBluetoothManager.getBluetoothAdapter() : null; + ? mBluetoothManager.getBluetoothAdapter() : null; } @Override public void onBluetoothStateChanged(int bluetoothState) { - mEnabled = bluetoothState == BluetoothAdapter.STATE_ON - || bluetoothState == BluetoothAdapter.STATE_TURNING_ON; - if (!mEnabled) { - mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; - } notifyChangeIfNeeded(); } @Override public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { - mConnectionState = state; - updateConnected(); notifyChangeIfNeeded(); } @@ -92,8 +82,6 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu return; } if (listening) { - mEnabled = mBluetoothAdapter.isEnabled(); - mConnectionState = mBluetoothAdapter.getConnectionState(); notifyChangeIfNeeded(); mBluetoothManager.getEventManager().registerCallback(this); } else { @@ -103,10 +91,10 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu @Override public String getSummary() { - if (!mEnabled) { + if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { return mContext.getString(R.string.bluetooth_disabled); } - switch (mConnectionState) { + switch (mBluetoothAdapter.getConnectionState()) { case BluetoothAdapter.STATE_CONNECTED: return getConnectedDeviceSummary(); case BluetoothAdapter.STATE_CONNECTING: @@ -118,50 +106,17 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu } } - private void updateConnected() { - if (mBluetoothAdapter == null) { - return; - } - // Make sure our connection state is up to date. - int state = mBluetoothAdapter.getConnectionState(); - if (state != mConnectionState) { - mConnectionState = state; - return; - } - final Collection devices = getDevices(); - if (devices == null) { - mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; - return; - } - if (mConnectionState == BluetoothAdapter.STATE_CONNECTED) { - CachedBluetoothDevice connectedDevice = null; - for (CachedBluetoothDevice device : devices) { - if (device.isConnected()) { - connectedDevice = device; - break; - } - } - if (connectedDevice == null) { - // If somehow we think we are connected, but have no connected devices, we - // aren't connected. - mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; - } - } - } - - private Collection getDevices() { - return mBluetoothManager != null - ? mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy() - : null; - } - @VisibleForTesting String getConnectedDeviceSummary() { String deviceName = null; int count = 0; final Set devices = mBluetoothAdapter.getBondedDevices(); - if (devices == null || devices.isEmpty()) { - return null; + if (devices == null) { + Log.e(TAG, "getConnectedDeviceSummary, bonded devices are null"); + return mContext.getString(R.string.bluetooth_disabled); + } else if (devices.isEmpty()) { + Log.e(TAG, "getConnectedDeviceSummary, no bonded devices"); + return mContext.getString(R.string.disconnected); } for (BluetoothDevice device : devices) { if (device.isConnected()) { @@ -173,12 +128,13 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu } } if (deviceName == null) { - Log.w(TAG, "getConnectedDeviceSummary, deviceName is null, numBondedDevices=" + Log.e(TAG, "getConnectedDeviceSummary, deviceName is null, numBondedDevices=" + devices.size()); for (BluetoothDevice device : devices) { - Log.w(TAG, "getConnectedDeviceSummary, device=" + device.getName() + "[" + Log.e(TAG, "getConnectedDeviceSummary, device=" + device.getName() + "[" + device.getAddress() + "]" + ", isConnected=" + device.isConnected()); } + return mContext.getString(R.string.disconnected); } return count > 1 ? mContext.getString(R.string.bluetooth_connected_multiple_devices_summary) : mContext.getString(R.string.bluetooth_connected_summary, deviceName); diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java index e3f00d807b6..0c27412c551 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java @@ -18,17 +18,25 @@ package com.android.settings.bluetooth; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; +import android.util.Log; import com.android.settings.R; -import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.junit.Before; import org.junit.Test; @@ -39,19 +47,9 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class BluetoothSummaryUpdaterTest { @@ -70,16 +68,33 @@ public class BluetoothSummaryUpdaterTest { @Mock private SummaryListener mListener; + // Disabled by default + private final boolean[] mAdapterEnabled = {false}; + // Not connected by default + private final int[] mAdapterConnectionState = {BluetoothAdapter.STATE_DISCONNECTED}; + // Not connected by default + private final boolean[] mDeviceConnected = {false, false}; + private final Set mBondedDevices = new HashSet<>(); private BluetoothSummaryUpdater mSummaryUpdater; @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mBluetoothManager.getBluetoothAdapter()).thenReturn(mBtAdapter); - when(mBtAdapter.isEnabled()).thenReturn(true); - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED); mContext = RuntimeEnvironment.application.getApplicationContext(); + doCallRealMethod().when(mListener).onSummaryChanged(anyString()); + // Setup mock adapter + when(mBluetoothManager.getBluetoothAdapter()).thenReturn(mBtAdapter); + doAnswer(invocation -> mAdapterEnabled[0]).when(mBtAdapter).isEnabled(); + doAnswer(invocation -> mAdapterConnectionState[0]).when(mBtAdapter).getConnectionState(); mSummaryUpdater = new BluetoothSummaryUpdater(mContext, mListener, mBluetoothManager); + // Setup first device + doReturn(DEVICE_NAME).when(mConnectedDevice).getName(); + doAnswer(invocation -> mDeviceConnected[0]).when(mConnectedDevice).isConnected(); + // Setup second device + doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName(); + doAnswer(invocation -> mDeviceConnected[1]).when(mConnectedKeyBoardDevice) + .isConnected(); + doReturn(mBondedDevices).when(mBtAdapter).getBondedDevices(); } @Test @@ -98,7 +113,10 @@ public class BluetoothSummaryUpdaterTest { @Test public void register_true_shouldSendSummaryChange() { - prepareConnectedDevice(false); + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED; + mBondedDevices.add(mConnectedDevice); + mDeviceConnected[0] = true; mSummaryUpdater.register(true); @@ -108,7 +126,11 @@ public class BluetoothSummaryUpdaterTest { @Test public void onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary() { - mSummaryUpdater.register(true); + // These states should be ignored + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED; + mBondedDevices.add(mConnectedDevice); + mDeviceConnected[0] = true; + mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF); verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled)); @@ -116,68 +138,83 @@ public class BluetoothSummaryUpdaterTest { @Test public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() { - prepareConnectedDevice(false); + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED; + mBondedDevices.add(mConnectedDevice); + mDeviceConnected[0] = true; - mSummaryUpdater.register(true); mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); verify(mListener).onSummaryChanged( mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME)); } + @Test + public void onBluetoothStateChanged_btEnabled_connectedMisMatch_shouldSendNotConnected() { + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED; + mBondedDevices.add(mConnectedDevice); + // State mismatch + mDeviceConnected[0] = false; + + mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); + } + @Test public void onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage() { - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED); - mSummaryUpdater.register(true); + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTED; + mBondedDevices.add(mConnectedDevice); + // This should be ignored + mDeviceConnected[0] = true; + mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON); - verify(mListener).onSummaryChanged( - mContext.getString(R.string.disconnected)); + verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); } @Test public void onBluetoothStateChanged_ConnectedDisabledEnabled_shouldSendDisconnectedSummary() { - final boolean[] connected = {false}; - final List devices = new ArrayList<>(); - devices.add(mock(CachedBluetoothDevice.class)); - doAnswer(invocation -> connected[0]).when(devices.get(0)).isConnected(); - when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) - .thenReturn(devices); - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED); - prepareConnectedDevice(false); + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTED; + mBondedDevices.add(mConnectedDevice); + mDeviceConnected[0] = false; mSummaryUpdater.register(true); verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); - connected[0] = true; - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED); + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED; + mDeviceConnected[0] = true; mSummaryUpdater.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_CONNECTED); verify(mListener).onSummaryChanged( mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME)); + mAdapterEnabled[0] = false; mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF); verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled)); - connected[0] = false; + // Turning ON means not enabled mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON); + // There should still be only one invocation of disabled message + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled)); + + mAdapterEnabled[0] = true; + mDeviceConnected[0] = false; + mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); verify(mListener, times(2)).onSummaryChanged(mContext.getString(R.string.disconnected)); verify(mListener, times(4)).onSummaryChanged(anyString()); } @Test public void onConnectionStateChanged_connected_shouldSendConnectedMessage() { - final List devices = new ArrayList<>(); - devices.add(mock(CachedBluetoothDevice.class)); - when(devices.get(0).isConnected()).thenReturn(true); - when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) - .thenReturn(devices); - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED); - prepareConnectedDevice(false); + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED; + mBondedDevices.add(mConnectedDevice); + mDeviceConnected[0] = true; - mSummaryUpdater.register(true); - - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED); mSummaryUpdater.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_CONNECTED); @@ -187,7 +224,22 @@ public class BluetoothSummaryUpdaterTest { @Test public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() { - mSummaryUpdater.register(true); + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTED; + mBondedDevices.add(mConnectedDevice); + mDeviceConnected[0] = false; + + mSummaryUpdater.onConnectionStateChanged(null /* device */, + BluetoothAdapter.STATE_CONNECTED); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); + } + + @Test + public void onConnectionStateChanged_noBondedDevice_shouldSendDisconnectedMessage() { + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTED; + mSummaryUpdater.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_CONNECTED); @@ -197,8 +249,10 @@ public class BluetoothSummaryUpdaterTest { @Test public void onConnectionStateChanged_connecting_shouldSendConnectingMessage() { - mSummaryUpdater.register(true); - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING); + // No need for bonded devices + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_CONNECTING; + mSummaryUpdater.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_CONNECTING); @@ -207,8 +261,10 @@ public class BluetoothSummaryUpdaterTest { @Test public void onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage() { - mSummaryUpdater.register(true); - when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTING); + // No need for bonded devices + mAdapterEnabled[0] = true; + mAdapterConnectionState[0] = BluetoothAdapter.STATE_DISCONNECTING; + mSummaryUpdater.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_DISCONNECTING); @@ -217,7 +273,8 @@ public class BluetoothSummaryUpdaterTest { @Test public void getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary() { - prepareConnectedDevice(false); + mBondedDevices.add(mConnectedDevice); + mDeviceConnected[0] = true; final String expectedSummary = mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME); @@ -226,28 +283,16 @@ public class BluetoothSummaryUpdaterTest { @Test public void getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary() { - prepareConnectedDevice(true); + mBondedDevices.add(mConnectedDevice); + mBondedDevices.add(mConnectedKeyBoardDevice); + mDeviceConnected[0] = true; + mDeviceConnected[1] = true; final String expectedSummary = mContext.getString( R.string.bluetooth_connected_multiple_devices_summary); assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary); } - private void prepareConnectedDevice(boolean multipleDevices) { - final Set devices = new HashSet<>(); - doReturn(DEVICE_NAME).when(mConnectedDevice).getName(); - doReturn(true).when(mConnectedDevice).isConnected(); - devices.add(mConnectedDevice); - if (multipleDevices) { - // Add one more device if we need to test multiple devices - doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName(); - doReturn(true).when(mConnectedKeyBoardDevice).isConnected(); - devices.add(mConnectedKeyBoardDevice); - } - - doReturn(devices).when(mBtAdapter).getBondedDevices(); - } - private class SummaryListener implements OnSummaryChangeListener { String summary; From f5539a196995bcee365b9b7786c98b3e4c9feaf6 Mon Sep 17 00:00:00 2001 From: jeffreyhuang Date: Tue, 19 Sep 2017 10:44:30 -0700 Subject: [PATCH 09/17] Add isAvailable check to devOptionsController - This avoids the null check for PreferenceControllers where isAvailable() is not always true Bug: 34203528 Test: make RunSettingsRoboTests -j40 Change-Id: Ibed8bc6a2a812355c521620d77fb571c1fd8a649 --- ...BluetoothSnoopLogPreferenceController.java | 4 +-- .../DeveloperOptionsPreferenceController.java | 23 ++++++++++++++-- .../OemUnlockPreferenceController.java | 8 ++---- .../StayAwakePreferenceController.java | 26 +++++++++---------- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java b/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java index 7734e938b29..44a2ecd19e4 100644 --- a/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java +++ b/src/com/android/settings/development/BluetoothSnoopLogPreferenceController.java @@ -69,12 +69,12 @@ public class BluetoothSnoopLogPreferenceController extends } @Override - public void onDeveloperOptionsEnabled() { + protected void onDeveloperOptionsSwitchEnabled() { mPreference.setEnabled(true); } @Override - public void onDeveloperOptionsDisabled() { + protected void onDeveloperOptionsSwitchDisabled() { SystemProperties.set(BLUETOOTH_BTSNOOP_ENABLE_PROPERTY, Boolean.toString(false)); mPreference.setChecked(false); mPreference.setEnabled(false); diff --git a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java index 7715c254fcb..2f1f2540ed0 100644 --- a/src/com/android/settings/development/DeveloperOptionsPreferenceController.java +++ b/src/com/android/settings/development/DeveloperOptionsPreferenceController.java @@ -54,10 +54,29 @@ public abstract class DeveloperOptionsPreferenceController extends /** * Called when developer options is enabled */ - public abstract void onDeveloperOptionsEnabled(); + public void onDeveloperOptionsEnabled() { + if (isAvailable()) { + onDeveloperOptionsSwitchEnabled(); + } + } /** * Called when developer options is disabled */ - public abstract void onDeveloperOptionsDisabled(); + public void onDeveloperOptionsDisabled() { + if (isAvailable()) { + onDeveloperOptionsSwitchDisabled(); + } + } + + /** + * Called when developer options is enabled and the preference is available + */ + protected abstract void onDeveloperOptionsSwitchEnabled(); + + /** + * Called when developer options is disabled and the preference is available + */ + protected abstract void onDeveloperOptionsSwitchDisabled(); + } diff --git a/src/com/android/settings/development/OemUnlockPreferenceController.java b/src/com/android/settings/development/OemUnlockPreferenceController.java index 7d85d2e20f5..cb391a8e893 100644 --- a/src/com/android/settings/development/OemUnlockPreferenceController.java +++ b/src/com/android/settings/development/OemUnlockPreferenceController.java @@ -122,12 +122,12 @@ public class OemUnlockPreferenceController extends DeveloperOptionsPreferenceCon } @Override - public void onDeveloperOptionsEnabled() { + protected void onDeveloperOptionsSwitchEnabled() { handleDeveloperOptionsToggled(); } @Override - public void onDeveloperOptionsDisabled() { + protected void onDeveloperOptionsSwitchDisabled() { handleDeveloperOptionsToggled(); } @@ -143,10 +143,6 @@ public class OemUnlockPreferenceController extends DeveloperOptionsPreferenceCon } private void handleDeveloperOptionsToggled() { - if (mPreference == null) { - return; - } - mPreference.setEnabled(enableOemUnlockPreference()); if (mPreference.isEnabled()) { // Check restriction, disable mEnableOemUnlock and apply policy transparency. diff --git a/src/com/android/settings/development/StayAwakePreferenceController.java b/src/com/android/settings/development/StayAwakePreferenceController.java index a590d7d66af..ecbb9d0b576 100644 --- a/src/com/android/settings/development/StayAwakePreferenceController.java +++ b/src/com/android/settings/development/StayAwakePreferenceController.java @@ -100,19 +100,6 @@ public class StayAwakePreferenceController extends DeveloperOptionsPreferenceCon mPreference.setChecked(stayAwakeMode != SETTING_VALUE_OFF); } - @Override - public void onDeveloperOptionsEnabled() { - mPreference.setEnabled(true); - } - - @Override - public void onDeveloperOptionsDisabled() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.STAY_ON_WHILE_PLUGGED_IN, SETTING_VALUE_OFF); - mPreference.setChecked(false); - mPreference.setEnabled(false); - } - @Override public void onResume() { if (mPreference != null) { @@ -127,6 +114,19 @@ public class StayAwakePreferenceController extends DeveloperOptionsPreferenceCon } } + @Override + protected void onDeveloperOptionsSwitchEnabled() { + mPreference.setEnabled(true); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, SETTING_VALUE_OFF); + mPreference.setChecked(false); + mPreference.setEnabled(false); + } + @VisibleForTesting RestrictedLockUtils.EnforcedAdmin checkIfMaximumTimeToLockSetByAdmin() { // A DeviceAdmin has specified a maximum time until the device From ebcf01f128422e82bf6bcd29dab8836efb94430f Mon Sep 17 00:00:00 2001 From: jeffreyhuang Date: Mon, 18 Sep 2017 15:24:01 -0700 Subject: [PATCH 10/17] Introduce CoolColorTemperaturePreferenceController - Create new CoolColorTemperaturePreferenceController - Create controller inside the DashboardFragment - Port logic from DevelopmentSettings into the controller Bug: 34203528 Test: make RunSettingsRoboTests -j40 Change-Id: Ib138dd55f197ae4cc14db4f3414abdd644e6b202 --- ...lColorTemperaturePreferenceController.java | 100 ++++++++++++++ .../DevelopmentSettingsDashboardFragment.java | 2 +- tests/robotests/res/values-mcc999/config.xml | 20 +++ tests/robotests/res/values/config.xml | 1 + ...orTemperaturePreferenceControllerTest.java | 125 ++++++++++++++++++ 5 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/development/CoolColorTemperaturePreferenceController.java create mode 100644 tests/robotests/res/values-mcc999/config.xml create mode 100644 tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java diff --git a/src/com/android/settings/development/CoolColorTemperaturePreferenceController.java b/src/com/android/settings/development/CoolColorTemperaturePreferenceController.java new file mode 100644 index 00000000000..348624b1957 --- /dev/null +++ b/src/com/android/settings/development/CoolColorTemperaturePreferenceController.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import android.content.Context; +import android.os.SystemProperties; +import android.support.annotation.VisibleForTesting; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.widget.Toast; + +import com.android.settings.R; +import com.android.settingslib.development.SystemPropPoker; + +public class CoolColorTemperaturePreferenceController extends + DeveloperOptionsPreferenceController implements + Preference.OnPreferenceChangeListener { + + private static final String COLOR_TEMPERATURE_KEY = "color_temperature"; + + @VisibleForTesting + static final String COLOR_TEMPERATURE_PROPERTY = "persist.sys.debug.color_temp"; + + private SwitchPreference mPreference; + + public CoolColorTemperaturePreferenceController(Context context) { + super(context); + } + + @Override + public boolean isAvailable() { + return mContext.getResources().getBoolean(R.bool.config_enableColorTemperature); + } + + @Override + public String getPreferenceKey() { + return COLOR_TEMPERATURE_KEY; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isColorTemperatureEnabled = (Boolean) newValue; + SystemProperties.set(COLOR_TEMPERATURE_PROPERTY, + Boolean.toString(isColorTemperatureEnabled)); + SystemPropPoker.getInstance().poke(); + displayColorTemperatureToast(); + return true; + } + + @Override + public void updateState(Preference preference) { + final boolean enableColorTemperature = SystemProperties.getBoolean( + COLOR_TEMPERATURE_PROPERTY, false /* default */); + mPreference.setChecked(enableColorTemperature); + } + + @Override + public void onDeveloperOptionsEnabled() { + if(!isAvailable()) { + return; + } + mPreference.setEnabled(true); + } + + @Override + public void onDeveloperOptionsDisabled() { + if(!isAvailable()) { + return; + } + SystemProperties.set(COLOR_TEMPERATURE_PROPERTY, Boolean.toString(false)); + mPreference.setChecked(false); + mPreference.setEnabled(false); + } + + @VisibleForTesting + void displayColorTemperatureToast() { + Toast.makeText(mContext, R.string.color_temperature_toast, Toast.LENGTH_LONG).show(); + } +} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 06623086b25..526e93ad933 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -183,7 +183,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra // convert to file encryption // picture color mode // webview implementation - // cool color temperature + controllers.add(new CoolColorTemperaturePreferenceController(context)); // automatic system updates // system ui demo mode // quick settings developer tiles diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml new file mode 100644 index 00000000000..800c6449eea --- /dev/null +++ b/tests/robotests/res/values-mcc999/config.xml @@ -0,0 +1,20 @@ + + + + + false + \ No newline at end of file diff --git a/tests/robotests/res/values/config.xml b/tests/robotests/res/values/config.xml index 156e20aef83..411412cffed 100644 --- a/tests/robotests/res/values/config.xml +++ b/tests/robotests/res/values/config.xml @@ -17,4 +17,5 @@ true + true \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java new file mode 100644 index 00000000000..16e6c2a74e2 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import static com.android.settings.development.CoolColorTemperaturePreferenceController + .COLOR_TEMPERATURE_PROPERTY; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowSystemProperties; + +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(manifest = TestConfig.MANIFEST_PATH, + sdk = TestConfig.SDK_VERSION, + shadows = {SettingsShadowSystemProperties.class}) +public class CoolColorTemperaturePreferenceControllerTest { + + private Context mContext; + @Mock + private SwitchPreference mPreference; + @Mock + private PreferenceScreen mPreferenceScreen; + private CoolColorTemperaturePreferenceController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mController = new CoolColorTemperaturePreferenceController(mContext); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( + mPreference); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void onPreferenceChanged_turnOnCoolColorTemperature() { + mController.onPreferenceChange(null, true); + final boolean mode = SettingsShadowSystemProperties.getBoolean( + COLOR_TEMPERATURE_PROPERTY, false); + + assertThat(mode).isTrue(); + } + + @Test + public void onPreferenceChanged_turnOffCoolColorTemperature() { + mController.onPreferenceChange(null, false); + final boolean mode = SettingsShadowSystemProperties.getBoolean( + COLOR_TEMPERATURE_PROPERTY, false); + + assertThat(mode).isFalse(); + } + + @Test + public void updateState_preferenceShouldBeChecked() { + SettingsShadowSystemProperties.set(COLOR_TEMPERATURE_PROPERTY, + Boolean.toString(true)); + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + } + + @Test + public void updateState_preferenceShouldNotBeChecked() { + SettingsShadowSystemProperties.set(COLOR_TEMPERATURE_PROPERTY, + Boolean.toString(false)); + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + } + + @Test + public void onDeveloperOptionsDisabled_shouldDisablePreference() { + mController.onDeveloperOptionsDisabled(); + + verify(mPreference).setEnabled(false); + verify(mPreference).setChecked(false); + } + + @Test + public void onDeveloperOptionsEnabled_shouldEnablePreference() { + mController.onDeveloperOptionsEnabled(); + + verify(mPreference).setEnabled(true); + } + + @Test + @Config(qualifiers = "mcc999") + public void isAvailable_shouldBeFalse() { + assertThat(mController.isAvailable()).isFalse(); + } +} From 8c57f28c7a2b037cec0e042324cf773ecd8d97db Mon Sep 17 00:00:00 2001 From: Jeffrey Huang Date: Thu, 21 Sep 2017 00:33:40 +0000 Subject: [PATCH 11/17] Revert "Introduce CoolColorTemperaturePreferenceController" This reverts commit ebcf01f128422e82bf6bcd29dab8836efb94430f. Change-Id: Ie26f1790833741e56012dda95702f087d867c552 --- ...lColorTemperaturePreferenceController.java | 100 -------------- .../DevelopmentSettingsDashboardFragment.java | 2 +- tests/robotests/res/values-mcc999/config.xml | 20 --- tests/robotests/res/values/config.xml | 1 - ...orTemperaturePreferenceControllerTest.java | 125 ------------------ 5 files changed, 1 insertion(+), 247 deletions(-) delete mode 100644 src/com/android/settings/development/CoolColorTemperaturePreferenceController.java delete mode 100644 tests/robotests/res/values-mcc999/config.xml delete mode 100644 tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java diff --git a/src/com/android/settings/development/CoolColorTemperaturePreferenceController.java b/src/com/android/settings/development/CoolColorTemperaturePreferenceController.java deleted file mode 100644 index 348624b1957..00000000000 --- a/src/com/android/settings/development/CoolColorTemperaturePreferenceController.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.development; - -import android.content.Context; -import android.os.SystemProperties; -import android.support.annotation.VisibleForTesting; -import android.support.v14.preference.SwitchPreference; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceScreen; -import android.widget.Toast; - -import com.android.settings.R; -import com.android.settingslib.development.SystemPropPoker; - -public class CoolColorTemperaturePreferenceController extends - DeveloperOptionsPreferenceController implements - Preference.OnPreferenceChangeListener { - - private static final String COLOR_TEMPERATURE_KEY = "color_temperature"; - - @VisibleForTesting - static final String COLOR_TEMPERATURE_PROPERTY = "persist.sys.debug.color_temp"; - - private SwitchPreference mPreference; - - public CoolColorTemperaturePreferenceController(Context context) { - super(context); - } - - @Override - public boolean isAvailable() { - return mContext.getResources().getBoolean(R.bool.config_enableColorTemperature); - } - - @Override - public String getPreferenceKey() { - return COLOR_TEMPERATURE_KEY; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey()); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean isColorTemperatureEnabled = (Boolean) newValue; - SystemProperties.set(COLOR_TEMPERATURE_PROPERTY, - Boolean.toString(isColorTemperatureEnabled)); - SystemPropPoker.getInstance().poke(); - displayColorTemperatureToast(); - return true; - } - - @Override - public void updateState(Preference preference) { - final boolean enableColorTemperature = SystemProperties.getBoolean( - COLOR_TEMPERATURE_PROPERTY, false /* default */); - mPreference.setChecked(enableColorTemperature); - } - - @Override - public void onDeveloperOptionsEnabled() { - if(!isAvailable()) { - return; - } - mPreference.setEnabled(true); - } - - @Override - public void onDeveloperOptionsDisabled() { - if(!isAvailable()) { - return; - } - SystemProperties.set(COLOR_TEMPERATURE_PROPERTY, Boolean.toString(false)); - mPreference.setChecked(false); - mPreference.setEnabled(false); - } - - @VisibleForTesting - void displayColorTemperatureToast() { - Toast.makeText(mContext, R.string.color_temperature_toast, Toast.LENGTH_LONG).show(); - } -} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 526e93ad933..06623086b25 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -183,7 +183,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra // convert to file encryption // picture color mode // webview implementation - controllers.add(new CoolColorTemperaturePreferenceController(context)); + // cool color temperature // automatic system updates // system ui demo mode // quick settings developer tiles diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml deleted file mode 100644 index 800c6449eea..00000000000 --- a/tests/robotests/res/values-mcc999/config.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - false - \ No newline at end of file diff --git a/tests/robotests/res/values/config.xml b/tests/robotests/res/values/config.xml index 411412cffed..156e20aef83 100644 --- a/tests/robotests/res/values/config.xml +++ b/tests/robotests/res/values/config.xml @@ -17,5 +17,4 @@ true - true \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java deleted file mode 100644 index 16e6c2a74e2..00000000000 --- a/tests/robotests/src/com/android/settings/development/CoolColorTemperaturePreferenceControllerTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.development; - -import static com.android.settings.development.CoolColorTemperaturePreferenceController - .COLOR_TEMPERATURE_PROPERTY; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.support.v14.preference.SwitchPreference; -import android.support.v7.preference.PreferenceScreen; - -import com.android.settings.TestConfig; -import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.testutils.shadow.SettingsShadowSystemProperties; - -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(manifest = TestConfig.MANIFEST_PATH, - sdk = TestConfig.SDK_VERSION, - shadows = {SettingsShadowSystemProperties.class}) -public class CoolColorTemperaturePreferenceControllerTest { - - private Context mContext; - @Mock - private SwitchPreference mPreference; - @Mock - private PreferenceScreen mPreferenceScreen; - private CoolColorTemperaturePreferenceController mController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mController = new CoolColorTemperaturePreferenceController(mContext); - when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( - mPreference); - mController.displayPreference(mPreferenceScreen); - } - - @Test - public void onPreferenceChanged_turnOnCoolColorTemperature() { - mController.onPreferenceChange(null, true); - final boolean mode = SettingsShadowSystemProperties.getBoolean( - COLOR_TEMPERATURE_PROPERTY, false); - - assertThat(mode).isTrue(); - } - - @Test - public void onPreferenceChanged_turnOffCoolColorTemperature() { - mController.onPreferenceChange(null, false); - final boolean mode = SettingsShadowSystemProperties.getBoolean( - COLOR_TEMPERATURE_PROPERTY, false); - - assertThat(mode).isFalse(); - } - - @Test - public void updateState_preferenceShouldBeChecked() { - SettingsShadowSystemProperties.set(COLOR_TEMPERATURE_PROPERTY, - Boolean.toString(true)); - mController.updateState(mPreference); - - verify(mPreference).setChecked(true); - } - - @Test - public void updateState_preferenceShouldNotBeChecked() { - SettingsShadowSystemProperties.set(COLOR_TEMPERATURE_PROPERTY, - Boolean.toString(false)); - mController.updateState(mPreference); - - verify(mPreference).setChecked(false); - } - - @Test - public void onDeveloperOptionsDisabled_shouldDisablePreference() { - mController.onDeveloperOptionsDisabled(); - - verify(mPreference).setEnabled(false); - verify(mPreference).setChecked(false); - } - - @Test - public void onDeveloperOptionsEnabled_shouldEnablePreference() { - mController.onDeveloperOptionsEnabled(); - - verify(mPreference).setEnabled(true); - } - - @Test - @Config(qualifiers = "mcc999") - public void isAvailable_shouldBeFalse() { - assertThat(mController.isAvailable()).isFalse(); - } -} From e781dfa10c35cd996efbe212fb66408dedbd54ee Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 20 Sep 2017 18:33:03 -0700 Subject: [PATCH 12/17] Import translations. DO NOT MERGE Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import Bug: 64712476 Change-Id: Ic65c57bb8ef17b6babd0dab69072b07a70e2b5ec --- res/values-bn/strings.xml | 3 +-- res/values-es/strings.xml | 30 +++++++++++++++--------------- res/values-gu/strings.xml | 3 +-- res/values-hi/strings.xml | 3 +-- res/values-kn/strings.xml | 3 +-- res/values-ml/strings.xml | 7 +++---- res/values-mr/strings.xml | 3 +-- res/values-ne/strings.xml | 3 +-- res/values-pa/strings.xml | 35 +++++++++++++++++------------------ res/values-ta/strings.xml | 3 +-- res/values-te/strings.xml | 3 +-- res/values-ur/strings.xml | 3 +-- 12 files changed, 44 insertions(+), 55 deletions(-) diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 26ef76f7b18..c9609191442 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -3399,8 +3399,7 @@ "টেলিফোনি মনিটর এ করা পরিবর্তন প্রয়োগ করতে ডিভাইসটি বন্ধ করে আবার চালু করুন" "ক্যামেরায় HAL HDR+" "ক্যামেরায় HAL HDR+ পরিবর্তনটি প্রয়োগ করতে ডিভাইসটি বন্ধ করে আবার চালু করুন" - - + "ক্যামেরা লেজার সেন্সর" "স্বয়ংক্রিয় সিস্টেম আপডেটগুলি" "ব্যবহার" "মোবাইল ডেটার ব্যবহার" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 54a1db28212..2f706d0bf4d 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -74,8 +74,8 @@ "asu" "LAC" "CID" - "Desactivar USB" - "Desactivar tarjeta SD" + "Desconectar USB" + "Desconectar tarjeta SD" "Borrar almacenamiento USB" "Borrar tarjeta SD" "Vista previa" @@ -1139,8 +1139,8 @@ "Almacenamiento" "Almacenamiento" "Ajustes de almacenamiento" - "Desactivar almacenamiento USB, ver almacenamiento disponible" - "Desactivar tarjeta SD, ver almacenamiento disponible." + "Desconectar almacenamiento USB, ver almacenamiento disponible" + "Desconectar tarjeta SD, ver almacenamiento disponible." "MDN" "Mi número de teléfono" "MIN" @@ -1175,10 +1175,10 @@ "Audio (música, tonos, etc.)" "Otros archivos" "Datos almacenados en caché" - "Desactivar almacenam comp" - "Desactivar tarjeta SD" - "Desactivar almacenamiento USB interno" - "Desactivar la tarjeta SD para extraerla de forma segura" + "Desconectar almacenamiento compartido" + "Desconectar tarjeta SD" + "Desconectar almacenamiento USB interno" + "Desconectar la tarjeta SD para extraerla de forma segura" "Inserta USB para activar almacenamiento" "Insertar una tarjeta SD para el montaje" "Activar almacenamiento USB" @@ -1192,18 +1192,18 @@ "¿Borrar datos almacenados en caché?" "Se borrarán los datos en caché de las aplicaciones." "La función MTP o PTP está activa." - "¿Desactivar almacenamiento USB?" - "¿Desactivar tarjeta SD?" - "Si desactivas el almacenamiento USB, se detendrán algunas aplicaciones que estás usando y no estarán disponibles hasta que vuelvas a activarlo." - "Si desactivas la tarjeta SD, se detendrán algunas aplicaciones en ejecución y no estarán disponibles hasta que la vuelvas a activar." + "¿Desconectar almacenamiento USB?" + "¿Desconectar tarjeta SD?" + "Si desconectas el almacenamiento USB, se detendrán algunas aplicaciones que estás usando y no estarán disponibles hasta que vuelvas a activarlo." + "Si desconectas la tarjeta SD, se detendrán algunas aplicaciones en ejecución y no estarán disponibles hasta que la vuelvas a activar." - "No se ha podido desactivar el almacenamiento USB. Inténtalo de nuevo más tarde." - "No se ha podido desactivar la tarjeta SD. Inténtalo de nuevo más tarde." + "No se ha podido desconectar el almacenamiento USB. Inténtalo de nuevo más tarde." + "No se ha podido desconectar la tarjeta SD. Inténtalo de nuevo más tarde." "Se desactivará el almacenamiento USB." "Se desactivará la tarjeta SD." "Desactivando" - "La tarjeta SD se está desactivando." + "La tarjeta SD se está desconectando." "No queda espacio de almacenamiento" "Es posible que algunas funciones del sistema, como la sincronización, no funcionen correctamente. Prueba a eliminar o a desactivar elementos para liberar espacio (por ejemplo, aplicaciones o contenido multimedia)." "Cambiar nombre" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 76ce887f20d..57d4ece4c16 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -3399,8 +3399,7 @@ "Telephony Monitor ના ફેરફારો લાગુ કરવા, ઉપકરણને રીબૂટ કરો" "કૅમેરા HAL HDR+" "કૅમેરા HAL HDR+ ફેરફાર લાગુ કરવા માટે, ઉપકરણને રીબૂટ કરો." - - + "કૅમેરાનું લેસર સેન્સર" "સ્વચાલિત સિસ્ટમ અપડેટ્સ" "વપરાશ" "મોબાઇલ ડેટા વપરાશ" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index b785a3ef8ec..95da31b64b8 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -3399,8 +3399,7 @@ "टेलीफ़ोनी मॉनिटर बदलाव लागू करने के लिए, डिवाइस को रीबूट करें" "कैमरा HAL HDR+" "कैमरा HAL HDR+ में किये गए बदलाव लागू करने के लिए, डिवाइस को फिर से चालू करें" - - + "कैमरा लेज़र सेंसर" "स्वचालित सिस्टम अपडेट" "उपयोग" "मोबाइल डेटा खर्च" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 0cb7dfc1349..e559a75628d 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -3399,8 +3399,7 @@ "ದೂರವಾಣಿ ಮಾನಿಟರ್ ಬದಲಾವಣೆಯನ್ನು ಅನ್ವಯಿಸಲು, ಸಾಧನವನ್ನು ರೀಬೂಟ್ ಮಾಡಿ" "HAL HDR+ ಕ್ಯಾಮೆರಾ" "HAL HDR+ ಕ್ಯಾಮೆರಾ ಬದಲಾವಣೆಯನ್ನು ಅನ್ವಯಿಸಲು, ಸಾಧನವನ್ನು ರೀಬೂಟ್ ಮಾಡಿ" - - + "ಕ್ಯಾಮೆರಾ ಲೇಸರ್ ಸೆನ್ಸಾರ್" "ಸ್ವಯಂಚಾಲಿತ ಸಿಸ್ಟಂ ಅಪ್‌ಡೇಟ್‌ಗಳು" "ಬಳಕೆ" "ಮೊಬೈಲ್ ಡೇಟಾ ಬಳಕೆ" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 22236fb26d9..5fd61fae5c2 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -384,7 +384,7 @@ "ഏതുവിധേയനയും ഒഴിവാക്കുക" "തിരിച്ചുപോവുക" "സെൻസർ സ്പർശിക്കുക" - "ഫോണിന്റെ പിൻവശത്താണ് അതുള്ളത്. നിങ്ങളുടെ ചൂണ്ടുവിരൽ ഉപയോഗിക്കുക." + "ഫോണിന്റെ പിൻവശത്താണ് അതുള്ളത്. ചൂണ്ടുവിരൽ ഉപയോഗിക്കുക." "ഉപകരണത്തിലെ ചിത്രീകരണവും ഫിംഗർപ്രിന്റ് സെൻസർ ലൊക്കേഷനും" "പേര്" "ശരി" @@ -2746,7 +2746,7 @@ "മൊബൈല്‍" "ഡാറ്റ ഉപയോഗം" "ഹോട്ട്‌സ്‌പോട്ട്" - "കണക്‌റ്റുചെയ്ത ഉപകരണം" + "കണക്‌റ്റ് ചെയ്ത ഉപകരണങ്ങൾ" "Bluetooth, കാസ്റ്റ്, NFC" "Bluetooth, കാസ്‌റ്റ്" "ആപ്‌സും അറിയിപ്പുകളും" @@ -3399,8 +3399,7 @@ "ടെലിഫോണി മോണിറ്ററിലെ മാറ്റം പ്രയോഗിക്കാൻ ഉപകരണം റീബൂട്ടുചെയ്യുക" "ക്യാമറ HAL HDR+" "ക്യാമറ HAL HDR+ മാറ്റം പ്രയോഗിക്കാൻ, ഉപകരണം റീബൂട്ട് ചെയ്യുക" - - + "ക്യാമറ ലേസർ സെൻസർ" "സ്വയമേവയുള്ള സിസ്റ്റം അപ്ഡേറ്റുകൾ" "ഉപയോഗം" "മൊബൈൽ ഡാറ്റ ഉപയോഗം" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 1aa45df2932..51ad1ac28cc 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -3399,8 +3399,7 @@ "टेलिफोनी मॉनिटर बदल लागू करण्‍यासाठी, डिव्हाइस रीबूट करा" "कॅमेरा HAL HDR+" "कॅमेरा HAL HDR+ बदल लागू करण्यासाठी डिव्हाइस रीबूट करा" - - + "कॅमेरा लेझर सेंसर" "स्वयंचलित सिस्टम अपडेट" "वापर" "मोबाइल डेटा वापर" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index e81a08f2fdd..678b811f406 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -3399,8 +3399,7 @@ "टेलिफोनी मनिटरमा गरिएको परिवर्तनलाई लागू गर्न यन्त्रलाई पुनःबुट गर्नुहोस्" "क्यामेरा HAL HDR+" "क्यामेरा HAL HDR+ मा गरिएको परिवर्तनलाई लागू गर्न यन्त्रलाई पुनः बुट गर्नुहोस्" - - + "क्यामेरा लेजरको सेन्सर" "स्वचालित प्रणाली अद्यावधिकहरू" "उपयोग" "मोबाइल डेटाको प्रयोग" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index ae1658d96cc..d3c2592b960 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -88,7 +88,7 @@ "ਨਮੂਨਾ ਲਿਖਤ" "ਔਜ਼ ਦਾ ਨਿਰਾਲਾ ਵਿਜ਼ਾਰਡ" "ਪਾਠ 11: ਔਜ਼ ਦਾ ਹੀਰਿਆਂ-ਪੰਨਿਆਂ ਵਾਲਾ ਨਿਰਾਲਾ ਸ਼ਹਿਰ" - "ਹਰੇ ਰੰਗ ਦੀਆਂ ਐਨਕਾਂ ਲਗਾ ਕੇ ਵੀ ਡੋਰਥੀ ਅਤੇ ਉਸ ਦੇ ਦੋਸਤ ਨਿਰਾਲੇ ਸ਼ਹਿਰ ਦੀ ਚਮਕ-ਦਮਕ ਦੇਖ ਕੇ ਹੈਰਾਨ ਸਨ। ਹਰੇ ਰੰਗ ਦੇ ਮਾਰਬਲ ਨਾਲ ਬਣੇ ਹੋਏ ਸੁੰਦਰ ਘਰ, ਗਲੀਆਂ ਵਿੱਚ ਕਤਾਰਬੱਧ ਸਨ ਅਤੇ ਉਹਨਾਂ ਵਿੱਚ ਚਮਕਦੇ ਪੰਨੇ ਜੜੇ ਹੋਏ ਸਨ। ਉਹ ਉਸੇ ਹਰੇ ਮਾਰਬਲ ਦੇ ਫੁੱਟਪਾਥ \'ਤੇ ਪੈਦਲ ਤੁਰ ਪਏ, ਅਤੇ ਜਿੱਥੇ ਆਲੇ-ਦੁਆਲੇ ਰੱਖੇ ਹੋਏ ਮਾਰਬਲ ਪੱਥਰ ਆਪਸ ਵਿੱਚ ਜੁੜਦੇ ਸਨ ਉੱਥੇ ਨੇੜੇ-ਨੇੜੇ ਲਗਾਏ ਗਏ ਪੰਨਿਆਂ ਦੀਆਂ ਕਤਾਰਾਂ ਸਨ ਅਤੇ ਧੁੱਪ ਵਿੱਚ ਚਮਕ ਰਹੀਆਂ ਸਨ। ਖਿੜਕੀਆਂ ਦੇ ਸ਼ੀਸ਼ੇ ਹਰੇ ਰੰਗ ਦੇ ਸਨ; ਇੱਥੋਂ ਤੱਕ ਕਿ ਸ਼ਹਿਰ ਦੇ ਉੱਤੇ ਅਸਮਾਨ ਵਿੱਚ ਵੀ ਹਰੇ ਰੰਗ ਦੀ ਭਾਅ ਸੀ, ਅਤੇ ਸੂਰਜ ਦੀਆਂ ਕਿਰਨਾਂ ਵੀ ਹਰੇ ਰੰਗ ਦੀਆਂ ਸਨ। \n\nਉੱਥੇ ਬਹੁਤ ਸਾਰੇ ਲੋਕ, ਆਦਮੀ, ਔਰਤਾਂ, ਅਤੇ ਬੱਚੇ ਘੁੰਮ ਰਹੇ ਸਨ, ਅਤੇ ਉਹਨਾਂ ਨੇ ਹਰੇ ਰੰਗ ਦੇ ਕੱਪੜੇ ਪਾਏ ਹੋਏ ਸਨ ਅਤੇ ਉਹਨਾਂ ਦੀ ਚਮੜੀ ਦਾ ਰੰਗ ਵੀ ਹਰਾ ਹੀ ਸੀ। ਉੁਹਨਾਂ ਨੇ ਡੋਰਥੀ ਅਤੇ ਉਸਦੇ ਅਜੀਬੋ-ਗਰੀਬ ਦੋਸਤਾਂ ਵੱਲ ਹੈਰਾਨੀ ਭਰੀਆਂ ਨਜ਼ਰਾਂ ਨਾਲ ਦੇਖਿਆ, ਅਤੇ ਜਦੋਂ ਬੱਚਿਆਂ ਨੇ ਸ਼ੇਰ ਨੂੰ ਦੇਖਿਆ ਤਾਂ ਸਾਰੇ ਬੱਚੇ ਭੱਜ ਗਏ ਅਤੇ ਆਪਣੀਆਂ ਮਾਵਾਂ ਦੇ ਪਿੱਛੇ ਜਾ ਕੇ ਲੁਕ ਗਏ; ਪਰ ਕਿਸੇ ਨੇ ਵੀ ਉਹਨਾਂ ਨਾਲ ਗੱਲ ਨਹੀਂ ਕੀਤੀ। ਗਲੀ ਵਿੱਚ ਬਹੁਤ ਸਾਰੀਆਂ ਦੁਕਾਨਾਂ ਸਨ, ਅਤੇ ਡੋਰਥੀ ਨੇ ਦੇਖਿਆ ਕਿ ਉੱਥੇ ਸਭ ਕੁਝ ਹਰੇ ਰੰਗ ਦਾ ਹੀ ਸੀ। ਹਰੇ ਰੰਗ ਦੀਆਂ ਟੌਫੀਆਂ, ਅਤੇ ਹਰੇ ਰੰਗੇ ਦੇ ਮੱਕੀ ਦੇ ਫੁੱਲੇ ਵੇਚੇ ਜਾ ਰਹੇ ਸਨ, ਹਰੇ ਰੰਗ ਦੇ ਬੂਟ ਵੀ, ਹਰੀਆਂ ਟੋਪੀਆਂ, ਅਤੇ ਸਾਰੀਆਂ ਕਿਸਮਾਂ ਦੇ ਹਰੇ ਰੰਗ ਦੇ ਕੱਪੜੇ। ਇੱਕ ਥਾਂ \'ਤੇ ਇੱਕ ਵਿਅਕਤੀ ਹਰੇ ਰੰਗ ਦੀ ਸ਼ਕੰਜਵੀ ਵੇਚ ਰਿਹਾ ਸੀ, ਅਤੇ ਜਦੋਂ ਬੱਚੇ ਸ਼ਕੰਜਵੀ ਨੂੰ ਖ਼ਰੀਦ ਰਹੇ ਸਨ ਤਾਂ ਡੋਰਥੀ ਦੇਖ ਸਕਦੀ ਸੀ ਕਿ ਉਹ ਉਸ ਵਿਅਕਤੀ ਨੂੰ ਹਰੇ ਰੰਗ ਦੀਆਂ ਪੈਨੀਆਂ (ਪੈਸੇ) ਦੇ ਰਹੇ ਸਨ। \n\nਇੰਝ ਪ੍ਰਤੀਤ ਹੁੰਦਾ ਸੀ ਕਿ ਉੱਥੇ ਕੋਈ ਘੋੜੇ ਜਾਂ ਕਿਸੇ ਵੀ ਕਿਸਮ ਦੇ ਜਾਨਵਰ ਨਹੀਂ ਸਨ: ਬੰਦੇ ਹਰੇ ਰੰਗ ਦੇ ਛੋਟੇ-ਛੋਟੇ ਗੱਡਿਆਂ ਉੱਤੇ ਚੀਜ਼ਾਂ ਨੂੰ ਇੱਧਰ-ਉੱਧਰ ਲੈ ਕੇ ਜਾ ਰਹੇ ਸਨ, ਜਿੰਨ੍ਹਾਂ ਨੂੰ ਉਹ ਆਪਣੇ ਅੱਗੇ ਧਕੇਲ ਕੇ ਲੈ ਜਾ ਰਹੇ ਸਨ। ਹਰ ਕੋਈ ਖੁਸ਼ ਅਤੇ ਸੰਤੁਸ਼ਟ ਅਤੇ ਖ਼ੁਸ਼ਹਾਲ ਪ੍ਰਤੀਤ ਹੋ ਰਿਹਾ ਸੀ।" + "ਹਰੇ ਰੰਗ ਦੀਆਂ ਐਨਕਾਂ ਲਗਾ ਕੇ ਵੀ ਡੋਰਥੀ ਅਤੇ ਉਸ ਦੇ ਦੋਸਤ ਨਿਰਾਲੇ ਸ਼ਹਿਰ ਦੀ ਚਮਕ-ਦਮਕ ਦੇਖ ਕੇ ਹੈਰਾਨ ਸਨ। ਹਰੇ ਰੰਗ ਦੇ ਮਾਰਬਲ ਨਾਲ ਬਣੇ ਹੋਏ ਸੁੰਦਰ ਘਰ, ਗਲੀਆਂ ਵਿੱਚ ਕਤਾਰਬੱਧ ਸਨ ਅਤੇ ਉਹਨਾਂ ਵਿੱਚ ਚਮਕਦੇ ਪੰਨੇ ਜੜੇ ਹੋਏ ਸਨ। ਉਹ ਉਸੇ ਹਰੇ ਮਾਰਬਲ ਦੇ ਫੁੱਟਪਾਥ \'ਤੇ ਪੈਦਲ ਤੁਰ ਪਏ, ਅਤੇ ਜਿੱਥੇ ਆਲੇ-ਦੁਆਲੇ ਰੱਖੇ ਹੋਏ ਮਾਰਬਲ ਪੱਥਰ ਆਪਸ ਵਿੱਚ ਜੁੜਦੇ ਸਨ ਉੱਥੇ ਨੇੜੇ-ਨੇੜੇ ਲਗਾਏ ਗਏ ਪੰਨਿਆਂ ਦੀਆਂ ਕਤਾਰਾਂ ਸਨ ਅਤੇ ਧੁੱਪ ਵਿੱਚ ਚਮਕ ਰਹੀਆਂ ਸਨ। ਖਿੜਕੀਆਂ ਦੇ ਸ਼ੀਸ਼ੇ ਹਰੇ ਰੰਗ ਦੇ ਸਨ; ਇੱਥੋਂ ਤੱਕ ਕਿ ਸ਼ਹਿਰ ਦੇ ਉੱਤੇ ਅਸਮਾਨ ਵਿੱਚ ਵੀ ਹਰੇ ਰੰਗ ਦੀ ਭਾਅ ਸੀ, ਅਤੇ ਸੂਰਜ ਦੀਆਂ ਕਿਰਨਾਂ ਵੀ ਹਰੇ ਰੰਗ ਦੀਆਂ ਸਨ। \n\nਉੱਥੇ ਬਹੁਤ ਸਾਰੇ ਲੋਕ, ਆਦਮੀ, ਔਰਤਾਂ, ਅਤੇ ਬੱਚੇ ਘੁੰਮ ਰਹੇ ਸਨ, ਅਤੇ ਉਹਨਾਂ ਨੇ ਹਰੇ ਰੰਗ ਦੇ ਕੱਪੜੇ ਪਾਏ ਹੋਏ ਸਨ ਅਤੇ ਉਹਨਾਂ ਦੀ ਚਮੜੀ ਦਾ ਰੰਗ ਵੀ ਹਰਾ ਹੀ ਸੀ। ਉੁਹਨਾਂ ਨੇ ਡੋਰਥੀ ਅਤੇ ਉਸਦੇ ਅਜੀਬੋ-ਗਰੀਬ ਦੋਸਤਾਂ ਵੱਲ ਹੈਰਾਨੀ ਭਰੀਆਂ ਨਜ਼ਰਾਂ ਨਾਲ ਦੇਖਿਆ, ਅਤੇ ਜਦੋਂ ਬੱਚਿਆਂ ਨੇ ਸ਼ੇਰ ਨੂੰ ਦੇਖਿਆ ਤਾਂ ਸਾਰੇ ਬੱਚੇ ਭੱਜ ਗਏ ਅਤੇ ਆਪਣੀਆਂ ਮਾਵਾਂ ਦੇ ਪਿੱਛੇ ਜਾ ਕੇ ਲੁਕ ਗਏ; ਪਰ ਕਿਸੇ ਨੇ ਵੀ ਉਹਨਾਂ ਨਾਲ ਗੱਲ ਨਹੀਂ ਕੀਤੀ। ਗਲੀ ਵਿੱਚ ਬਹੁਤ ਸਾਰੀਆਂ ਦੁਕਾਨਾਂ ਸਨ, ਅਤੇ ਡੋਰਥੀ ਨੇ ਦੇਖਿਆ ਕਿ ਉੱਥੇ ਸਭ ਕੁਝ ਹਰੇ ਰੰਗ ਦਾ ਹੀ ਸੀ। ਹਰੇ ਰੰਗ ਦੀਆਂ ਟੌਫੀਆਂ, ਅਤੇ ਹਰੇ ਰੰਗੇ ਦੇ ਮੱਕੀ ਦੇ ਫੁੱਲੇ ਵੇਚੇ ਜਾ ਰਹੇ ਸਨ, ਹਰੇ ਰੰਗ ਦੇ ਬੂਟ ਵੀ, ਹਰੀਆਂ ਟੋਪੀਆਂ, ਅਤੇ ਸਾਰੀਆਂ ਕਿਸਮਾਂ ਦੇ ਹਰੇ ਰੰਗ ਦੇ ਕੱਪੜੇ। ਇੱਕ ਥਾਂ \'ਤੇ ਇੱਕ ਵਿਅਕਤੀ ਹਰੇ ਰੰਗ ਦੀ ਸ਼ਕੰਜਵੀ ਵੇਚ ਰਿਹਾ ਸੀ, ਅਤੇ ਜਦੋਂ ਬੱਚੇ ਸ਼ਕੰਜਵੀ ਨੂੰ ਖਰੀਦ ਰਹੇ ਸਨ ਤਾਂ ਡੋਰਥੀ ਦੇਖ ਸਕਦੀ ਸੀ ਕਿ ਉਹ ਉਸ ਵਿਅਕਤੀ ਨੂੰ ਹਰੇ ਰੰਗ ਦੀਆਂ ਪੈਨੀਆਂ (ਪੈਸੇ) ਦੇ ਰਹੇ ਸਨ। \n\nਇੰਝ ਪ੍ਰਤੀਤ ਹੁੰਦਾ ਸੀ ਕਿ ਉੱਥੇ ਕੋਈ ਘੋੜੇ ਜਾਂ ਕਿਸੇ ਵੀ ਕਿਸਮ ਦੇ ਜਾਨਵਰ ਨਹੀਂ ਸਨ: ਬੰਦੇ ਹਰੇ ਰੰਗ ਦੇ ਛੋਟੇ-ਛੋਟੇ ਗੱਡਿਆਂ ਉੱਤੇ ਚੀਜ਼ਾਂ ਨੂੰ ਇੱਧਰ-ਉੱਧਰ ਲੈ ਕੇ ਜਾ ਰਹੇ ਸਨ, ਜਿੰਨ੍ਹਾਂ ਨੂੰ ਉਹ ਆਪਣੇ ਅੱਗੇ ਧਕੇਲ ਕੇ ਲੈ ਜਾ ਰਹੇ ਸਨ। ਹਰ ਕੋਈ ਖੁਸ਼ ਅਤੇ ਸੰਤੁਸ਼ਟ ਅਤੇ ਖ਼ੁਸ਼ਹਾਲ ਪ੍ਰਤੀਤ ਹੋ ਰਿਹਾ ਸੀ।" "ਠੀਕ" "USB ਸਟੋਰੇਜ" "SD ਕਾਰਡ" @@ -326,8 +326,8 @@ "ਸਮਾਂ" "ਸਵੈਚਲਿਤ ਤਰੀਕੇ ਨਾਲ ਲਾਕ ਕਰੋ" "ਸਲੀਪ ਤੋਂ ਬਾਅਦ %1$s" - "ਸਲੀਪ ਦੇ ਤੁਰੰਤ ਬਾਅਦ, ਇਸਦੇ ਸਿਵਾਏ ਜਦੋਂ %1$s ਵੱਲੋਂ ਅਨਲੌਕ ਨਾ ਰੱਖਿਆ ਗਿਆ ਹੋਵੇ" - "ਸਲੀਪ ਤੋਂ ਬਾਅਦ %1$s, ਸਿਵਾਏ ਇਸਦੇ ਜਦੋਂ %2$s ਵੱਲੋਂ ਅਨਲੌਕ ਰੱਖਿਆ ਹੋਵੇ" + "ਸਲੀਪ ਦੇ ਤੁਰੰਤ ਬਾਅਦ, ਇਸਦੇ ਸਿਵਾਏ ਜਦੋਂ %1$s ਵੱਲੋਂ ਅਣਲਾਕ ਨਾ ਰੱਖਿਆ ਗਿਆ ਹੋਵੇ" + "ਸਲੀਪ ਤੋਂ ਬਾਅਦ %1$s, ਸਿਵਾਏ ਇਸਦੇ ਜਦੋਂ %2$s ਵੱਲੋਂ ਅਣਲਾਕ ਰੱਖਿਆ ਹੋਵੇ" "ਲੌਕ ਸਕ੍ਰੀਨ ਤੇ ਮਾਲਕ ਜਾਣਕਾਰੀ ਦਿਖਾਓ" "ਲੌਕ ਸਕ੍ਰੀਨ ਸੁਨੇਹਾ" "ਵਿਜੇਟ ਨੂੰ ਚਾਲੂ ਕਰੋ" @@ -1282,7 +1282,7 @@ "ਮੂਵ ਦੇ ਦੌਰਾਨ \n• ^1 ਨੂੰ ਨਾ ਹਟਾਓ. \n• ਕੁਝ ਐਪ ਸਹੀ ਤਰੀਕੇ ਦੇ ਨਾਲ ਕੰਮ ਨਹੀਂ ਕਰਨ। \n• ਡੀਵਾਈਸ ਨੂੰ ਚਾਰਜ ਰੱਖੋ" "^1 ਤਿਆਰ ਹੈ" "ਤੁਹਾਡ ^1 ਫ਼ੋਟੋਆਂ ਅਤੇ ਦੂਜੇ ਮੀਡੀਆ ਦੇ ਨਾਲ ਉਪਯੋਗ ਕਰਨ ਲਈ ਬਿਲਕੁਲ ਤਿਆਰ ਹੈ." - "ਤੁਹਾਡਾ ਨਵਾਂ ^1 ਕੰਮ ਕਰ ਰਿਹਾ ਹੈ। \n\nਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫੋਟੋਆਂ, ਫ਼ਾਈਲਾਂ, ਅਤੇ ਐਪ ਡਾਟਾ ਨੂੰ ਲੈ ਜਾਣ ਲਈ, ਸੈੱਟਿੰਗਾਂ ਅਤੇ ਸਟੋਰੇਜ \'ਤੇ ਜਾਓ।" + "ਤੁਹਾਡਾ ਨਵਾਂ ^1 ਕੰਮ ਕਰ ਰਿਹਾ ਹੈ। \n\nਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫੋਟੋਆਂ, ਫ਼ਾਈਲਾਂ, ਅਤੇ ਐਪ ਡਾਟਾ ਨੂੰ ਲੈ ਜਾਣ ਲਈ, ਸੈਟਿੰਗਾਂ ਅਤੇ ਸਟੋਰੇਜ \'ਤੇ ਜਾਓ।" "^1 ਨੂੰ ਮੂਵ ਕਰੋ" "^1 ਅਤੇ ਇਸਦੇ ਡਾਟਾ ਨੂੰ ^2 ਵਿੱਚ ਮੂਵ ਕਰਨਾ ਸਿਰਫ਼ ਕੁਝ ਸਮਾਂ ਲੈਂਦਾ ਹੈ। ਤੁਸੀਂ ਉਦੋਂ ਤੱਕ ਇਸ ਐਪ ਦੀ ਵਰਤੋਂ ਕਰਨ ਦੇ ਯੋਗ ਨਹੀਂ ਹੋਵੋਗੇ ਜਦੋਂ ਤੱਕ ਇਹ ਮੂਵ ਪੂਰਾ ਨਹੀਂ ਹੋ ਜਾਂਦਾ ਹੈ। \n\nਮੂਵ ਦੇ ਦੌਰਾਨ ^2 ਨੂੰ ਨਾ ਹਟਾਓ।" "^1 ਨੂੰ ਮੂਵ ਕਰ ਰਿਹਾ ਹੈ…" @@ -1412,7 +1412,7 @@ "ਮੋਡ" "ਉੱਚ ਸ਼ੁੱਧਤਾ" "ਬੈਟਰੀ ਦੀ ਬਚਤ" - "ਕੇਵਲ ਡਿਵਾਈਸ" + "ਕੇਵਲ ਡੀਵਾਈਸ" "ਟਿਕਾਣਾ ਬੰਦ" "ਐਪ-ਪੱਧਰ ਇਜਾਜ਼ਤਾਂ" "ਹਾਲੀਆ ਟਿਕਾਣਾ ਬੇਨਤੀਆਂ" @@ -1525,7 +1525,7 @@ "ਪ੍ਰੋਫਾਈਲ ਪੈਟਰਨ ਨੂੰ ਦਿਖਣਯੋਗ ਬਣਾਓ" "ਟੈਪ \'ਤੇ ਥਰਥਰਾਹਟ ਕਰੋ" "ਪਾਵਰ ਬਟਨ ਤੁਰੰਤ ਲੌਕ ਹੁੰਦਾ ਹੈ" - "ਸਿਵਾਏ ਇਸਦੇ ਜਦੋਂ %1$s ਵੱਲੋਂ ਅਨਲੌਕ ਰੱਖਿਆ ਹੋਵੇ" + "ਸਿਵਾਏ ਇਸਦੇ ਜਦੋਂ %1$s ਵੱਲੋਂ ਅਣਲਾਕ ਰੱਖਿਆ ਹੋਵੇ" "ਅਣਲਾਕ ਪੈਟਰਨ ਸੈੱਟ ਕਰੋ" "ਅਣਲਾਕ ਪੈਟਰਨ ਬਦਲੋ" "ਇੱਕ ਅਣਲਾਕ ਪੈਟਰਨ ਕਿਵੇਂ ਉਲੀਕੀਏ" @@ -2182,7 +2182,7 @@ "ਚਲਾਓ" "ਪਾਵਰ ਨਿਯੰਤਰਣ" "ਵਾਈ‑ਫਾਈ ਸੈਟਿੰਗ ਅੱਪਡੇਟ ਹੋ ਰਹੀ ਹੈ" - "Bluetooth ਸੈਟਿੰਗ ਅਪਡੇਟ ਕਰ ਰਿਹਾ ਹੈ" + "Bluetooth ਸੈਟਿੰਗ ਅੱਪਡੇਟ ਕਰ ਰਿਹਾ ਹੈ" "%1$s %2$s" "ਚਾਲੂ" "ਬੰਦ" @@ -2212,7 +2212,7 @@ "ਵਿਕਸਿਤ" "ਸਟੋਰੇਜ ਦਾ ਪ੍ਰਕਾਰ" "ਹਾਰਡਵੇਅਰ-ਸਮਰਥਿਤ" - "ਕੇਵਲ ਸੌਫਟਵੇਅਰ" + "ਕੇਵਲ ਸਾਫਟਵੇਅਰ" "ਇਸ ਵਰਤੋਂਕਾਰ ਲਈ ਕ੍ਰੀਡੈਂਸ਼ੀਅਲ ਉਪਲਬਧ ਨਹੀਂ ਹਨ" "VPN ਅਤੇ ਐਪਾਂ ਲਈ ਸਥਾਪਤ ਕੀਤਾ ਗਿਆ" "ਵਾਈ-ਫਾਈ ਲਈ ਸਥਾਪਤ ਕੀਤਾ ਗਿਆ" @@ -2244,7 +2244,7 @@ "ਵਰਤਮਾਨ ਤੌਰ \'ਤੇ ਕੋਈ ਖਾਤਾ ਉਸ ਡਾਟਾ ਨੂੰ ਸਟੋਰ ਨਹੀਂ ਕਰ ਰਿਹਾ ਹੈ, ਜਿਸ ਦਾ ਬੈਕਅੱਪ ਲਿਆ ਗਿਆ ਹੈ" "ਆਪਣੇ ਵਾਈ-ਫਾਈ ਪਾਸਵਰਡਾਂ, ਬੁੱਕਮਾਰਕਾਂ, ਹੋਰ ਸੈਟਿੰਗਾਂ ਅਤੇ ਐਪ ਡਾਟਾ ਨੂੰ ਬੈਕ ਅੱਪ ਕਰਨਾ ਬੰਦ ਕਰਨਾ ਹੈ ਅਤੇ Google ਸਰਵਰਾਂ ਦੀਆਂ ਸਾਰੀਆਂ ਕਾਪੀਆਂ ਮਿਟਾਉਣੀਆਂ ਹਨ?" - "ਕੀ ਡੀਵਾਈਸ ਡਾਟਾ (ਜਿਵੇਂ ਕਿ ਵਾਈ-ਫਾਈ ਪਾਸਵਰਡ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ) ਅਤੇ ਐਪ ਡਾਟਾ (ਜਿਵੇਂ ਕਿ ਐਪਾਂ ਦਾ ਸਟੋਰ ਕੀਤੀ ਸੈੱਟਿੰਗਾਂ ਅਤੇ ਫ਼ਾਈਲਾਂ) ਦੇ ਬੈੱਕਅੱਪ ਤੋਂ ਰੋਕਣਾ ਹੈ, ਅਤੇ ਇਸਦੇ ਨਾਲ-ਨਾਲ ਰਿਮੋਟ ਸਰਵਰ \'ਤੇ ਸਾਰੀਆਂ ਪ੍ਰਤੀਲਿਪੀਆਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?" + "ਕੀ ਡੀਵਾਈਸ ਡਾਟਾ (ਜਿਵੇਂ ਕਿ ਵਾਈ-ਫਾਈ ਪਾਸਵਰਡ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ) ਅਤੇ ਐਪ ਡਾਟਾ (ਜਿਵੇਂ ਕਿ ਐਪਾਂ ਦਾ ਸਟੋਰ ਕੀਤੀ ਸੈਟਿੰਗਾਂ ਅਤੇ ਫ਼ਾਈਲਾਂ) ਦੇ ਬੈੱਕਅੱਪ ਤੋਂ ਰੋਕਣਾ ਹੈ, ਅਤੇ ਇਸਦੇ ਨਾਲ-ਨਾਲ ਰਿਮੋਟ ਸਰਵਰ \'ਤੇ ਸਾਰੀਆਂ ਪ੍ਰਤੀਲਿਪੀਆਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?" "ਡੀਵਾਈਸ ਡਾਟਾ ਰਿਮੋਟ ਦੇ ਰੂਪ ਵਿੱਚ (ਜਿਵੇਂ ਕਿ ਵਾਈ-ਫਾਈ ਪਾਸਵਰਡ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ) ਅਤੇ ਐਪ ਡਾਟਾ (ਜਿਵੇਂ ਕਿ ਐਪਾਂ ਵੱਲੋਂ ਸਟੋਰ ਕੀਤੀਆਂ ਸੈਟਿੰਗਾਂ ਅਤੇ ਫ਼ਾਈਲਾਂ) ਦਾ ਸਵੈਚਲਿਤ ਬੈੱਕਅੱਪ ਲਓ।\n\nਜਦੋਂ ਤੁਸੀਂ ਸਵੈਚਲਿਤ ਬੈਕਅੱਪ ਨੂੰ ਚਾਲੂ ਕਰਦੇ ਹੋ, ਤਾਂ ਡੀਵਾਈਸ ਅਤੇ ਐਪ ਡਾਟਾ ਨੂੰ ਰਿਮੋਟ ਦੇ ਰੂਪ ਵਿੱਚ ਅਵਧੀ ਅਨੁਸਾਰ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਐਪ ਡਾਟਾ ਕੋਈ ਵੀ ਡਾਟਾ ਹੋ ਸਕਦਾ ਹੈ ਜੋ ਸੰਪਰਕ, ਸੁਨੇਹੇ, ਅਤੇ ਫ਼ੋਟੋਆਂ ਵਰਗੇ ਸੰਭਾਵੀ ਸੰਵੇਦਨਸ਼ੀਲ ਡਾਟਾ ਸਮੇਤ, ਕਿਸੇ ਐਪ ਨੇ ਰੱਖਿਅਤ ਕੀਤਾ ਹੁੰਦਾ ਹੈ (ਵਿਕਾਸਕਾਰ ਸੈਟਿੰਗਾਂ ਦੇ ਅਧਾਰ \'ਤੇ)।" "ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਸੈਟਿੰਗਾਂ" "ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਐਪ" @@ -2792,7 +2792,7 @@ "ਐਪਾਂ, ਡਾਊਨਲੋਡ ਕਰੋ, ਐਪਲੀਕੇਸ਼ਨਾਂ, ਸਿਸਟਮ" "ਐਪ, ਇਜਾਜ਼ਤਾਂ, ਸੁਰੱਖਿਆ" "ਐਪਾਂ, ਪੂਰਵ-ਨਿਰਧਾਰਤ" - "ਅਨੁਕੂਲਨ ਅਣਡਿੱਠ ਕਰੋ, doze, ਐਪ ਸਟੈਂਡਬਾਇ" + "ਅਨੁਕੂਲਨ ਅਣਡਿੱਠ ਕਰੋ, doze, ਐਪ ਸਟੈਂਡਬਾਏ" "ਚਮਕੀਲਾ, rgb, srgb, ਰੰਗ, ਕੁਦਰਤੀ, ਸਧਾਰਨ" "ਰੰਗ ਤਾਪਮਾਨ D65 D73 ਚਿੱਟਾ ਪੀਲਾ ਨੀਲਾ ਨਿੱਘਾ ਠੰਡਾ" "ਅਣਲਾਕ ਕਰਨ ਲਈ ਸਲਾਈਡ ਕਰੋ, ਪਾਸਵਰਡ, ਪੈਟਰਨ, ਪਿੰਨ ਦਾਖਲ ਕਰੋ" @@ -3309,7 +3309,7 @@ "ਕੈਮਰੇ ਲਈ ਦੋ ਵਾਰ ਮੋੜੋ" "ਆਪਣੇ ਗੁੱਟ ਨੂੰ ਦੋ ਵਾਰ ਮੋੜਕੇ ਕੈਮਰਾ ਐਪ ਖੋਲ੍ਹੋ" "ਕੈਮਰੇ ਲਈ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦੋ ਵਾਰੀ ਦਬਾਓ" - "ਆਪਣੀ ਸਕ੍ਰੀਨ ਨੂੰ ਅਨਲੌਕ ਕੀਤੇ ਬਗੈਰ ਤੁਰੰਤ ਕੈਮਰਾ ਖੋਲ੍ਹੋ" + "ਆਪਣੀ ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕੀਤੇ ਬਗੈਰ ਤੁਰੰਤ ਕੈਮਰਾ ਖੋਲ੍ਹੋ" "ਡਿਸਪਲੇ ਆਕਾਰ" "ਸਕ੍ਰੀਨ \'ਤੇ ਪਈਆਂ ਆਈਟਮਾਂ ਨੂੰ ਵੱਡਾ ਜਾਂ ਛੋਟਾ ਕਰੋ" "ਡਿਸਪਲੇ ਘਣਤਾ, ਸਕ੍ਰੀਨ ਜ਼ੂਮ, ਪੈਮਾਨਾ, ਸਕੇਲਿੰਗ" @@ -3399,8 +3399,7 @@ "ਟੈਲੀਫ਼ੋਨੀ ਮੋਨੀਟਰ ਤਬਦੀਲੀ ਲਾਗੂ ਕਰਨ ਲਈ, ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰੋ" "ਕੈਮਰਾ HAL HDR+" "ਕੈਮਰਾ HAL HDR+ ਤਬਦੀਲੀ ਲਾਗੂ ਕਰਨ ਲਈ, ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰੋ" - - + "ਕੈਮਰਾ ਲੇਜ਼ਰ ਸੈਂਸਰ" "ਸਵੈਚਾਲਿਤ ਸਿਸਟਮ ਅੱਪਡੇਟਾਂ" "ਵਰਤੋਂ" "ਮੋਬਾਈਲ ਡਾਟਾ ਵਰਤੋਂ" @@ -3491,7 +3490,7 @@ %d ਐਪ ਗੈਰ-ਪਾਬੰਦੀਸ਼ੁਦਾ ਡਾਟੇ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀ ਹੈ %d ਐਪਾਂ ਗੈਰ-ਪਾਬੰਦੀਸ਼ੁਦਾ ਡਾਟੇ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀਆਂ ਹਨ - "ਕੀ ਸੱਚਮੁੱਚ ਵਰਤੋਂਕਾਰ ਡੈਟੇ ਨੂੰ ਮਿਟਾਉਣਾ ਅਤੇ ਫ਼ਾਈਲ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕਰਨਾ ਹੈ?" + "ਕੀ ਸੱਚਮੁੱਚ ਵਰਤੋਂਕਾਰ ਡਾਟੇ ਨੂੰ ਮਿਟਾਉਣਾ ਅਤੇ ਫ਼ਾਈਲ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕਰਨਾ ਹੈ?" "ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ" "ShortcutManager ਰੇਟ-ਲਿਮਟਿੰਗ ਰੀਸੈੱਟ ਕਰੋ" "ShortcutManager ਰੇਟ-ਲਿਮਟਿੰਗ ਰੀਸੈੱਟ ਕੀਤੀ ਜਾ ਚੁੱਕੀ ਹੈ" @@ -3610,10 +3609,10 @@ "ਤੁਹਾਡੀ ਸਟੋਰੇਜ ਦਾ ਪ੍ਰਬੰਧਨ ਹੁਣ ਸਟੋਰੇਜ ਪ੍ਰਬੰਧਕ ਵੱਲੋਂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" "%1$s ਦੇ ਖਾਤੇ" "ਰੂਪ-ਰੇਖਾ ਬਦਲੋ" - "ਡੈਟੇ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸਮਕਾਲੀਕਿਰਤ ਕਰੋ" - "ਨਿੱਜੀ ਡੈਟੇ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸਮਕਾਲੀਕਿਰਤ ਕਰੋ" - "ਕਾਰਜ ਡੈਟੇ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸਮਕਾਲੀਕਿਰਤ ਕਰੋ" - "ਐਪਾਂ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਡੈਟੇ ਨੂੰ ਤਾਜ਼ਾ ਕਰਨ ਦਿਓ" + " ਡਾਟੇ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸਮਕਾਲੀਕਿਰਤ ਕਰੋ" + "ਨਿੱਜੀ ਡਾਟੇ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸਮਕਾਲੀਕਿਰਤ ਕਰੋ" + "ਕਾਰਜ ਡਾਟੇ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸਮਕਾਲੀਕਿਰਤ ਕਰੋ" + "ਐਪਾਂ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਡਾਟੇ ਨੂੰ ਤਾਜ਼ਾ ਕਰਨ ਦਿਓ" "ਖਾਤਾ ਸਮਕਾਲੀਕਰਨ" "%2$d ਵਿੱਚੋਂ %1$d ਆਈਟਮਾਂ ਲਈ ਸਮਕਾਲੀਕਰਨ ਚਾਲੂ ਹੈ" "ਸਾਰੀਆਂ ਆਈਟਮਾਂ ਲਈ ਸਮਕਾਲੀਕਰਨ ਚਾਲੂ ਹੈ" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 490f67daed6..5d60765324d 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -3399,8 +3399,7 @@ "டெலிஃபோனி மானிட்டரில் செய்த மாற்றத்தைப் பயன்படுத்த, சாதனத்தை மறுதொடக்கம் செய்யவும்" "கேமரா HAL HDR+" "கேமரா HAL HDR+ இல் செய்த மாற்றத்தைப் பயன்படுத்த, சாதனத்தை மறுதொடக்கம் செய்யவும்" - - + "கேமராவின் லேசர் சென்சார்" "தானியங்கு முறைமை புதுப்பிப்புகள்" "பயன்பாடு" "மொபைல் டேட்டா பயன்பாடு" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index bace96edcf6..f25733b2f4c 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -3400,8 +3400,7 @@ "టెలిఫోనీ మానిటర్ మార్పును వర్తింపజేయాలంటే, పరికరాన్ని రీబూట్ చేయండి" "కెమెరా HAL HDR+" "కెమెరా HAL HDR+ మార్పును వర్తింపజేయడానికి, పరికరాన్ని రీబూట్ చేయండి" - - + "కెమెరా లేజర్ సెన్సార్" "స్వయంచాలక సిస్టమ్ అప్‌డేట్‌లు" "వినియోగం" "మొబైల్ డేటా వినియోగం" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 81d1087851a..7687a7e0c60 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -3397,8 +3397,7 @@ "ٹیلیفونی مانیٹر کی تبدیلی لاگو کرنے کیلئے، آلہ ریبوٹ کریں" "‏کیمرا HAL HDR+‎" "‏کیمرا HAL HDR+‎ کی تبدیلی لاگو کرنے کے لیے، آلہ ریبوٹ کریں" - - + "کیمرا لیزر سینسر" "خودکار سسٹم اپ ڈیٹس" "استعمال" "موبائل ڈیٹا کا استعمال" From 39de7aedee622984cbfccca09bf2116381571d81 Mon Sep 17 00:00:00 2001 From: tiansiming Date: Tue, 19 Sep 2017 18:16:19 +0800 Subject: [PATCH 13/17] Fix NPE crash in UsageAccessDetails mPackageInfo will be null in RefreshUI method in UsageAccessDetails.java Bug:https://issuetracker.google.com/issues/65872768 Test:As explained in the link above Change-Id: I8bd4b822cfe5d8a3347ca7f5886605cbdfb9b8b6 Signed-off-by: tiansiming --- src/com/android/settings/applications/AppInfoBase.java | 4 +++- .../android/settings/applications/UsageAccessDetails.java | 3 +++ .../settings/applications/UsageAccessDetailsTest.java | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java index a93bfbd4fef..3e83856c0e1 100644 --- a/src/com/android/settings/applications/AppInfoBase.java +++ b/src/com/android/settings/applications/AppInfoBase.java @@ -214,7 +214,9 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment @Override public void onPackageListChanged() { - refreshUi(); + if (!refreshUi()) { + setIntentAndFinish(true, true); + } } public static void startAppInfoFragment(Class fragment, int titleRes, diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java index e40ae37c23f..253ddfdbbba 100644 --- a/src/com/android/settings/applications/UsageAccessDetails.java +++ b/src/com/android/settings/applications/UsageAccessDetails.java @@ -137,6 +137,9 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc @Override protected boolean refreshUi() { + if (mPackageInfo == null) { + return false; + } mUsageState = mUsageBridge.getUsageInfo(mPackageName, mPackageInfo.applicationInfo.uid); diff --git a/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java b/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java index 532a92350fc..fba02c3d67d 100644 --- a/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java +++ b/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java @@ -17,6 +17,7 @@ package com.android.settings.applications; import android.content.Context; +import android.os.RemoteException; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.SettingsRobolectricTestRunner; @@ -65,4 +66,11 @@ public class UsageAccessDetailsTest { verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class), eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY), eq("app")); } + + @Test + public void refreshUi_nullPackageInfo_shouldNotCrash() throws RemoteException { + mFragment.mPackageInfo = null; + mFragment.refreshUi(); + // should not crash + } } From 3baa08b44f78aa82b642d08b0f72f7da4371351b Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 15 Sep 2017 14:48:46 -0700 Subject: [PATCH 14/17] Fixing issue with PiP settings not showing apps for other profiles. Bug: 65417722 Test: make -j40 RunSettingsRoboTests Change-Id: I3c3fc7a68c172eeccc767cd04b1393b38b36073e --- .../PictureInPictureSettings.java | 113 +++++++++--- .../settings/wrapper/UserManagerWrapper.java | 4 + .../PictureInPictureSettingsTest.java | 170 ++++++++++++++++++ 3 files changed, 262 insertions(+), 25 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java diff --git a/src/com/android/settings/applications/PictureInPictureSettings.java b/src/com/android/settings/applications/PictureInPictureSettings.java index d8e0b2bf8fc..5569a4ef785 100644 --- a/src/com/android/settings/applications/PictureInPictureSettings.java +++ b/src/com/android/settings/applications/PictureInPictureSettings.java @@ -22,14 +22,16 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; -import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.os.Bundle; import android.os.UserHandle; +import android.os.UserManager; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference.OnPreferenceClickListener; import android.support.v7.preference.PreferenceScreen; -import android.util.ArrayMap; +import android.util.IconDrawableFactory; +import android.util.Pair; import android.view.View; import com.android.internal.annotations.VisibleForTesting; @@ -37,9 +39,13 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.notification.EmptyTextSettings; import com.android.settings.wrapper.ActivityInfoWrapper; +import com.android.settings.wrapper.UserManagerWrapper; +import com.android.settingslib.wrapper.PackageManagerWrapper; +import java.text.Collator; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; public class PictureInPictureSettings extends EmptyTextSettings { @@ -51,8 +57,38 @@ public class PictureInPictureSettings extends EmptyTextSettings { IGNORE_PACKAGE_LIST.add("com.android.systemui"); } + /** + * Comparator by name, then user id. + * {@see PackageItemInfo#DisplayNameComparator} + */ + static class AppComparator implements Comparator> { + + private final Collator mCollator = Collator.getInstance(); + private final PackageManager mPm; + + public AppComparator(PackageManager pm) { + mPm = pm; + } + + public final int compare(Pair a, + Pair b) { + CharSequence sa = a.first.loadLabel(mPm); + if (sa == null) sa = a.first.name; + CharSequence sb = b.first.loadLabel(mPm); + if (sb == null) sb = b.first.name; + int nameCmp = mCollator.compare(sa.toString(), sb.toString()); + if (nameCmp != 0) { + return nameCmp; + } else { + return a.second - b.second; + } + } + } + private Context mContext; - private PackageManager mPackageManager; + private PackageManagerWrapper mPackageManager; + private UserManagerWrapper mUserManager; + private IconDrawableFactory mIconDrawableFactory; /** * @return true if the package has any activities that declare that they support @@ -94,12 +130,23 @@ public class PictureInPictureSettings extends EmptyTextSettings { return false; } + public PictureInPictureSettings() { + // Do nothing + } + + public PictureInPictureSettings(PackageManagerWrapper pm, UserManagerWrapper um) { + mPackageManager = pm; + mUserManager = um; + } + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); mContext = getActivity(); - mPackageManager = mContext.getPackageManager(); + mPackageManager = new PackageManagerWrapper(mContext.getPackageManager()); + mUserManager = new UserManagerWrapper(mContext.getSystemService(UserManager.class)); + mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext)); } @@ -111,33 +158,25 @@ public class PictureInPictureSettings extends EmptyTextSettings { final PreferenceScreen screen = getPreferenceScreen(); screen.removeAll(); - // Fetch the set of applications which have at least one activity that declare that they - // support picture-in-picture - final ArrayMap packageToState = new ArrayMap<>(); - final ArrayList pipApps = new ArrayList<>(); - final List installedPackages = mPackageManager.getInstalledPackagesAsUser( - GET_ACTIVITIES, UserHandle.myUserId()); - for (PackageInfo packageInfo : installedPackages) { - if (checkPackageHasPictureInPictureActivities(packageInfo.packageName, - packageInfo.activities)) { - final String packageName = packageInfo.applicationInfo.packageName; - final boolean state = PictureInPictureDetails.getEnterPipStateForPackage( - mContext, packageInfo.applicationInfo.uid, packageName); - pipApps.add(packageInfo.applicationInfo); - packageToState.put(packageName, state); - } - } - Collections.sort(pipApps, new PackageItemInfo.DisplayNameComparator(mPackageManager)); + // Fetch the set of applications for each profile which have at least one activity that + // declare that they support picture-in-picture + final PackageManager pm = mPackageManager.getPackageManager(); + final ArrayList> pipApps = + collectPipApps(UserHandle.myUserId()); + Collections.sort(pipApps, new AppComparator(pm)); // Rebuild the list of prefs final Context prefContext = getPrefContext(); - for (final ApplicationInfo appInfo : pipApps) { + for (final Pair appData : pipApps) { + final ApplicationInfo appInfo = appData.first; + final int userId = appData.second; + final UserHandle user = UserHandle.of(userId); final String packageName = appInfo.packageName; - final CharSequence label = appInfo.loadLabel(mPackageManager); + final CharSequence label = appInfo.loadLabel(pm); final Preference pref = new Preference(prefContext); - pref.setIcon(appInfo.loadIcon(mPackageManager)); - pref.setTitle(label); + pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId)); + pref.setTitle(pm.getUserBadgedLabel(label, user)); pref.setSummary(PictureInPictureDetails.getPreferenceSummary(prefContext, appInfo.uid, packageName)); pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { @@ -163,4 +202,28 @@ public class PictureInPictureSettings extends EmptyTextSettings { public int getMetricsCategory() { return MetricsEvent.SETTINGS_MANAGE_PICTURE_IN_PICTURE; } + + /** + * @return the list of applications for the given user and all their profiles that have + * activities which support PiP. + */ + ArrayList> collectPipApps(int userId) { + final ArrayList> pipApps = new ArrayList<>(); + final ArrayList userIds = new ArrayList<>(); + for (UserInfo user : mUserManager.getProfiles(userId)) { + userIds.add(user.id); + } + + for (int id : userIds) { + final List installedPackages = mPackageManager.getInstalledPackagesAsUser( + GET_ACTIVITIES, id); + for (PackageInfo packageInfo : installedPackages) { + if (checkPackageHasPictureInPictureActivities(packageInfo.packageName, + packageInfo.activities)) { + pipApps.add(new Pair<>(packageInfo.applicationInfo, id)); + } + } + } + return pipApps; + } } diff --git a/src/com/android/settings/wrapper/UserManagerWrapper.java b/src/com/android/settings/wrapper/UserManagerWrapper.java index eeb648bd102..4b4d2f4166a 100644 --- a/src/com/android/settings/wrapper/UserManagerWrapper.java +++ b/src/com/android/settings/wrapper/UserManagerWrapper.java @@ -41,4 +41,8 @@ public class UserManagerWrapper { public List getUsers() { return mUserManager.getUsers(); } + + public List getProfiles(int userHandle) { + return mUserManager.getProfiles(userHandle); + } } diff --git a/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java new file mode 100644 index 00000000000..d379dbd10a0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings.applications; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.UserInfo; +import android.util.Pair; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.TestConfig; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.wrapper.UserManagerWrapper; +import com.android.settingslib.wrapper.PackageManagerWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import java.util.ArrayList; +import java.util.Collections; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class PictureInPictureSettingsTest { + + private static final int PRIMARY_USER_ID = 0; + private static final int PROFILE_USER_ID = 10; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + + private FakeFeatureFactory mFeatureFactory; + private PictureInPictureSettings mFragment; + @Mock + private PackageManagerWrapper mPackageManager; + @Mock + private UserManagerWrapper mUserManager; + private ArrayList mPrimaryUserPackages; + private ArrayList mProfileUserPackages; + private ArrayList mUsers; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); + mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + mFragment = new PictureInPictureSettings(mPackageManager, mUserManager); + mPrimaryUserPackages = new ArrayList<>(); + mProfileUserPackages = new ArrayList<>(); + mUsers = new ArrayList<>(); + when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PRIMARY_USER_ID))) + .thenReturn(mPrimaryUserPackages); + when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PROFILE_USER_ID))) + .thenReturn(mProfileUserPackages); + when(mUserManager.getProfiles(anyInt())).thenReturn(mUsers); + } + + @Test + public void testCollectPipApps() { + PackageInfo primaryP1 = createPackage("Calculator", true); + PackageInfo primaryP2 = createPackage("Clock", false); + PackageInfo profileP1 = createPackage("Calculator", false); + PackageInfo profileP2 = createPackage("Clock", true); + + mPrimaryUserPackages.add(primaryP1); + mPrimaryUserPackages.add(primaryP2); + mProfileUserPackages.add(profileP1); + mProfileUserPackages.add(profileP2); + + ArrayList> apps = mFragment.collectPipApps(PRIMARY_USER_ID); + assertThat(containsPackages(apps, primaryP1, profileP2)); + assertThat(!containsPackages(apps, primaryP2, profileP1)); + } + + @Test + public void testAppSort() { + PackageInfo primaryP1 = createPackage("Android", true); + PackageInfo primaryP2 = createPackage("Boop", true); + PackageInfo primaryP3 = createPackage("Deck", true); + PackageInfo profileP1 = createPackage("Android", true); + PackageInfo profileP2 = createPackage("Cool", true); + PackageInfo profileP3 = createPackage("Fast", false); + + mPrimaryUserPackages.add(primaryP1); + mPrimaryUserPackages.add(primaryP2); + mPrimaryUserPackages.add(primaryP3); + mProfileUserPackages.add(profileP1); + mProfileUserPackages.add(profileP2); + mProfileUserPackages.add(profileP3); + + ArrayList> apps = mFragment.collectPipApps(PRIMARY_USER_ID); + Collections.sort(apps, new PictureInPictureSettings.AppComparator(null)); + assertThat(isOrdered(apps, primaryP1, profileP1, primaryP2, profileP2)); + } + + private boolean containsPackages(ArrayList> apps, + PackageInfo... packages) { + for (int i = 0; i < packages.length; i++) { + boolean found = false; + for (int j = 0; j < apps.size(); j++) { + if (apps.get(j).first == packages[i].applicationInfo) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; + } + + private boolean isOrdered(ArrayList> apps, + PackageInfo... packages) { + if (apps.size() != packages.length) { + return false; + } + + for (int i = 0; i < packages.length; i++) { + if (packages[i].applicationInfo != apps.get(i).first) { + return false; + } + } + return true; + } + + private PackageInfo createPackage(String appTitle, boolean supportsPip) { + PackageInfo pi = new PackageInfo(); + ActivityInfo ai = new ActivityInfo(); + if (supportsPip) { + ai.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; + } + pi.activities = new ActivityInfo[1]; + pi.activities[0] = ai; + pi.applicationInfo = new ApplicationInfo(); + pi.applicationInfo.name = appTitle; + return pi; + } +} From 1fb3ce01d0cc3848b99d71d9df589fa4abc0c6fd Mon Sep 17 00:00:00 2001 From: Maurice Lam Date: Thu, 21 Sep 2017 11:33:55 -0700 Subject: [PATCH 15/17] Fix pattern header string usage Test: Builds with ag/2933970 cherry-picked on top Bug: 66446463 Change-Id: I0c649427474cd9b1cf388f45407deea582d01c9a --- res/layout/choose_lock_pattern_common.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/layout/choose_lock_pattern_common.xml b/res/layout/choose_lock_pattern_common.xml index 65135ddc90c..949d1303293 100644 --- a/res/layout/choose_lock_pattern_common.xml +++ b/res/layout/choose_lock_pattern_common.xml @@ -24,7 +24,7 @@ android:icon="@drawable/ic_lock" android:layout="@layout/suw_glif_blank_template" settings:suwFooter="@layout/choose_lock_pattern_common_footer" - settings:suwHeaderText="@string/lockpassword_choose_your_pattern_header"> + settings:suwHeaderText="@string/lockpassword_choose_your_screen_lock_header"> Date: Mon, 18 Sep 2017 14:45:49 -0700 Subject: [PATCH 16/17] Introduce PictureColorModePreferenceController - Create new PictureColorModePreferenceController - Create controller inside the DashboardFragment - Refactor ColorModePreference so isAvailable() no longer depends on ColorModePreference - Port logic from DevelopmentSettings into the controller Bug: 34203528 Test: make RunSettingsRoboTests -j40 Change-Id: I8dff5b0d5ad1d7f043fc7ead540b2c0c960933e3 --- .../development/ColorModePreference.java | 41 +++--- .../DevelopmentSettingsDashboardFragment.java | 2 +- .../PictureColorModePreferenceController.java | 99 +++++++++++++ ...elopmentSettingsDashboardFragmentTest.java | 12 ++ ...tureColorModePreferenceControllerTest.java | 137 ++++++++++++++++++ 5 files changed, 273 insertions(+), 18 deletions(-) create mode 100644 src/com/android/settings/development/PictureColorModePreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/development/PictureColorModePreferenceControllerTest.java diff --git a/src/com/android/settings/development/ColorModePreference.java b/src/com/android/settings/development/ColorModePreference.java index e0b0837d800..20af2c62ac8 100644 --- a/src/com/android/settings/development/ColorModePreference.java +++ b/src/com/android/settings/development/ColorModePreference.java @@ -28,6 +28,7 @@ import android.view.Display; import com.android.settings.R; import java.util.ArrayList; +import java.util.List; public class ColorModePreference extends SwitchPreference implements DisplayListener { @@ -35,7 +36,28 @@ public class ColorModePreference extends SwitchPreference implements DisplayList private Display mDisplay; private int mCurrentIndex; - private ArrayList mDescriptions; + private List mDescriptions; + + public static List getColorModeDescriptions(Context context) { + + List colorModeDescriptions = new ArrayList<>(); + Resources resources = context.getResources(); + int[] colorModes = resources.getIntArray(R.array.color_mode_ids); + String[] titles = resources.getStringArray(R.array.color_mode_names); + String[] descriptions = resources.getStringArray(R.array.color_mode_descriptions); + // Map the resource information describing color modes. + for (int i = 0; i < colorModes.length; i++) { + if (colorModes[i] != -1 && i != 1 /* Skip Natural for now. */) { + ColorModeDescription desc = new ColorModeDescription(); + desc.colorMode = colorModes[i]; + desc.title = titles[i]; + desc.summary = descriptions[i]; + colorModeDescriptions.add(desc); + } + } + + return colorModeDescriptions; + } public ColorModePreference(Context context, AttributeSet attrs) { super(context, attrs); @@ -75,22 +97,7 @@ public class ColorModePreference extends SwitchPreference implements DisplayList public void updateCurrentAndSupported() { mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); - mDescriptions = new ArrayList<>(); - - Resources resources = getContext().getResources(); - int[] colorModes = resources.getIntArray(R.array.color_mode_ids); - String[] titles = resources.getStringArray(R.array.color_mode_names); - String[] descriptions = resources.getStringArray(R.array.color_mode_descriptions); - // Map the resource information describing color modes. - for (int i = 0; i < colorModes.length; i++) { - if (colorModes[i] != -1 && i != 1 /* Skip Natural for now. */) { - ColorModeDescription desc = new ColorModeDescription(); - desc.colorMode = colorModes[i]; - desc.title = titles[i]; - desc.summary = descriptions[i]; - mDescriptions.add(desc); - } - } + mDescriptions = getColorModeDescriptions(getContext()); int currentColorMode = mDisplay.getColorMode(); mCurrentIndex = -1; diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 06623086b25..d334bd05185 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -181,7 +181,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new OemUnlockPreferenceController(context, activity, fragment)); // running services // convert to file encryption - // picture color mode + controllers.add(new PictureColorModePreferenceController(context, lifecycle)); // webview implementation // cool color temperature // automatic system updates diff --git a/src/com/android/settings/development/PictureColorModePreferenceController.java b/src/com/android/settings/development/PictureColorModePreferenceController.java new file mode 100644 index 00000000000..fe4755ff409 --- /dev/null +++ b/src/com/android/settings/development/PictureColorModePreferenceController.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import android.content.Context; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +public class PictureColorModePreferenceController extends + DeveloperOptionsPreferenceController implements + LifecycleObserver, OnResume, OnPause { + + private static final String KEY_COLOR_MODE = "picture_color_mode"; + + private ColorModePreference mPreference; + + public PictureColorModePreferenceController(Context context, Lifecycle lifecycle) { + super(context); + + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public boolean isAvailable() { + return getColorModeDescriptionsSize() > 1 && !isWideColorGamut(); + } + + @Override + public String getPreferenceKey() { + return KEY_COLOR_MODE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = (ColorModePreference) screen.findPreference(getPreferenceKey()); + if (mPreference != null) { + mPreference.updateCurrentAndSupported(); + } + } + + @Override + public void onResume() { + if (mPreference == null) { + return; + } + mPreference.startListening(); + mPreference.updateCurrentAndSupported(); + } + + @Override + public void onPause() { + if (mPreference == null) { + return; + } + mPreference.stopListening(); + } + + @Override + protected void onDeveloperOptionsSwitchEnabled() { + mPreference.setEnabled(true); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + mPreference.setEnabled(false); + } + + @VisibleForTesting + boolean isWideColorGamut() { + return mContext.getDisplay().isWideColorGamut(); + } + + @VisibleForTesting + int getColorModeDescriptionsSize() { + return ColorModePreference.getColorModeDescriptions(mContext).size(); + } +} diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java index c8748deeac3..13f73742d65 100644 --- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java @@ -112,6 +112,9 @@ public class DevelopmentSettingsDashboardFragmentTest { } @Test + @Config(shadows = { + ShadowPictureColorModePreferenceController.class + }) public void searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable() { final Context appContext = RuntimeEnvironment.application; DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, true); @@ -198,4 +201,13 @@ public class DevelopmentSettingsDashboardFragmentTest { mShown = true; } } + + @Implements(PictureColorModePreferenceController.class) + public static class ShadowPictureColorModePreferenceController { + + @Implementation + public boolean isAvailable() { + return true; + } + } } diff --git a/tests/robotests/src/com/android/settings/development/PictureColorModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/PictureColorModePreferenceControllerTest.java new file mode 100644 index 00000000000..5cf4e10a4f2 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/PictureColorModePreferenceControllerTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class PictureColorModePreferenceControllerTest { + + @Mock + private ColorModePreference mPreference; + @Mock + private Context mContext; + @Mock + private PreferenceScreen mPreferenceScreen; + @Mock + private Resources mResources; + + private Lifecycle mLifecycle; + private PictureColorModePreferenceController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mLifecycle = new Lifecycle(); + mController = new PictureColorModePreferenceController(mContext, mLifecycle); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( + mPreference); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getIntArray(R.array.color_mode_ids)).thenReturn(new int[0]); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void isAvailable_shouldReturnFalseWhenWideColorGambit() { + mController = spy(mController); + doReturn(2).when(mController).getColorModeDescriptionsSize(); + doReturn(true).when(mController).isWideColorGamut(); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isAvailable_shouldReturnTrueWhenNotWideColorGambit() { + mController = spy(mController); + doReturn(2).when(mController).getColorModeDescriptionsSize(); + doReturn(false).when(mController).isWideColorGamut(); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_shouldReturnFalseWhenColorCountIsOne() { + mController = spy(mController); + doReturn(1).when(mController).getColorModeDescriptionsSize(); + doReturn(true).when(mController).isWideColorGamut(); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isAvailable_shouldReturnTrueWhenColorCountIsTwo() { + mController = spy(mController); + doReturn(2).when(mController).getColorModeDescriptionsSize(); + doReturn(false).when(mController).isWideColorGamut(); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void onDeveloperOptionEnabled_shouldEnablePreference() { + mController = spy(mController); + doReturn(true).when(mController).isAvailable(); + mController.onDeveloperOptionsEnabled(); + + verify(mPreference).setEnabled(true); + } + + @Test + public void onDeveloperOptionDisabled_shouldDisablePreference() { + mController = spy(mController); + doReturn(true).when(mController).isAvailable(); + mController.onDeveloperOptionsDisabled(); + + verify(mPreference).setEnabled(false); + } + + @Test + public void onResume_shouldStartListening() { + mLifecycle.onResume(); + + verify(mPreference).startListening(); + } + + @Test + public void onPause_shouldStopListening() { + mLifecycle.onPause(); + + verify(mPreference).stopListening(); + } +} From 097f4184fa2eab8afcb6279b6c109fa11fbd86a1 Mon Sep 17 00:00:00 2001 From: Ng Zhi An Date: Wed, 20 Sep 2017 15:17:29 -0700 Subject: [PATCH 17/17] Fix icon for sim card in settings Change-Id: I1e235cad1712343e2990eab114fd2ef383aac77f Bug: 65574460 Fixes: 65574460 Test: manually build and install Settings apk and open app to check --- res/drawable/ic_settings_sim.xml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/res/drawable/ic_settings_sim.xml b/res/drawable/ic_settings_sim.xml index ca548cfefad..d083c9de6cf 100644 --- a/res/drawable/ic_settings_sim.xml +++ b/res/drawable/ic_settings_sim.xml @@ -15,7 +15,14 @@ limitations under the License. --> - - + + + +