Merge "Add feature flag for settings slice injection"
diff --git a/OWNERS b/OWNERS
index d2bc1ff..37124b2 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,8 +7,11 @@
dehboxturtle@google.com
dhnishi@google.com
dling@google.com
+edgarwang@google.com
jackqdyulei@google.com
mfritze@google.com
+rafftsai@google.com
+tmfang@google.com
zhfan@google.com
# Emergency approvers in case the above are not available
diff --git a/res/drawable/ic_battery_saver_accent_24dp.xml b/res/drawable/ic_battery_saver_accent_24dp.xml
deleted file mode 100644
index c8def54..0000000
--- a/res/drawable/ic_battery_saver_accent_24dp.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
- <path
- android:fillColor="#FF000000"
- android:pathData="M15,14l-2,0l0,2l-2,0l0,-2l-2,0l0,-2l2,0l0,-2l2,0l0,2l2,0z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M16.2,22.5H7.8c-1.3,0 -2.3,-1 -2.3,-2.3V5.8c0,-1.3 1,-2.3 2.3,-2.3h0.7v-2h7v2h0.7c1.3,0 2.3,1.1 2.3,2.3v14.3C18.5,21.5 17.5,22.5 16.2,22.5zM7.8,5.5c-0.2,0 -0.3,0.2 -0.3,0.3v14.3c0,0.2 0.2,0.3 0.3,0.3h8.3c0.2,0 0.3,-0.1 0.3,-0.3V5.8c0,-0.2 -0.1,-0.3 -0.3,-0.3h-2.7v-2h-3v2H7.8z"/>
-</vector>
diff --git a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
index 0c938f8..fb50def 100644
--- a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
@@ -24,20 +24,19 @@
<include layout="@layout/wifi_dpp_fragment_header"/>
- <FrameLayout
+ <com.android.settings.wifi.qrcode.QrPreviewLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <SurfaceView
+ android:layout_height="match_parent"
+ android:layout_gravity="center">
+ <TextureView
android:id="@+id/preview_view"
- android:layout_width="426dp"
- android:layout_height="320dp"
- android:layout_gravity="center"/>
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
<com.android.settings.wifi.qrcode.QrDecorateView
android:id="@+id/decorate_view"
- android:layout_width="426dp"
- android:layout_height="320dp"
- android:layout_gravity="center"/>
- </FrameLayout>
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+ </com.android.settings.wifi.qrcode.QrPreviewLayout>
<TextView android:id="@+id/error_message"
android:layout_width="wrap_content"
diff --git a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
index 0beacff..913998f 100644
--- a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
@@ -24,20 +24,20 @@
<include layout="@layout/wifi_dpp_fragment_header"/>
- <FrameLayout
+ <com.android.settings.wifi.qrcode.QrPreviewLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <SurfaceView
+ android:layout_height="match_parent">
+ <TextureView
android:id="@+id/preview_view"
- android:layout_width="320dp"
- android:layout_height="426dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
android:layout_gravity="center"/>
<com.android.settings.wifi.qrcode.QrDecorateView
android:id="@+id/decorate_view"
- android:layout_width="320dp"
- android:layout_height="426dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
android:layout_gravity="center"/>
- </FrameLayout>
+ </com.android.settings.wifi.qrcode.QrPreviewLayout>
<TextView android:id="@+id/error_message"
android:layout_width="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d042ebc..be0369a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2071,6 +2071,8 @@
<string name="wifi_dpp_scan_qr_code">Scan QR code</string>
<!-- Hint for the user to center another device's QR code in the below camera window [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_scan_qr_code_join_network">Join \u201c<xliff:g id="ssid" example="OfficeWifi">%1$s</xliff:g>\u201d by scanning a QR code</string>
+ <!-- Hint for the user to center another device's QR code in the below camera window [CHAR LIMIT=NONE] -->
+ <string name="wifi_dpp_scan_qr_code_join_unknown_network">Join Wi\u2011Fi by scanning a QR code</string>
<!-- Title for the fragment to share Wi-Fi [CHAR LIMIT=50] -->
<string name="wifi_dpp_share_wifi">Share Wi\u2011Fi</string>
<!-- Hint for the user to use another device to scan QR code on screen to join Wi-Fi [CHAR LIMIT=NONE] -->
@@ -9019,12 +9021,6 @@
<!-- Summary of condition that do not disturb is on [CHAR LIMIT=36] -->
<string name="condition_zen_summary">Impacts what you hear and see</string>
- <!-- Title of condition that battery saver is on [CHAR LIMIT=30] -->
- <string name="condition_battery_title">Battery Saver is on</string>
-
- <!-- Summary of condition that battery saver is on [CHAR LIMIT=NONE] -->
- <string name="condition_battery_summary">Features restricted</string>
-
<!-- Title of condition that cellular data is off [CHAR LIMIT=50] -->
<string name="condition_cellular_title">Mobile data is off</string>
diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml
index a086018..89f7eab 100644
--- a/res/xml/battery_saver_settings.xml
+++ b/res/xml/battery_saver_settings.xml
@@ -26,18 +26,6 @@
android:title="@string/battery_saver_schedule_settings_title"
settings:controller="com.android.settings.fuelgauge.batterysaver.BatterySaverSchedulePreferenceController"/>
- <!-- Turn on automatically -->
- <SwitchPreference
- android:key="auto_battery_saver"
- android:title="@string/battery_saver_auto_title"
- settings:controller="com.android.settings.fuelgauge.batterysaver.AutoBatterySaverPreferenceController" />
-
- <com.android.settings.widget.SeekBarPreference
- android:key="battery_saver_seek_bar"
- android:title="@string/battery_saver_seekbar_title_placeholder"
- android:max="15"
- android:min="1" />
-
<com.android.settings.widget.TwoStateButtonPreference
android:key="battery_saver"
android:title="@string/battery_saver"
diff --git a/res/xml/network_and_internet_v2.xml b/res/xml/network_and_internet_v2.xml
index 974739d..8e0b426 100644
--- a/res/xml/network_and_internet_v2.xml
+++ b/res/xml/network_and_internet_v2.xml
@@ -18,8 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="network_and_internet_screen"
- android:title="@string/network_dashboard_title"
- settings:initialExpandedChildrenCount="5">
+ android:title="@string/network_dashboard_title">
<PreferenceCategory
android:key="multi_network_header"
diff --git a/src/com/android/settings/fuelgauge/BatterySaverController.java b/src/com/android/settings/fuelgauge/BatterySaverController.java
index 28cc893..cd32c6c 100644
--- a/src/com/android/settings/fuelgauge/BatterySaverController.java
+++ b/src/com/android/settings/fuelgauge/BatterySaverController.java
@@ -15,6 +15,7 @@
*/
package com.android.settings.fuelgauge;
+import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
@@ -22,6 +23,7 @@
import android.os.PowerManager;
import android.provider.Settings;
+import android.provider.Settings.Global;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -31,6 +33,7 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
public class BatterySaverController extends BasePreferenceController
implements LifecycleObserver, OnStart, OnStop, BatterySaverReceiver.BatterySaverListener {
@@ -45,6 +48,7 @@
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBatteryStateChangeReceiver = new BatterySaverReceiver(context);
mBatteryStateChangeReceiver.setBatterySaverListener(this);
+ BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
}
@Override
@@ -81,16 +85,23 @@
@Override
public CharSequence getSummary() {
+ final ContentResolver resolver = mContext.getContentResolver();
final boolean isPowerSaveOn = mPowerManager.isPowerSaveMode();
- final int percent = Settings.Global.getInt(mContext.getContentResolver(),
+ final int percent = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ final int mode = Settings.Global.getInt(resolver,
+ Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE);
if (isPowerSaveOn) {
return mContext.getString(R.string.battery_saver_on_summary);
- } else if (percent != 0) {
- return mContext.getString(R.string.battery_saver_off_scheduled_summary,
- Utils.formatPercentage(percent));
+ } else if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+ if (percent != 0) {
+ return mContext.getString(R.string.battery_saver_off_scheduled_summary,
+ Utils.formatPercentage(percent));
+ } else {
+ return mContext.getString(R.string.battery_saver_off_summary);
+ }
} else {
- return mContext.getString(R.string.battery_saver_off_summary);
+ return mContext.getString(R.string.battery_saver_auto_routine);
}
}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceController.java
deleted file mode 100644
index c0ecbf9..0000000
--- a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceController.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterysaver;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-
-import com.android.settings.core.TogglePreferenceController;
-import com.android.settingslib.fuelgauge.BatterySaverUtils;
-
-/**
- * Controller that update whether to turn on battery saver automatically
- */
-public class AutoBatterySaverPreferenceController extends TogglePreferenceController implements
- Preference.OnPreferenceChangeListener {
-
- /**
- * Default value for {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL}.
- */
- static final int DEFAULT_TRIGGER_LEVEL = 0;
-
- /**
- * The default value to set to {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL} when the
- * user enables battery saver.
- */
- private final int mDefaultTriggerLevelForOn;
-
- @VisibleForTesting
- static final String KEY_AUTO_BATTERY_SAVER = "auto_battery_saver";
-
- public AutoBatterySaverPreferenceController(Context context) {
- super(context, KEY_AUTO_BATTERY_SAVER);
- mDefaultTriggerLevelForOn = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryWarningLevel);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public boolean isChecked() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, DEFAULT_TRIGGER_LEVEL) != 0;
- }
-
- @Override
- public boolean setChecked(boolean isChecked) {
- BatterySaverUtils.setAutoBatterySaverTriggerLevel(mContext,
- isChecked ? mDefaultTriggerLevelForOn : 0);
- return true;
- }
-}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java
deleted file mode 100644
index 0fbba0b..0000000
--- a/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceController.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterysaver;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.widget.SeekBarPreference;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-/**
- * Controller that update the battery saver seekbar
- */
-public class AutoBatterySeekBarPreferenceController extends BasePreferenceController implements
- LifecycleObserver, OnStart, OnStop, SeekBarPreference.OnPreferenceChangeListener {
- private static final String TAG = "AutoBatterySeekBarPreferenceController";
- private static final int INTERVAL = 5;
- @VisibleForTesting
- static final String KEY_AUTO_BATTERY_SEEK_BAR = "battery_saver_seek_bar";
- private SeekBarPreference mPreference;
- private AutoBatterySaverSettingObserver mContentObserver;
-
- public AutoBatterySeekBarPreferenceController(Context context, Lifecycle lifecycle) {
- super(context, KEY_AUTO_BATTERY_SEEK_BAR);
- mContentObserver = new AutoBatterySaverSettingObserver(new Handler(Looper.getMainLooper()));
- if (lifecycle != null) {
- lifecycle.addObserver(this);
- }
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mPreference = (SeekBarPreference) screen.findPreference(
- KEY_AUTO_BATTERY_SEEK_BAR);
- mPreference.setContinuousUpdates(true);
- mPreference.setAccessibilityRangeInfoType(
- AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_PERCENT);
- updatePreference(mPreference);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public void updateState(Preference preference) {
- super.updateState(preference);
- updatePreference(preference);
- }
-
- @Override
- public void onStart() {
- mContentObserver.registerContentObserver();
- }
-
- @Override
- public void onStop() {
- mContentObserver.unRegisterContentObserver();
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final int progress = (int) newValue;
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, progress * INTERVAL);
- return true;
- }
-
- @VisibleForTesting
- void updatePreference(Preference preference) {
- final ContentResolver contentResolver = mContext.getContentResolver();
-
- // Override the max value with LOW_POWER_MODE_TRIGGER_LEVEL_MAX, if set.
- final int maxLevel = Settings.Global.getInt(contentResolver,
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 0) / INTERVAL;
- if (maxLevel > 0) {
- if (!(preference instanceof SeekBarPreference)) {
- Log.e(TAG, "Unexpected preference class: " + preference.getClass());
- } else {
- final SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
- if (maxLevel < seekBarPreference.getMin()) {
- Log.e(TAG, "LOW_POWER_MODE_TRIGGER_LEVEL_MAX too low; ignored.");
- } else {
- seekBarPreference.setMax(maxLevel);
- }
- }
- }
-
- // Set the current value.
- final int level = Settings.Global.getInt(contentResolver,
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL,
- AutoBatterySaverPreferenceController.DEFAULT_TRIGGER_LEVEL);
- if (level == 0) {
- preference.setVisible(false);
- } else {
- preference.setVisible(true);
- preference.setTitle(mContext.getString(R.string.battery_saver_seekbar_title,
- Utils.formatPercentage(level)));
- SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
- seekBarPreference.setProgress(level / INTERVAL);
- seekBarPreference.setSeekBarContentDescription(
- mContext.getString(R.string.battery_saver_turn_on_automatically_title));
- }
- }
-
- /**
- * Observer that listens to change from {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL}
- */
- private final class AutoBatterySaverSettingObserver extends ContentObserver {
- private final Uri mUri = Settings.Global.getUriFor(
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL);
- private final ContentResolver mContentResolver;
-
- public AutoBatterySaverSettingObserver(Handler handler) {
- super(handler);
- mContentResolver = mContext.getContentResolver();
- }
-
- public void registerContentObserver() {
- mContentResolver.registerContentObserver(mUri, false, this);
- }
-
- public void unRegisterContentObserver() {
- mContentResolver.unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri, int userId) {
- if (mUri.equals(uri)) {
- updatePreference(mPreference);
- }
- }
- }
-}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
index cc6aa00..868c73f 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
@@ -26,7 +26,13 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
+/**
+ * Simple controller to navigate users to the scheduling page from
+ * "Settings > Battery > Battery Saver". Also updates the summary for preference based on
+ * the currently selected settings.
+ */
public class BatterySaverSchedulePreferenceController extends BasePreferenceController {
@VisibleForTesting
@@ -36,6 +42,7 @@
public BatterySaverSchedulePreferenceController(Context context) {
super(context, KEY_BATTERY_SAVER_SCHEDULE);
+ BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
}
@Override
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
new file mode 100644
index 0000000..d49eb0a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.fuelgauge.batterysaver;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+
+/**
+ * Responds to user actions in the Settings > Battery > Set a Schedule Screen
+ *
+ * Note that this is not a preference controller since that screen does not inherit from
+ * DashboardFragment.
+ *
+ * Will call the appropriate power manager APIs and modify the correct settings to enable
+ * users to control their automatic battery saver toggling preferences.
+ * See {@link Settings.Global#AUTOMATIC_POWER_SAVER_MODE} for more details.
+ */
+public class BatterySaverScheduleRadioButtonsController {
+
+ public static final String KEY_NO_SCHEDULE = "key_battery_saver_no_schedule";
+ public static final String KEY_ROUTINE = "key_battery_saver_routine";
+ public static final String KEY_PERCENTAGE = "key_battery_saver_percentage";
+
+ private Context mContext;
+ private BatterySaverScheduleSeekBarController mSeekBarController;
+
+ public BatterySaverScheduleRadioButtonsController(Context context,
+ BatterySaverScheduleSeekBarController seekbar) {
+ mContext = context;
+ mSeekBarController = seekbar;
+ }
+
+ public String getDefaultKey() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ // Note: this can also be obtained via PowerManager.getPowerSaveMode()
+ final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ // if mode is "dynamic" we are in routine mode, percentage with non-zero threshold is
+ // percentage mode, otherwise it is no schedule mode
+ if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+ final int threshold =
+ Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ if (threshold <= 0) {
+ return KEY_NO_SCHEDULE;
+ }
+ return KEY_PERCENTAGE;
+ }
+ return KEY_ROUTINE;
+ }
+
+ public boolean setDefaultKey(String key) {
+ final ContentResolver resolver = mContext.getContentResolver();
+ switch(key) {
+ case KEY_NO_SCHEDULE:
+ Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ break;
+ case KEY_PERCENTAGE:
+ Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+ break;
+ case KEY_ROUTINE:
+ Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_DYNAMIC);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Not a valid key for " + this.getClass().getSimpleName());
+ }
+ mSeekBarController.updateSeekBar();
+ return true;
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
new file mode 100644
index 0000000..215f400
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.fuelgauge.batterysaver;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.PreferenceScreen;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.widget.SeekBarPreference;
+
+/**
+ * Responds to user actions in the Settings > Battery > Set a Schedule Screen for the seekbar.
+ * Note that this seekbar is only visible when the radio button selected is "Percentage".
+ *
+ * Note that this is not a preference controller since that screen does not inherit from
+ * DashboardFragment.
+ *
+ * Will call the appropriate power manager APIs and modify the correct settings to enable
+ * users to control their automatic battery saver toggling preferences.
+ * See {@link Settings.Global#AUTOMATIC_POWER_SAVER_MODE} for more details.
+ */
+public class BatterySaverScheduleSeekBarController implements
+ OnPreferenceChangeListener {
+
+ public static final int MAX_SEEKBAR_VALUE = 15;
+ public static final int MIN_SEEKBAR_VALUE = 1;
+ public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar";
+
+ @VisibleForTesting
+ public SeekBarPreference mSeekBarPreference;
+ private Context mContext;
+
+ public BatterySaverScheduleSeekBarController(Context context) {
+ mContext = context;
+ mSeekBarPreference = new SeekBarPreference(context);
+ mSeekBarPreference.setOnPreferenceChangeListener(this);
+ mSeekBarPreference.setMax(BatterySaverScheduleSeekBarController.MAX_SEEKBAR_VALUE);
+ mSeekBarPreference.setMin(BatterySaverScheduleSeekBarController.MIN_SEEKBAR_VALUE);
+ mSeekBarPreference.setKey(KEY_BATTERY_SAVER_SEEK_BAR);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ // The nits are in intervals of 5%
+ final int percentage = ((Integer) newValue) * 5;
+ Settings.Global.putInt(mContext.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL,
+ percentage);
+ preference.setTitle(mContext.getString(
+ R.string.battery_saver_seekbar_title, Utils.formatPercentage(percentage)));
+ return true;
+ }
+
+ public void updateSeekBar() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ // Note: this can also be obtained via PowerManager.getPowerSaveMode()
+ final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ // if mode is "dynamic" we are in routine mode, percentage with non-zero threshold is
+ // percentage mode, otherwise it is no schedule mode
+ if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+ final int threshold =
+ Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ if (threshold <= 0) {
+ mSeekBarPreference.setVisible(false);
+ } else {
+ mSeekBarPreference.setVisible(true);
+ mSeekBarPreference.setProgress(MIN_SEEKBAR_VALUE);
+ mSeekBarPreference.setTitle(mContext.getString(
+ R.string.battery_saver_seekbar_title,
+ Utils.formatPercentage(MIN_SEEKBAR_VALUE * 5)));
+ }
+ } else {
+ mSeekBarPreference.setVisible(false);
+ }
+ }
+
+ public void addToScreen(PreferenceScreen screen) {
+ screen.addPreference(mSeekBarPreference);
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
index fad9f31..6e9c711 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java
@@ -18,24 +18,32 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.TextUtils;
import android.view.View;
import androidx.preference.PreferenceScreen;
import com.android.settings.widget.RadioButtonPickerFragment;
import com.android.settings.R;
import com.android.settings.widget.RadioButtonPreference;
-import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.widget.CandidateInfo;
import com.google.common.collect.Lists;
import java.util.List;
+/**
+ * Fragment that allows users to customize their automatic battery saver mode settings.
+ *
+ * Location: Settings > Battery > Battery Saver > Set a Schedule
+ * See {@link BatterySaverSchedulePreferenceController} for the controller that manages navigation
+ * to this screen from "Settings > Battery > Battery Saver" and the summary.
+ * See {@link BatterySaverScheduleRadioButtonsController} &
+ * {@link BatterySaverScheduleSeekBarController} for the controller that manages user
+ * interactions in this screen.
+ */
public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
- private static final String KEY_NO_SCHEDULE = "key_battery_saver_no_schedule";
- private static final String KEY_ROUTINE = "key_battery_saver_routine";
- private static final String KEY_PERCENTAGE = "key_battery_saver_percentage";
- public static final int MAX_SEEKBAR_VALUE = 15;
- public static final int MIN_SEEKBAR_VALUE = 1;
- public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar";
+ public BatterySaverScheduleRadioButtonsController mRadioButtonController;
+ private BatterySaverScheduleSeekBarController mSeekBarController;
@Override
protected int getPreferenceScreenResId() {
@@ -43,23 +51,44 @@
}
@Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mSeekBarController = new BatterySaverScheduleSeekBarController(context);
+ mRadioButtonController = new BatterySaverScheduleRadioButtonsController(
+ context, mSeekBarController);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
protected List<? extends CandidateInfo> getCandidates() {
Context context = getContext();
List<CandidateInfo> candidates = Lists.newArrayList();
+ String routineProviderApp = getContext().getResources()
+ .getString(com.android.internal.R.string.config_batterySaverScheduleProvider);
candidates.add(new BatterySaverScheduleCandidateInfo(
context.getText(R.string.battery_saver_auto_no_schedule),
/* summary */ null,
- KEY_NO_SCHEDULE,
+ BatterySaverScheduleRadioButtonsController.KEY_NO_SCHEDULE,
/* enabled */ true));
- candidates.add(new BatterySaverScheduleCandidateInfo(
- context.getText(R.string.battery_saver_auto_routine),
- context.getText(R.string.battery_saver_auto_routine_summary),
- KEY_ROUTINE,
- /* enabled */ true));
+ // only add routine option if an app has been specified
+ if (!TextUtils.isEmpty(routineProviderApp)) {
+ candidates.add(new BatterySaverScheduleCandidateInfo(
+ context.getText(R.string.battery_saver_auto_routine),
+ context.getText(R.string.battery_saver_auto_routine_summary),
+ BatterySaverScheduleRadioButtonsController.KEY_ROUTINE,
+ /* enabled */ true));
+ } else {
+ // Make sure routine is not selected if no provider app is configured
+ BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
+ }
candidates.add(new BatterySaverScheduleCandidateInfo(
context.getText(R.string.battery_saver_auto_percentage),
/* summary */ null,
- KEY_PERCENTAGE,
+ BatterySaverScheduleRadioButtonsController.KEY_PERCENTAGE,
/* enabled */ true));
return candidates;
@@ -79,22 +108,18 @@
@Override
protected void addStaticPreferences(PreferenceScreen screen) {
- SeekBarPreference seekbar = new SeekBarPreference(getContext());
- seekbar.setMax(MAX_SEEKBAR_VALUE);
- seekbar.setMin(MIN_SEEKBAR_VALUE);
- seekbar.setTitle(R.string.battery_saver_seekbar_title_placeholder);
- seekbar.setKey(KEY_BATTERY_SAVER_SEEK_BAR);
- screen.addPreference(seekbar);
+ mSeekBarController.updateSeekBar();
+ mSeekBarController.addToScreen(screen);
}
@Override
protected String getDefaultKey() {
- return null;
+ return mRadioButtonController.getDefaultKey();
}
@Override
protected boolean setDefaultKey(String key) {
- return false;
+ return mRadioButtonController.setDefaultKey(key);
}
@Override
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java
index 8b0409c..6aef9b5 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSettings.java
@@ -61,23 +61,10 @@
}
@Override
- protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
- return buildPreferenceControllers(context, getSettingsLifecycle());
- }
-
- @Override
public int getHelpResource() {
return R.string.help_url_battery_saver_settings;
}
- private static List<AbstractPreferenceController> buildPreferenceControllers(
- Context context, Lifecycle lifecycle) {
- final List<AbstractPreferenceController> controllers = new ArrayList<>();
- controllers.add(new AutoBatterySaverPreferenceController(context));
- controllers.add(new AutoBatterySeekBarPreferenceController(context, lifecycle));
- return controllers;
- }
-
/**
* For Search.
*/
@@ -90,11 +77,5 @@
sir.xmlResId = R.xml.battery_saver_settings;
return Arrays.asList(sir);
}
-
- @Override
- public List<AbstractPreferenceController> createPreferenceControllers(
- Context context) {
- return buildPreferenceControllers(context, null);
- }
};
}
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java b/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java
deleted file mode 100644
index bce7c5d..0000000
--- a/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.conditional;
-
-import android.content.Context;
-import android.os.PowerManager;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.R;
-import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.fuelgauge.BatterySaverReceiver;
-import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
-import com.android.settings.homepage.contextualcards.ContextualCard;
-import com.android.settingslib.fuelgauge.BatterySaverUtils;
-
-import java.util.Objects;
-
-public class BatterySaverConditionController implements ConditionalCardController,
- BatterySaverReceiver.BatterySaverListener {
- static final int ID = Objects.hash("BatterySaverConditionController");
-
- private final Context mAppContext;
- private final ConditionManager mConditionManager;
- private final BatterySaverReceiver mReceiver;
- private final PowerManager mPowerManager;
-
- public BatterySaverConditionController(Context appContext, ConditionManager conditionManager) {
- mAppContext = appContext;
- mConditionManager = conditionManager;
- mPowerManager = appContext.getSystemService(PowerManager.class);
- mReceiver = new BatterySaverReceiver(appContext);
- mReceiver.setBatterySaverListener(this);
- }
-
- @Override
- public long getId() {
- return ID;
- }
-
- @Override
- public boolean isDisplayable() {
- return mPowerManager.isPowerSaveMode();
- }
-
- @Override
- public void onPrimaryClick(Context context) {
- new SubSettingLauncher(context)
- .setDestination(BatterySaverSettings.class.getName())
- .setSourceMetricsCategory(MetricsProto.MetricsEvent.DASHBOARD_SUMMARY)
- .setTitleRes(R.string.battery_saver)
- .launch();
- }
-
- @Override
- public void onActionClick() {
- BatterySaverUtils.setPowerSaveMode(mAppContext, false,
- /*needFirstTimeWarning*/ false);
- }
-
- @Override
- public ContextualCard buildContextualCard() {
- return new ConditionalContextualCard.Builder()
- .setConditionId(ID)
- .setMetricsConstant(MetricsProto.MetricsEvent.SETTINGS_CONDITION_BATTERY_SAVER)
- .setActionText(mAppContext.getText(R.string.condition_turn_off))
- .setName(mAppContext.getPackageName() + "/"
- + mAppContext.getText(R.string.condition_battery_title))
- .setTitleText(mAppContext.getText(R.string.condition_battery_title).toString())
- .setSummaryText(mAppContext.getText(R.string.condition_battery_summary).toString())
- .setIconDrawable(mAppContext.getDrawable(R.drawable.ic_battery_saver_accent_24dp))
- .setIsHalfWidth(true)
- .build();
- }
-
- @Override
- public void startMonitoringStateChange() {
- mReceiver.setListening(true);
- }
-
- @Override
- public void stopMonitoringStateChange() {
- mReceiver.setListening(false);
- }
-
- @Override
- public void onPowerSaveModeChanged() {
- mConditionManager.onConditionChanged();
- }
-
- @Override
- public void onBatteryChanged(boolean pluggedIn) {
-
- }
-}
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
index c741b98..39f4903 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
@@ -154,7 +154,6 @@
mCardControllers.add(new AirplaneModeConditionController(mAppContext, this /* manager */));
mCardControllers.add(
new BackgroundDataConditionController(mAppContext, this /* manager */));
- mCardControllers.add(new BatterySaverConditionController(mAppContext, this /* manager */));
mCardControllers.add(new CellularDataConditionController(mAppContext, this /* manager */));
mCardControllers.add(new DndConditionCardController(mAppContext, this /* manager */));
mCardControllers.add(new HotspotConditionController(mAppContext, this /* manager */));
diff --git a/src/com/android/settings/network/MultiNetworkHeaderController.java b/src/com/android/settings/network/MultiNetworkHeaderController.java
index 1c0fc74..881aaa2 100644
--- a/src/com/android/settings/network/MultiNetworkHeaderController.java
+++ b/src/com/android/settings/network/MultiNetworkHeaderController.java
@@ -19,18 +19,55 @@
import android.content.Context;
import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
// This controls a header at the top of the Network & internet page that only appears when there
// are two or more active mobile subscriptions. It shows an overview of available network
// connections with an entry for wifi (if connected) and an entry for each subscription.
-public class MultiNetworkHeaderController extends BasePreferenceController {
+public class MultiNetworkHeaderController extends BasePreferenceController implements
+ SubscriptionsPreferenceController.UpdateListener {
+ public static final String TAG = "MultiNetworkHdrCtrl";
+
+ private SubscriptionsPreferenceController mSubscriptionsController;
+ private PreferenceCategory mPreferenceCategory;
public MultiNetworkHeaderController(Context context, String key) {
- super(context, key);
+ super(context, key);
+ }
+
+ public void init(Lifecycle lifecycle) {
+ mSubscriptionsController = createSubscriptionsController(lifecycle);
+ // TODO(asargent) - add in a controller for showing wifi status here
+ }
+
+ @VisibleForTesting
+ SubscriptionsPreferenceController createSubscriptionsController(Lifecycle lifecycle) {
+ return new SubscriptionsPreferenceController(mContext, lifecycle, this, mPreferenceKey, 10);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreferenceCategory = (PreferenceCategory) screen.findPreference(mPreferenceKey);
+ mPreferenceCategory.setVisible(isAvailable());
+ mSubscriptionsController.displayPreference(screen);
}
@Override
public int getAvailabilityStatus() {
- return UNSUPPORTED_ON_DEVICE;
+ if (mSubscriptionsController == null || !mSubscriptionsController.isAvailable()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ } else {
+ return AVAILABLE;
+ }
+ }
+
+ @Override
+ public void onChildrenUpdated() {
+ mPreferenceCategory.setVisible(isAvailable());
}
}
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 70481f1..7d94bba 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -72,6 +72,9 @@
public void onAttach(Context context) {
super.onAttach(context);
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlags.NETWORK_INTERNET_V2)) {
+ use(MultiNetworkHeaderController.class).init(getSettingsLifecycle());
+ }
use(AirplaneModePreferenceController.class).setFragment(this);
}
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index cfe27db..6b14759 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -24,8 +24,20 @@
import java.util.Iterator;
import java.util.List;
+import androidx.annotation.VisibleForTesting;
+
public class SubscriptionUtil {
+ private static List<SubscriptionInfo> sResultsForTesting;
+
+ @VisibleForTesting
+ static void setAvailableSubscriptionsForTesting(List<SubscriptionInfo> results) {
+ sResultsForTesting = results;
+ }
+
public static List<SubscriptionInfo> getAvailableSubscriptions(SubscriptionManager manager) {
+ if (sResultsForTesting != null) {
+ return sResultsForTesting;
+ }
List<SubscriptionInfo> subscriptions = manager.getAvailableSubscriptionInfoList();
if (subscriptions == null) {
subscriptions = new ArrayList<>();
diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java
new file mode 100644
index 0000000..9e55341
--- /dev/null
+++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+
+import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.Map;
+
+import androidx.collection.ArrayMap;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+/**
+ * This manages a set of Preferences it places into a PreferenceGroup owned by some parent
+ * controller class - one for each available subscription. This controller is only considered
+ * available if there are 2 or more subscriptions.
+ */
+public class SubscriptionsPreferenceController extends AbstractPreferenceController implements
+ LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
+ private static final String TAG = "SubscriptionsPrefCntrlr";
+
+ private UpdateListener mUpdateListener;
+ private String mPreferenceGroupKey;
+ private PreferenceGroup mPreferenceGroup;
+ private SubscriptionManager mManager;
+ private SubscriptionsChangeListener mSubscriptionsListener;
+
+ // Map of subscription id to Preference
+ private Map<Integer, Preference> mSubscriptionPreferences;
+ private int mStartOrder;
+
+ /**
+ * This interface lets a parent of this class know that some change happened - this could
+ * either be because overall availability changed, or because we've added/removed/updated some
+ * preferences.
+ */
+ public interface UpdateListener {
+ void onChildrenUpdated();
+ }
+
+ /**
+ * @param context the context for the UI where we're placing these preferences
+ * @param lifecycle for listening to lifecycle events for the UI
+ * @param updateListener called to let our parent controller know that our availability has
+ * changed, or that one or more of the preferences we've placed in the
+ * PreferenceGroup has changed
+ * @param preferenceGroupKey the key used to lookup the PreferenceGroup where Preferences will
+ * be placed
+ * @param startOrder the order that should be given to the first Preference placed into
+ * the PreferenceGroup; the second will use startOrder+1, third will
+ * use startOrder+2, etc. - this is useful for when the parent wants
+ * to have other preferences in the same PreferenceGroup and wants
+ * a specific ordering relative to this controller's prefs.
+ */
+ public SubscriptionsPreferenceController(Context context, Lifecycle lifecycle,
+ UpdateListener updateListener, String preferenceGroupKey, int startOrder) {
+ super(context);
+ mUpdateListener = updateListener;
+ mPreferenceGroupKey = preferenceGroupKey;
+ mStartOrder = startOrder;
+ mManager = context.getSystemService(SubscriptionManager.class);
+ mSubscriptionPreferences = new ArrayMap<>();
+ mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
+ lifecycle.addObserver(this);
+ }
+
+ @OnLifecycleEvent(ON_RESUME)
+ public void onResume() {
+ mSubscriptionsListener.start();
+ update();
+ }
+
+ @OnLifecycleEvent(ON_PAUSE)
+ public void onPause() {
+ mSubscriptionsListener.stop();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreferenceGroup = (PreferenceGroup) screen.findPreference(mPreferenceGroupKey);
+ update();
+ }
+
+ private void update() {
+ if (mPreferenceGroup == null) {
+ return;
+ }
+
+ if (mSubscriptionsListener.isAirplaneModeOn()) {
+ for (Preference pref : mSubscriptionPreferences.values()) {
+ mPreferenceGroup.removePreference(pref);
+ }
+ mSubscriptionPreferences.clear();
+ mUpdateListener.onChildrenUpdated();
+ return;
+ }
+
+ final Map<Integer, Preference> existingPrefs = mSubscriptionPreferences;
+ mSubscriptionPreferences = new ArrayMap<>();
+
+ int order = mStartOrder;
+ for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mManager) ) {
+ final int subId = info.getSubscriptionId();
+ Preference pref = existingPrefs.remove(subId);
+ if (pref == null) {
+ pref = new Preference(mPreferenceGroup.getContext());
+ mPreferenceGroup.addPreference(pref);
+ }
+ pref.setTitle(info.getDisplayName());
+ pref.setIcon(R.drawable.ic_network_cell);
+ pref.setOrder(order++);
+
+ // TODO(asargent) - set summary here to indicate default for calls/sms and data
+
+ pref.setOnPreferenceClickListener(clickedPref -> {
+ // TODO(asargent) - make this start MobileNetworkActivity once we've
+ // added support for it to take a subscription id
+ return true;
+ });
+
+ mSubscriptionPreferences.put(subId, pref);
+ }
+
+ // Remove any old preferences that no longer map to a subscription.
+ for (Preference pref : existingPrefs.values()) {
+ mPreferenceGroup.removePreference(pref);
+ }
+ mUpdateListener.onChildrenUpdated();
+ }
+
+ /**
+ *
+ * @return true if there are at least 2 available subscriptions.
+ */
+ @Override
+ public boolean isAvailable() {
+ if (mSubscriptionsListener.isAirplaneModeOn()) {
+ return false;
+ }
+ return SubscriptionUtil.getAvailableSubscriptions(mManager).size() >= 2;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return null;
+ }
+
+ @Override
+ public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
+ update();
+ }
+
+ @Override
+ public void onSubscriptionsChanged() {
+ update();
+ }
+}
diff --git a/src/com/android/settings/wifi/AddNetworkFragment.java b/src/com/android/settings/wifi/AddNetworkFragment.java
index 72d878b..cd8f76e 100644
--- a/src/com/android/settings/wifi/AddNetworkFragment.java
+++ b/src/com/android/settings/wifi/AddNetworkFragment.java
@@ -73,7 +73,7 @@
scannerButton.setOnClickListener((View v) -> {
// Launch QR code scanner to join a network.
getContext().startActivity(
- WifiDppUtils.getConfiguratorQRCodeScannerIntent(/* ssid */ null));
+ WifiDppUtils.getEnrolleeQrCodeScannerIntent(/* ssid */ null));
});
}
}
diff --git a/src/com/android/settings/wifi/WifiDialog.java b/src/com/android/settings/wifi/WifiDialog.java
index 0e2ca60..0bee671 100644
--- a/src/com/android/settings/wifi/WifiDialog.java
+++ b/src/com/android/settings/wifi/WifiDialog.java
@@ -86,9 +86,13 @@
if (scannerButton != null) {
scannerButton.setVisibility(View.VISIBLE);
scannerButton.setOnClickListener((View v) -> {
+ String ssid = null;
+ if (mAccessPoint != null) {
+ ssid = mAccessPoint.getSsidStr();
+ }
// Launch QR code scanner to join a network.
getContext().startActivity(
- WifiDppUtils.getConfiguratorQRCodeScannerIntent(/* ssid */ null));
+ WifiDppUtils.getEnrolleeQrCodeScannerIntent(ssid));
});
}
}
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 54de28d..95e912d 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -244,7 +244,7 @@
mAddPreference.setButtonOnClickListener((View v) -> {
// Launch QR code scanner to join a network.
getContext().startActivity(
- WifiDppUtils.getConfiguratorQRCodeScannerIntent(/* ssid */ null));
+ WifiDppUtils.getEnrolleeQrCodeScannerIntent(/* ssid */ null));
});
}
mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE);
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index e1179f8..f3e8fc1 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -549,11 +549,8 @@
* Show QR code to share the network represented by this preference.
*/
public void launchQRCodeGenerator() {
- final Intent intent = new Intent(
- WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
- intent.putExtra(WifiDppUtils.EXTRA_WIFI_SECURITY,
+ Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntent(mAccessPoint.getSsidStr(),
mAccessPoint.getSecurityString(/* concise */ false));
- intent.putExtra(WifiDppUtils.EXTRA_WIFI_SSID, mAccessPoint.getSsidStr());
mContext.startActivity(intent);
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
index 177e79d..8d6aa68 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
@@ -18,6 +18,7 @@
import android.os.Bundle;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
/**
@@ -31,6 +32,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+ }
+
+ @Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
index 7d031c1..66bc349 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
@@ -18,6 +18,7 @@
import android.os.Bundle;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
/**
@@ -31,6 +32,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+ }
+
+ @Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
index e4ae292..6c95f09 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
@@ -27,7 +27,6 @@
import androidx.fragment.app.FragmentTransaction;
import com.android.internal.logging.nano.MetricsProto;
-
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.R;
@@ -67,8 +66,7 @@
@Override
public int getMetricsCategory() {
- //TODO:Should we use a new metrics category for Wi-Fi DPP?
- return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
+ return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
}
@Override
diff --git a/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java b/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java
index 7c58fd5..920e736 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivity.java
@@ -27,7 +27,6 @@
import androidx.fragment.app.FragmentTransaction;
import com.android.internal.logging.nano.MetricsProto;
-
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.R;
@@ -35,7 +34,7 @@
* To provision "this" device with specified Wi-Fi network.
*
* To use intent action {@code ACTION_ENROLLEE_QR_CODE_SCANNER}, specify the SSID string of the
- * Wi-Fi network to be provisioned in {@code WifiDppUtils.EXTRA_QR_CODE}.
+ * Wi-Fi network to be provisioned in {@code WifiDppUtils.EXTRA_WIFI_SSID}.
*/
public class WifiDppEnrolleeActivity extends InstrumentedActivity {
private static final String TAG = "WifiDppEnrolleeActivity";
@@ -47,8 +46,7 @@
@Override
public int getMetricsCategory() {
- //TODO:Should we use a new metrics category for Wi-Fi DPP?
- return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
+ return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_ENROLLEE;
}
@Override
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
index cddd55c..5b76f11 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
@@ -18,7 +18,6 @@
import android.os.Bundle;
import android.view.LayoutInflater;
-import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -27,14 +26,11 @@
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.android.internal.logging.nano.MetricsProto;
-
import com.android.settings.core.InstrumentedFragment;
-import com.android.settings.wifi.qrcode.QrDecorateView;
import com.android.settings.R;
/**
- * TODO: Should refine code to only initiate UI component in each child fragment.
+ * TODO: b/120645817 should refine code to only initiate UI component in each child fragment.
*/
/**
@@ -50,14 +46,8 @@
private TextView mTitle;
private TextView mDescription;
- private SurfaceView mPreviewView; //optional, for WifiDppQrCodeScannerFragment
- private QrDecorateView mDecorateViiew; //optional, for WifiDppQrCodeScannerFragment
private TextView mErrorMessage; //optional, for WifiDppQrCodeScannerFragment
-
- private ImageView mBarcodeView; //optional, for WifiDppQrCodeGeneratorFragment
-
private ListView mSavedWifiNetworkList; //optional, for WifiDppChooseSavedWifiNetworkFragment
-
private ProgressBar mProgressBar; //optional, for WifiDppAddDeviceFragment
private ImageView mWifiApPictureView; //optional, for WifiDppAddDeviceFragment
private TextView mChooseDifferentNetwork;//optional, for WifiDppAddDeviceFragment
@@ -70,12 +60,6 @@
abstract protected int getLayout();
@Override
- public int getMetricsCategory() {
- //TODO:Should we use a new metrics category for Wi-Fi DPP?
- return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
- }
-
- @Override
public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@@ -91,13 +75,8 @@
private void initView(View view) {
mTitle = view.findViewById(R.id.title);
mDescription = view.findViewById(R.id.description);
-
- mPreviewView = view.findViewById(R.id.preview_view);
- mDecorateViiew = view.findViewById(R.id.decorate_view);
mErrorMessage = view.findViewById(R.id.error_message);
- mBarcodeView = view.findViewById(R.id.barcode_view);
-
mSavedWifiNetworkList = view.findViewById(R.id.saved_wifi_network_list);
mProgressBar = view.findViewById(R.id.progress_bar);
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
index b064253..ec22415 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
@@ -23,6 +23,7 @@
import android.view.MenuInflater;
import android.view.MenuItem;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
/**
@@ -35,6 +36,11 @@
return R.layout.wifi_dpp_qrcode_generator_fragment;
}
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+ }
+
// Container Activity must implement this interface
public interface OnQrCodeGeneratorFragmentAddButtonClickedListener {
public void onQrCodeGeneratorFragmentAddButtonClicked();
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index 5689c56..64ede72 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -21,24 +21,28 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.Size;
import android.view.Menu;
import android.view.MenuInflater;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.wifi.qrcode.QrCamera;
import com.android.settings.wifi.qrcode.QrDecorateView;
public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment implements
- SurfaceHolder.Callback,
+ SurfaceTextureListener,
QrCamera.ScannerCallback {
private QrCamera mCamera;
- private SurfaceView mSurfaceView;
+ private TextureView mTextureView;
private QrDecorateView mDecorateView;
/** true if the fragment working for configurator, false enrollee*/
@@ -52,6 +56,15 @@
return R.layout.wifi_dpp_qrcode_scanner_fragment;
}
+ @Override
+ public int getMetricsCategory() {
+ if (mConfiguratorMode) {
+ return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
+ } else {
+ return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_ENROLLEE;
+ }
+ }
+
/**
* Configurator container activity of the fragment should create instance with this constructor.
*/
@@ -87,7 +100,14 @@
setDescription(getString(R.string.wifi_dpp_center_qr_code, wifiNetworkConfig.getSsid()));
} else {
setTitle(getString(R.string.wifi_dpp_scan_qr_code));
- setDescription(getString(R.string.wifi_dpp_scan_qr_code_join_network, mSsid));
+
+ String description;
+ if (TextUtils.isEmpty(mSsid)) {
+ description = getString(R.string.wifi_dpp_scan_qr_code_join_unknown_network, mSsid);
+ } else {
+ description = getString(R.string.wifi_dpp_scan_qr_code_join_network, mSsid);
+ }
+ setDescription(description);
}
ActionBar actionBar = getActivity().getActionBar();
@@ -101,23 +121,13 @@
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- mSurfaceView = (SurfaceView) view.findViewById(R.id.preview_view);
- final SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
- surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- surfaceHolder.addCallback(this);
+ mTextureView = (TextureView) view.findViewById(R.id.preview_view);
+ mTextureView.setSurfaceTextureListener(this);
mDecorateView = (QrDecorateView) view.findViewById(R.id.decorate_view);
}
@Override
- public void onDestroyView() {
- SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
- surfaceHolder.removeCallback(this);
-
- super.onDestroyView();
- }
-
- @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.removeItem(Menu.FIRST);
@@ -125,23 +135,29 @@
}
@Override
- public void surfaceCreated(final SurfaceHolder holder) {
- initCamera(holder);
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ initCamera(surface);
}
@Override
- public void surfaceDestroyed(SurfaceHolder holder) {
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
destroyCamera();
+ return true;
}
@Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Do nothing
}
@Override
public Size getViewSize() {
- return new Size(mSurfaceView.getWidth(), mSurfaceView.getHeight());
+ return new Size(mTextureView.getWidth(), mTextureView.getHeight());
}
@Override
@@ -150,6 +166,11 @@
}
@Override
+ public void setTransform(Matrix transform) {
+ mTextureView.setTransform(transform);
+ }
+
+ @Override
public void handleSuccessfulResult(String qrCode) {
destroyCamera();
mDecorateView.setFocused(true);
@@ -161,11 +182,11 @@
destroyCamera();
}
- private void initCamera(SurfaceHolder holder) {
+ private void initCamera(SurfaceTexture surface) {
// Check if the camera has already created.
if (mCamera == null) {
mCamera = new QrCamera(getContext(), this);
- mCamera.start(holder);
+ mCamera.start(surface);
}
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index dc0ff84..3275695 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -88,14 +88,14 @@
}
/**
- * Returns an intent to launch QR code scanner.
+ * Returns an intent to launch QR code scanner for Wi-Fi DPP enrollee.
*
* @param ssid The data corresponding to {@code WifiConfiguration} SSID
* @return Intent for launching QR code scanner
*/
- public static Intent getConfiguratorQRCodeScannerIntent(String ssid) {
+ public static Intent getEnrolleeQrCodeScannerIntent(String ssid) {
final Intent intent = new Intent(
- WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_SCANNER);
+ WifiDppEnrolleeActivity.ACTION_ENROLLEE_QR_CODE_SCANNER);
if (!TextUtils.isEmpty(ssid)) {
intent.putExtra(EXTRA_WIFI_SSID, ssid);
}
@@ -109,7 +109,8 @@
* @param Security The data is from {@code AccessPoint.securityToString}
* @return Intent for launching QR code generator
*/
- public static Intent getConfiguratorQRCodeGeneratorIntent(String ssid, String Security) {
+ public static Intent getConfiguratorQrCodeGeneratorIntent(String ssid, String Security) {
+ //TODO: b/118794858#comment6 should put password & hideSsid in intent extra
final Intent intent = new Intent(
WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
if (!TextUtils.isEmpty(ssid)) {
diff --git a/src/com/android/settings/wifi/qrcode/QrCamera.java b/src/com/android/settings/wifi/qrcode/QrCamera.java
index dc650b9..c60c30e 100644
--- a/src/com/android/settings/wifi/qrcode/QrCamera.java
+++ b/src/com/android/settings/wifi/qrcode/QrCamera.java
@@ -17,7 +17,10 @@
package com.android.settings.wifi.qrcode;
import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
@@ -29,7 +32,6 @@
import android.util.Log;
import android.util.Size;
import android.view.Surface;
-import android.view.SurfaceHolder;
import android.view.WindowManager;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
@@ -50,7 +52,7 @@
/**
* Manage the camera for the QR scanner and help the decoder to get the image inside the scanning
- * frame. Caller prepares a {@link SurfaceHolder} then call {@link #start(SurfaceHolder)} to
+ * frame. Caller prepares a {@link SurfaceTexture} then call {@link #start(SurfaceTexture)} to
* start QR Code scanning. The scanning result will return by ScannerCallback interface. Caller
* can also call {@link #stop()} to halt QR Code scanning before the result returned.
*/
@@ -90,12 +92,11 @@
* The function start camera preview and capture pictures to decode QR code continuously in a
* background task.
*
- * @param surfaceHolder the Surface to be used for live preview, must already contain a surface
- * when this method is called.
+ * @param surface The surface to be used for live preview.
*/
- public void start(SurfaceHolder surfaceHolder) {
+ public void start(SurfaceTexture surface) {
if (mDecodeTask == null) {
- mDecodeTask = new DecodingTask(surfaceHolder);
+ mDecodeTask = new DecodingTask(surface);
// Execute in the separate thread pool to prevent block other AsyncTask.
mDecodeTask.executeOnExecutor(Executors.newSingleThreadExecutor());
}
@@ -144,6 +145,13 @@
* @return The rectangle would like to crop from the camera preview shot.
*/
Rect getFramePosition(Size previewSize, int cameraOrientation);
+
+ /**
+ * Sets the transform to associate with preview area.
+ *
+ * @param transform The transform to apply to the content of preview
+ */
+ void setTransform(Matrix transform);
}
private void setCameraParameter() {
@@ -200,15 +208,15 @@
private class DecodingTask extends AsyncTask<Void, Void, String> {
private QrYuvLuminanceSource mImage;
- private SurfaceHolder mSurfaceHolder;
+ private SurfaceTexture mSurface;
- private DecodingTask(SurfaceHolder surfaceHolder) {
- mSurfaceHolder = surfaceHolder;
+ private DecodingTask(SurfaceTexture surface) {
+ mSurface = surface;
}
@Override
protected String doInBackground(Void... tmp) {
- if (!initCamera(mSurfaceHolder)) {
+ if (!initCamera(mSurface)) {
return null;
}
@@ -253,7 +261,7 @@
}
}
- private boolean initCamera(SurfaceHolder surfaceHolder) {
+ private boolean initCamera(SurfaceTexture surface) {
final int numberOfCameras = Camera.getNumberOfCameras();
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
try {
@@ -261,7 +269,7 @@
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
mCamera = Camera.open(i);
- mCamera.setPreviewDisplay(surfaceHolder);
+ mCamera.setPreviewTexture(surface);
mCameraOrientation = cameraInfo.orientation;
break;
}
@@ -272,6 +280,7 @@
return false;
}
setCameraParameter();
+ setTransformationMatrix(mScannerCallback.getViewSize());
if (!startPreview()) {
Log.e(TAG, "Error to init Camera");
mCamera = null;
@@ -288,6 +297,36 @@
}
}
+ /** Set transfom matrix to crop and center the preview picture */
+ private void setTransformationMatrix(Size viewSize) {
+ // Check aspect ratio, can only handle square view.
+ final int viewRatio = (int)getRatio(viewSize.getWidth(), viewSize.getHeight());
+ if (viewRatio != 1) {
+ throw new IllegalArgumentException("Preview area should be square");
+ }
+
+ final boolean isPortrait = mContext.get().getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT ? true : false;
+
+ final int previewWidth = isPortrait ? mPreviewSize.getWidth() : mPreviewSize.getHeight();
+ final int previewHeight = isPortrait ? mPreviewSize.getHeight() : mPreviewSize.getWidth();
+ final float ratioPreview = (float) getRatio(previewWidth, previewHeight);
+
+ // Calculate transformation matrix.
+ float scaleX = 1.0f;
+ float scaleY = 1.0f;
+ if (previewWidth > previewHeight) {
+ scaleY = scaleX / ratioPreview;
+ } else {
+ scaleX = scaleY / ratioPreview;
+ }
+
+ // Set the transform matrix.
+ final Matrix matrix = new Matrix();
+ matrix.setScale(scaleX, scaleY);
+ mScannerCallback.setTransform(matrix);
+ }
+
private QrYuvLuminanceSource getFrameImage(byte[] imageData) {
final Rect frame = mScannerCallback.getFramePosition(mPreviewSize, mCameraOrientation);
final Camera.Size size = mParameters.getPictureSize();
diff --git a/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java b/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
new file mode 100644
index 0000000..56566ae
--- /dev/null
+++ b/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settings.wifi.qrcode;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.FrameLayout;
+
+/**
+ * A customize square {@link FrameLayout}.
+ * This is used for camera preview. Choose the smaller size of both dimensions as length and width.
+ */
+public class QrPreviewLayout extends FrameLayout {
+ public QrPreviewLayout(Context context) {
+ super(context);
+ }
+
+ public QrPreviewLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public QrPreviewLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Choose the smaller size of the two dimensions.
+ if (MeasureSpec.getSize(widthMeasureSpec) > MeasureSpec.getSize(heightMeasureSpec)) {
+ super.onMeasure(heightMeasureSpec, heightMeasureSpec);
+ } else {
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+ }
+}
diff --git a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
index befd880..ce03d95 100644
--- a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
+++ b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
@@ -7,7 +7,6 @@
com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController
com.android.settings.datausage.DataUsageSummaryPreferenceController
com.android.settings.fuelgauge.RestrictAppPreferenceController
-com.android.settings.fuelgauge.batterysaver.AutoBatterySeekBarPreferenceController
com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController
com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController
com.android.settings.security.VisiblePatternProfilePreferenceController
diff --git a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java
index ce438b0..28e394f 100644
--- a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java
+++ b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java
@@ -71,7 +71,9 @@
.that(badClasses.toString())
.isEmpty();
- assertWithMessage("Something in the grandfather list is no longer relevant. Please remove")
+ assertWithMessage("Something in the grandfather list is no longer relevant. Please remove"
+ + "it from packages/apps/Settings/tests/robotests/assets/"
+ + "grandfather_invalid_base_preference_controller_constructor")
.that(grandfather)
.isEmpty();
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceControllerTest.java
deleted file mode 100644
index df1d909..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySaverPreferenceControllerTest.java
+++ /dev/null
@@ -1,89 +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.fuelgauge.batterysaver;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.preference.SwitchPreference;
-
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class AutoBatterySaverPreferenceControllerTest {
-
- private AutoBatterySaverPreferenceController mController;
- private Context mContext;
- private SwitchPreference mPreference;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mContext = RuntimeEnvironment.application;
- mPreference = new SwitchPreference(mContext);
- mController = new AutoBatterySaverPreferenceController(mContext);
- }
-
- @Test
- public void testUpdateState_lowPowerLevelZero_preferenceNotChecked() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
- mController.updateState(mPreference);
-
- assertThat(mPreference.isChecked()).isFalse();
- }
-
- @Test
- public void testUpdateState_lowPowerLevelZero_preferenceChecked() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15);
- mController.updateState(mPreference);
-
- assertThat(mPreference.isChecked()).isTrue();
- }
-
- @Test
- public void testOnPreferenceChange_turnOn_setValueNotZero() {
- mController.onPreferenceChange(mPreference, true);
-
- assertThat(Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isNotEqualTo(0);
- }
-
- @Test
- public void testOnPreferenceChange_turnOff_setValueZero() {
- mController.onPreferenceChange(mPreference, false);
-
- assertThat(Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isEqualTo(0);
- }
-
- @Test
- public void testIsChecked_useDefaultValue_returnFalse() {
- assertThat(mController.isChecked()).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java
deleted file mode 100644
index c13b913..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batterysaver;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.lifecycle.LifecycleOwner;
-
-import com.android.settings.widget.SeekBarPreference;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class AutoBatterySeekBarPreferenceControllerTest {
-
- private static final int TRIGGER_LEVEL = 20;
- private static final int INTERVAL = 5;
-
- private AutoBatterySeekBarPreferenceController mController;
- private Context mContext;
- private SeekBarPreference mPreference;
- private Lifecycle mLifecycle;
- private LifecycleOwner mLifecycleOwner;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mLifecycleOwner = () -> mLifecycle;
- mLifecycle = new Lifecycle(mLifecycleOwner);
-
- mContext = RuntimeEnvironment.application;
- mPreference = new SeekBarPreference(mContext);
- mPreference.setMax(100);
- mController = new AutoBatterySeekBarPreferenceController(mContext, mLifecycle);
- }
-
- @Test
- public void testPreference_lowPowerLevelZero_preferenceInvisible() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isFalse();
- }
-
- @Test
- public void testPreference_defaultValue_preferenceNotVisible() {
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isFalse();
- }
-
- @Test
- public void testPreference_lowPowerLevelNotZero_updatePreference() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL);
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getTitle()).isEqualTo("20%");
- assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL / INTERVAL);
- }
-
- @Test
- public void testOnPreferenceChange_updateValue() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
-
- mController.onPreferenceChange(mPreference, TRIGGER_LEVEL / INTERVAL);
-
- assertThat(Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)).isEqualTo(TRIGGER_LEVEL);
- }
-
- @Test
- public void testOnPreferenceChange_changeMax() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 50);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getMax()).isEqualTo(50 / INTERVAL);
- }
-
- @Test
- public void testOnPreferenceChange_noChangeMax() {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 0);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getMax()).isEqualTo(100);
- }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java
new file mode 100644
index 0000000..6d412ee
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java
@@ -0,0 +1,57 @@
+package com.android.settings.fuelgauge.batterysaver;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatterySaverScheduleRadioButtonsControllerTest {
+ private Context mContext;
+ private ContentResolver mResolver;
+ private BatterySaverScheduleRadioButtonsController mController;
+ private BatterySaverScheduleSeekBarController mSeekBarController;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mSeekBarController = new BatterySaverScheduleSeekBarController(mContext);
+ mController = new BatterySaverScheduleRadioButtonsController(
+ mContext, mSeekBarController);
+ mResolver = mContext.getContentResolver();
+ }
+
+ @Test
+ public void getDefaultKey_routine_returnsCorrectValue() {
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_DYNAMIC);
+ assertThat(mController.getDefaultKey())
+ .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_ROUTINE);
+ }
+
+ @Test
+ public void getDefaultKey_automatic_returnsCorrectValue() {
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+ assertThat(mController.getDefaultKey())
+ .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_PERCENTAGE);
+ }
+
+ @Test
+ public void getDefaultKey_none_returnsCorrectValue() {
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ assertThat(mController.getDefaultKey())
+ .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_NO_SCHEDULE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java
new file mode 100644
index 0000000..8aac9d8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java
@@ -0,0 +1,65 @@
+package com.android.settings.fuelgauge.batterysaver;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatterySaverScheduleSeekBarControllerTest {
+
+ private Context mContext;
+ private ContentResolver mResolver;
+ private BatterySaverScheduleSeekBarController mController;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mController = new BatterySaverScheduleSeekBarController(mContext);
+ mResolver = mContext.getContentResolver();
+ }
+
+ @Test
+ public void onPreferenceChange_updatesSettingsGlobal() {
+ Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+ mController.onPreferenceChange(mController.mSeekBarPreference, 10);
+ assertThat(Settings.Global.getInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+ .isEqualTo(50);
+
+ assertThat(mController.mSeekBarPreference.getTitle()).isEqualTo("50%");
+ }
+
+ @Test
+ public void updateSeekBar_routineMode_hasCorrectProperties() {
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_DYNAMIC);
+ mController.updateSeekBar();
+ assertThat(mController.mSeekBarPreference.isVisible()).isFalse();
+ }
+
+ @Test
+ public void updateSeekBar_percentageMode_hasCorrectProperties() {
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
+ mController.updateSeekBar();
+ assertThat(mController.mSeekBarPreference.isVisible()).isTrue();
+ }
+
+ @Test
+ public void updateSeekBar_noneMode_hasCorrectProperties() {
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ mController.updateSeekBar();
+ assertThat(mController.mSeekBarPreference.isVisible()).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java
deleted file mode 100644
index e4ca6c0..0000000
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.conditional;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.IntentFilter;
-import android.os.PowerManager;
-
-import com.android.settings.fuelgauge.BatterySaverReceiver;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowPowerManager;
-
-@RunWith(RobolectricTestRunner.class)
-public class BatterySaverConditionControllerTest {
- @Mock
- private ConditionManager mConditionManager;
-
- private ShadowPowerManager mPowerManager;
- private Context mContext;
- private BatterySaverConditionController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application);
- mPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class));
- mController = new BatterySaverConditionController(mContext, mConditionManager);
- }
-
- @Test
- public void startMonitor_shouldRegisterReceiver() {
- mController.startMonitoringStateChange();
-
- verify(mContext).registerReceiver(any(BatterySaverReceiver.class), any(IntentFilter.class));
- }
-
- @Test
- public void stopMonitor_shouldUnregisterReceiver() {
- mController.startMonitoringStateChange();
- mController.stopMonitoringStateChange();
-
- verify(mContext).unregisterReceiver(any(BatterySaverReceiver.class));
- }
-
- @Test
- public void isDisplayable_PowerSaverOn_true() {
- mPowerManager.setIsPowerSaveMode(true);
-
- assertThat(mController.isDisplayable()).isTrue();
- }
-
- @Test
- public void isDisplayable_PowerSaverOff_false() {
- mPowerManager.setIsPowerSaveMode(false);
-
- assertThat(mController.isDisplayable()).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java b/tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
new file mode 100644
index 0000000..fbd7867
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class MultiNetworkHeaderControllerTest {
+ private static final String KEY_HEADER = "multi_network_header";
+
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+ @Mock
+ private SubscriptionsPreferenceController mSubscriptionsController;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+
+ private Context mContext;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private MultiNetworkHeaderController mHeaderController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+ when(mPreferenceScreen.findPreference(eq(KEY_HEADER))).thenReturn(mPreferenceCategory);
+
+ mHeaderController = spy(new MultiNetworkHeaderController(mContext, KEY_HEADER));
+ doReturn(mSubscriptionsController).when(mHeaderController).createSubscriptionsController(
+ mLifecycle);
+ }
+
+ @Test
+ public void isAvailable_beforeInitIsCalled_notAvailable() {
+ assertThat(mHeaderController.isAvailable()).isFalse();
+ }
+
+ // When calling displayPreference, the header itself should only be visible if the
+ // subscriptions controller says it is available. This is a helper for test cases of this logic.
+ private void displayPreferenceTest(boolean subscriptionsAvailable,
+ boolean setVisibleExpectedValue) {
+ when(mSubscriptionsController.isAvailable()).thenReturn(subscriptionsAvailable);
+
+ mHeaderController.init(mLifecycle);
+ mHeaderController.displayPreference(mPreferenceScreen);
+ verify(mPreferenceCategory, never()).setVisible(eq(!setVisibleExpectedValue));
+ verify(mPreferenceCategory, atLeastOnce()).setVisible(eq(setVisibleExpectedValue));
+ }
+
+ @Test
+ public void displayPreference_subscriptionsNotAvailable_categoryIsNotVisible() {
+ displayPreferenceTest(false, false);
+ }
+
+ @Test
+ public void displayPreference_subscriptionsAvailable_categoryIsVisible() {
+ displayPreferenceTest(true, true);
+ }
+
+ @Test
+ public void onChildUpdated_subscriptionsBecameAvailable_categoryIsVisible() {
+ when(mSubscriptionsController.isAvailable()).thenReturn(false);
+ mHeaderController.init(mLifecycle);
+ mHeaderController.displayPreference(mPreferenceScreen);
+
+ when(mSubscriptionsController.isAvailable()).thenReturn(true);
+ mHeaderController.onChildrenUpdated();
+ ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+
+ verify(mPreferenceCategory, atLeastOnce()).setVisible(captor.capture());
+ List<Boolean> values = captor.getAllValues();
+ assertThat(values.get(values.size()-1)).isEqualTo(Boolean.TRUE);
+ }
+
+ @Test
+ public void onChildUpdated_subscriptionsBecameUnavailable_categoryIsNotVisible() {
+ when(mSubscriptionsController.isAvailable()).thenReturn(true);
+ mHeaderController.init(mLifecycle);
+ mHeaderController.displayPreference(mPreferenceScreen);
+
+ when(mSubscriptionsController.isAvailable()).thenReturn(false);
+ mHeaderController.onChildrenUpdated();
+ ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+
+ verify(mPreferenceCategory, atLeastOnce()).setVisible(captor.capture());
+ List<Boolean> values = captor.getAllValues();
+ assertThat(values.get(values.size()-1)).isEqualTo(Boolean.FALSE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
new file mode 100644
index 0000000..016a885
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class SubscriptionsPreferenceControllerTest {
+ private static final String KEY = "preference_group";
+
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+
+ private Context mContext;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private SubscriptionsPreferenceController mController;
+ private int mOnChildUpdatedCount;
+ private SubscriptionsPreferenceController.UpdateListener mUpdateListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+ when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
+ when(mPreferenceCategory.getContext()).thenReturn(mContext);
+ mOnChildUpdatedCount = 0;
+ mUpdateListener = () -> mOnChildUpdatedCount++;
+
+ mController = new SubscriptionsPreferenceController(mContext, mLifecycle, mUpdateListener,
+ KEY, 5);
+ }
+
+ @Test
+ public void isAvailable_oneSubscription_availableFalse() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(
+ Arrays.asList(mock(SubscriptionInfo.class)));
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_twoSubscriptions_availableTrue() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(
+ Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_fiveSubscriptions_availableTrue() {
+ final ArrayList<SubscriptionInfo> subs = new ArrayList<>();
+ for (int i = 0; i < 5; i++) {
+ subs.add(mock(SubscriptionInfo.class));
+ }
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(subs);
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_airplaneModeOn_availableFalse() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(
+ Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
+ assertThat(mController.isAvailable()).isTrue();
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void onAirplaneModeChanged_airplaneModeTurnedOn_eventFired() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(
+ Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
+ mController.onResume();
+ mController.displayPreference(mScreen);
+ assertThat(mController.isAvailable()).isTrue();
+
+ final int updateCountBeforeModeChange = mOnChildUpdatedCount;
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+ mController.onAirplaneModeChanged(true);
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
+ }
+
+ @Test
+ public void onAirplaneModeChanged_airplaneModeTurnedOff_eventFired() {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(
+ Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
+ mController.onResume();
+ mController.displayPreference(mScreen);
+ assertThat(mController.isAvailable()).isFalse();
+
+ final int updateCountBeforeModeChange = mOnChildUpdatedCount;
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
+ mController.onAirplaneModeChanged(false);
+ assertThat(mController.isAvailable()).isTrue();
+ assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
+ }
+
+ @Test
+ public void onSubscriptionsChanged_countBecameTwo_eventFired() {
+ final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
+ final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
+ mController.onResume();
+ mController.displayPreference(mScreen);
+ assertThat(mController.isAvailable()).isFalse();
+
+ final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ mController.onSubscriptionsChanged();
+ assertThat(mController.isAvailable()).isTrue();
+ assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
+ }
+
+ @Test
+ public void onSubscriptionsChanged_countBecameOne_eventFired() {
+ final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
+ final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ mController.onResume();
+ mController.displayPreference(mScreen);
+ assertThat(mController.isAvailable()).isTrue();
+
+ final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
+ mController.onSubscriptionsChanged();
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
+ }
+
+
+ @Test
+ public void onSubscriptionsChanged_subscriptionReplaced_preferencesChanged() {
+ final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
+ final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
+ final SubscriptionInfo sub3 = mock(SubscriptionInfo.class);
+ when(sub1.getDisplayName()).thenReturn("sub1");
+ when(sub2.getDisplayName()).thenReturn("sub2");
+ when(sub3.getDisplayName()).thenReturn("sub3");
+ when(sub1.getSubscriptionId()).thenReturn(1);
+ when(sub2.getSubscriptionId()).thenReturn(2);
+ when(sub3.getSubscriptionId()).thenReturn(3);
+
+ // Start out with only sub1 and sub2.
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
+ mController.onResume();
+ mController.displayPreference(mScreen);
+ final ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+ verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
+ assertThat(captor.getAllValues().size()).isEqualTo(2);
+ assertThat(captor.getAllValues().get(0).getTitle()).isEqualTo("sub1");
+ assertThat(captor.getAllValues().get(1).getTitle()).isEqualTo("sub2");
+
+ // Now replace sub2 with sub3, and make sure the old preference was removed and the new
+ // preference was added.
+ final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub3));
+ mController.onSubscriptionsChanged();
+ assertThat(mController.isAvailable()).isTrue();
+ assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
+
+ verify(mPreferenceCategory).removePreference(captor.capture());
+ assertThat(captor.getValue().getTitle()).isEqualTo("sub2");
+ verify(mPreferenceCategory, times(3)).addPreference(captor.capture());
+ assertThat(captor.getValue().getTitle()).isEqualTo("sub3");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
index e32ac6b..0ef0273 100644
--- a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
@@ -22,9 +22,11 @@
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
import android.util.Size;
-import android.view.SurfaceHolder;
import com.android.settings.R;
@@ -48,7 +50,7 @@
public class QrCameraTest {
@Mock
- private SurfaceHolder mSurfaceHolder;
+ private SurfaceTexture mSurfaceTexture;
private QrCamera mCamera;
private Context mContext;
@@ -78,6 +80,11 @@
mCameraCallbacked = true;
mCallbackSignal.countDown();
}
+
+ @Override
+ public void setTransform(Matrix transform) {
+ // Do nothing
+ }
}
private ScannerTestCallback mScannerCallback;
@@ -87,7 +94,7 @@
mContext = RuntimeEnvironment.application;
mScannerCallback = new ScannerTestCallback();
mCamera = new QrCamera(mContext, mScannerCallback);
- mSurfaceHolder = mock(SurfaceHolder.class);
+ mSurfaceTexture = mock(SurfaceTexture.class);
mQrCode = "";
mCameraCallbacked = false;
mCallbackSignal = null;
@@ -96,7 +103,7 @@
@Test
public void testCamera_Init_Callback() throws InterruptedException {
mCallbackSignal = new CountDownLatch(1);
- mCamera.start(mSurfaceHolder);
+ mCamera.start(mSurfaceTexture);
mCallbackSignal.await(5000, TimeUnit.MILLISECONDS);
assertThat(mCameraCallbacked).isTrue();
}