DO NOT MERGE - Merge qt-qpr1-dev-plus-aosp-without-vendor (6129114) into stage-aosp-master

Bug: 146167222
Change-Id: I77344517b74a2192af49a3f8956d8756b81741d9
This commit is contained in:
Xin Li
2020-01-15 15:57:20 -08:00
68 changed files with 1783 additions and 703 deletions

View File

@@ -936,6 +936,24 @@
android:value="true" /> android:value="true" />
</activity> </activity>
<activity
android:name="Settings$DarkThemeSettingsActivity"
android:label="@string/dark_ui_auto_mode_title"
android:enabled="true">
<intent-filter android:priority="32">
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.settings.DARK_THEME_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.display.darkmode.DarkModeSettingsFragment" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
<activity <activity
android:name="Settings$NightDisplaySuggestionActivity" android:name="Settings$NightDisplaySuggestionActivity"
android:enabled="@*android:bool/config_nightDisplayAvailable" android:enabled="@*android:bool/config_nightDisplayAvailable"

View File

@@ -51,8 +51,9 @@
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/bt_battery_icon" android:id="@+id/bt_battery_icon"
android:layout_width="13dp" android:layout_width="wrap_content"
android:layout_height="20dp"/> android:layout_height="wrap_content"
android:layout_gravity="center"/>
<TextView <TextView
android:id="@+id/bt_battery_summary" android:id="@+id/bt_battery_summary"
style="@style/TextAppearance.EntityHeaderSummary" style="@style/TextAppearance.EntityHeaderSummary"

View File

@@ -44,16 +44,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:minLines="2" android:minLines="3"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"/> android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"/>
android:textColor="?android:attr/textColorPrimary"/> android:textColor="?android:attr/textColorPrimary"/>
<TextView
android:id="@+id/summary2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"/>
android:textColor="?android:attr/textColorPrimary"/>
</LinearLayout> </LinearLayout>
<com.android.settings.fuelgauge.BatteryMeterView <com.android.settings.fuelgauge.BatteryMeterView

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight">
<Button
android:id="@+id/dark_ui_turn_on_button"
style="@style/ActionPrimaryButton"
android:layout_marginStart="@dimen/screen_margin_sides"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
<Button
android:id="@+id/dark_ui_turn_off_button"
style="@style/ActionSecondaryButton"
android:layout_marginStart="@dimen/screen_margin_sides"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
</LinearLayout>

View File

@@ -124,9 +124,9 @@
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<TextView <TextView
android:id="@+id/face_enroll_introduction_footer_part_2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"/>
android:text="@string/security_settings_face_enroll_introduction_footer_part_2"/>
</LinearLayout> </LinearLayout>

View File

@@ -100,6 +100,14 @@
<item>1800000</item> <item>1800000</item>
</string-array> </string-array>
<!-- Dark theme scheduling preferences [CHAR LIMIT=NONE] -->
<string-array name="dark_ui_scheduler_preference_titles">
<!-- 1: None -->
<item>@string/dark_ui_auto_mode_never</item>
<!-- 2: Auto -->
<item>@string/dark_ui_auto_mode_auto</item>
</string-array>
<!-- Security settings. The delay after screen is turned off until device locks. <!-- Security settings. The delay after screen is turned off until device locks.
These are shown in a list dialog. --> These are shown in a list dialog. -->
<string-array name="lock_after_timeout_entries"> <string-array name="lock_after_timeout_entries">

View File

@@ -417,4 +417,7 @@
<!-- "Show work policy info" intent action. TODO(b/134391103): Replace with final SystemAPI intent when it's available. --> <!-- "Show work policy info" intent action. TODO(b/134391103): Replace with final SystemAPI intent when it's available. -->
<string name="config_work_policy_info_intent_action" translatable="false"/> <string name="config_work_policy_info_intent_action" translatable="false"/>
<!-- RTT setting intent action -->
<string name="config_rtt_setting_intent_action" translatable="false"></string>
</resources> </resources>

View File

@@ -414,4 +414,11 @@
<!-- System navigation settings illustration height --> <!-- System navigation settings illustration height -->
<dimen name="system_navigation_illustration_height">320dp</dimen> <dimen name="system_navigation_illustration_height">320dp</dimen>
<!-- Header title size of advanced bluetooth device -->
<dimen name="advanced_bluetooth_header_title_text_size">16sp</dimen>
<!-- Battery meter icon size of advanced bluetooth device -->
<dimen name="advanced_bluetooth_battery_meter_width">7.8dp</dimen>
<dimen name="advanced_bluetooth_battery_meter_height">13dp</dimen>
</resources> </resources>

View File

@@ -827,7 +827,9 @@
<!-- Button text in face settings which lets the user enroll their face [CHAR LIMIT=40] --> <!-- Button text in face settings which lets the user enroll their face [CHAR LIMIT=40] -->
<string name="security_settings_face_settings_enroll">Set up face unlock</string> <string name="security_settings_face_settings_enroll">Set up face unlock</string>
<!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] --> <!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_footer">Use face unlock to unlock your device, sign in to apps, and confirm payments.\n\nKeep in mind:\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour phone can be unlocked by someone who looks a lot like you, say, an identical sibling.</string> <string name="security_settings_face_settings_footer">Use face unlock to unlock your device, sign in to apps, and confirm payments.\n\nKeep in mind:\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face while your eyes are open.\n\nYour phone can be unlocked by someone who looks a lot like you, say, an identical sibling.</string>
<!-- Text shown in face settings explaining what your face can be used for. Used when attention checking is not supported. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_footer_attention_not_supported">Use face unlock to unlock your device, sign in to apps, and confirm payments.\n\nKeep in mind:\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour phone can be unlocked by someone who looks a lot like you, say, an identical sibling.</string>
<!-- Dialog title shown when the user removes an enrollment [CHAR LIMIT=35] --> <!-- Dialog title shown when the user removes an enrollment [CHAR LIMIT=35] -->
<string name="security_settings_face_settings_remove_dialog_title">Delete face data?</string> <string name="security_settings_face_settings_remove_dialog_title">Delete face data?</string>
<!-- Dialog contents shown when the user removes an enrollment [CHAR LIMIT=NONE] --> <!-- Dialog contents shown when the user removes an enrollment [CHAR LIMIT=NONE] -->
@@ -2757,6 +2759,40 @@
<!-- Night display slice screen, subtitle of intensity setting when night light is off. [CHAR LIMIT=30] --> <!-- Night display slice screen, subtitle of intensity setting when night light is off. [CHAR LIMIT=30] -->
<string name="night_display_not_currently_on">Night Light not currently on</string> <string name="night_display_not_currently_on">Night Light not currently on</string>
<!-- Dark ui screen-->
<!-- Display settings screen, activation button action for manual mode. [CHAR LIMIT=40] -->
<string name="dark_ui_activation_on_manual">Turn on now</string>
<!-- Display settings screen, deactivation button action for manual mode. [CHAR LIMIT=40] -->
<string name="dark_ui_activation_off_manual">Turn off now</string>
<!-- Display settings screen, activation button action for sunset-to-sunrise schedule [CHAR LIMIT=40] -->
<string name="dark_ui_activation_on_auto">Turn on until sunrise</string>
<!-- Display settings screen, deactivation button action for sunset-to-sunrise schedule [CHAR LIMIT=40] -->
<string name="dark_ui_activation_off_auto">Turn off until sunset</string>
<!-- Dark UI screen, setting option name to enable Dark UI [CHAR LIMIT=30] -->
<string name="dark_ui_title">Dark Mode</string>
<!-- Dark UI screen, setting option name to configure whether Dark UI turn on/off automatically. [CHAR LIMIT=30] -->
<string name="dark_ui_auto_mode_title">Schedule</string>
<!-- Dark UI screen, setting option value for Dark UI to *never* turn on/off automatically. [CHAR LIMIT=30] -->
<string name="dark_ui_auto_mode_never">None</string>
<!-- Dark UIscreen, setting option value for Dark UI to turn on/off automatically at sunset/sunrise. [CHAR LIMIT=32] -->
<string name="dark_ui_auto_mode_auto">Turns on from sunset to sunrise</string>
<!-- Dark UIscreen, setting option name controlling the current activation status. [CHAR LIMIT=30] -->
<string name="dark_ui_status_title">Status</string>
<!-- Display settings screen, summary format of Dark UI when off. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_off">Off / <xliff:g name="auto_mode_summary" example="Never turn on automatically">%1$s</xliff:g></string>
<!-- Display settings screen, summary of Dark UI when off and will *never* turn on automatically. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_off_auto_mode_never">Will never turn on automatically</string>
<!-- Display settings screen, summary of Dark UI when off and will turn on automatically at sunset. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_off_auto_mode_auto">Will turn on automatically at sunset</string>
<!-- Display settings screen, summary format of Dark UI when on. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_on">On / <xliff:g name="auto_mode_summary" example="Never turn off automatically">%1$s</xliff:g></string>
<!-- Display settings screen, summary of Dark UI when on and will *never* turn off automatically. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_on_auto_mode_never">Will never turn off automatically</string>
<!-- Display settings screen, summary of Dark UI when on and will turn off automatically at sunrise. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_on_auto_mode_auto">Will turn off automatically at sunrise</string>
<!-- Dark theme screen, description of Dark theme feature. [CHAR LIMIT=NONE] -->
<string name="dark_ui_text">Dark theme uses a black background to help keep battery alive longer on some screens. Dark theme schedules wait to turn on until your screen is off.</string>
<!-- Sound & display settings screen, setting option name to change screen timeout --> <!-- Sound & display settings screen, setting option name to change screen timeout -->
<string name="screen_timeout">Screen timeout</string> <string name="screen_timeout">Screen timeout</string>
<!-- Sound & display settings screen, setting option name to change screen timeout [CHAR LIMIT=30] --> <!-- Sound & display settings screen, setting option name to change screen timeout [CHAR LIMIT=30] -->
@@ -6880,6 +6916,7 @@
<string name="help_url_connected_devices" translatable="false"></string> <string name="help_url_connected_devices" translatable="false"></string>
<string name="help_url_apps_and_notifications" translatable="false"></string> <string name="help_url_apps_and_notifications" translatable="false"></string>
<string name="help_url_night_display" translatable="false"></string> <string name="help_url_night_display" translatable="false"></string>
<string name="help_url_dark_theme" translatable="false"></string>
<string name="help_url_screen_saver" translatable="false"></string> <string name="help_url_screen_saver" translatable="false"></string>
<string name="help_url_pickup_gesture" translatable="false"></string> <string name="help_url_pickup_gesture" translatable="false"></string>
<string name="help_url_storage_dashboard" translatable="false"></string> <string name="help_url_storage_dashboard" translatable="false"></string>
@@ -9975,6 +10012,12 @@
<!-- [CHAR_LIMIT=40] Positive button text in dark theme notification --> <!-- [CHAR_LIMIT=40] Positive button text in dark theme notification -->
<string name="dark_ui_settings_dialog_acknowledge">Got it</string> <string name="dark_ui_settings_dialog_acknowledge">Got it</string>
<!-- [CHAR_LIMIT=50] Title string in the dark theme slice(suggestion) -->
<string name="dark_theme_slice_title">Try Dark theme</string>
<!-- [CHAR_LIMIT=50] Subtitle string in the dark theme slice(suggestion) -->
<string name="dark_theme_slice_subtitle">Helps extend battery life</string>
<!-- [CHAR LIMIT=60] Name of dev option to enable extra quick settings tiles --> <!-- [CHAR LIMIT=60] Name of dev option to enable extra quick settings tiles -->
<string name="quick_settings_developer_tiles">Quick settings developer tiles</string> <string name="quick_settings_developer_tiles">Quick settings developer tiles</string>
@@ -11237,5 +11280,13 @@
<string name="work_policy_privacy_settings">Your work policy info</string> <string name="work_policy_privacy_settings">Your work policy info</string>
<!-- Summary for Enterprise Privacy settings, explaining what the user can expect to find under it [CHAR LIMIT=NONE]--> <!-- Summary for Enterprise Privacy settings, explaining what the user can expect to find under it [CHAR LIMIT=NONE]-->
<string name="work_policy_privacy_settings_summary">Settings managed by your IT admin</string> <string name="work_policy_privacy_settings_summary">Settings managed by your IT admin</string>
<!-- Title for RTT setting. [CHAR LIMIT=NONE] -->
<string name="rtt_settings_title"></string>
<!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] -->
<string name="rtt_settings_no_visible"></string>
<!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] -->
<string name="rtt_settings_visible_during_call"></string>
<!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] -->
<string name="rtt_settings_always_visible"></string>
</resources> </resources>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/dark_ui_mode"
android:key="dark_ui_mode_screen"
settings:keywords="@string/keywords_dark_ui_mode">
<DropDownPreference
android:key="dark_ui_auto_mode"
android:title="@string/dark_ui_auto_mode_title"
android:summary="%s"
android:entries="@array/dark_ui_scheduler_preference_titles"
android:entryValues="@array/dark_ui_scheduler_preference_titles"
settings:controller="com.android.settings.display.darkmode.DarkModeScheduleSelectorController"
settings:keywords="@string/keywords_dark_ui_mode"/>
<com.android.settingslib.widget.LayoutPreference
android:key="dark_ui_activated"
android:title="@string/dark_ui_title"
android:selectable="false"
android:layout="@layout/dark_ui_activation_button"
settings:allowDividerBelow="true"
settings:controller="com.android.settings.display.darkmode.DarkModeActivationPreferenceController"
settings:keywords="@string/keywords_dark_ui_mode"/>
<com.android.settingslib.widget.FooterPreference
android:key="dark_ui_footer"
android:title="@string/dark_ui_text"
android:selectable="false"
settings:searchable="false"
settings:allowDividerAbove="true"/>
</PreferenceScreen>

View File

@@ -31,6 +31,15 @@
<intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" /> <intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
</com.android.settingslib.RestrictedPreference> </com.android.settingslib.RestrictedPreference>
<com.android.settings.display.darkmode.DarkModePreference
android:key="dark_ui_mode"
android:title="@string/dark_ui_mode"
android:fragment="com.android.settings.display.darkmode.DarkModeSettingsFragment"
android:widgetLayout="@null"
settings:widgetLayout="@null"
settings:controller="com.android.settings.display.DarkUIPreferenceController"
settings:searchable="false"/>
<com.android.settings.display.NightDisplayPreference <com.android.settings.display.NightDisplayPreference
android:key="night_display" android:key="night_display"
android:title="@string/night_display_title" android:title="@string/night_display_title"
@@ -54,13 +63,6 @@
settings:controller="com.android.settings.display.WallpaperPreferenceController"> settings:controller="com.android.settings.display.WallpaperPreferenceController">
</com.android.settingslib.RestrictedPreference> </com.android.settingslib.RestrictedPreference>
<SwitchPreference
android:key="dark_ui_mode"
android:title="@string/dark_ui_mode"
settings:keywords="@string/keywords_dark_ui_mode"
settings:controller="com.android.settings.display.DarkUIPreferenceController"/>
<!-- Cross-listed item, if you change this, also change it in power_usage_summary.xml --> <!-- Cross-listed item, if you change this, also change it in power_usage_summary.xml -->
<com.android.settings.display.TimeoutListPreference <com.android.settings.display.TimeoutListPreference
android:key="screen_timeout" android:key="screen_timeout"

View File

@@ -22,13 +22,6 @@
android:title="@string/privacy_dashboard_title" android:title="@string/privacy_dashboard_title"
settings:initialExpandedChildrenCount="4"> settings:initialExpandedChildrenCount="4">
<!-- This preference isn't searchable, and user won't see title in this preference.
So, we just set empty text for title. -->
<com.android.settingslib.widget.BarChartPreference
android:key="permission_bar_chart"
android:title="@string/summary_placeholder"
settings:controller="com.android.settings.privacy.PermissionBarChartPreferenceController"/>
<!-- Work Policy info --> <!-- Work Policy info -->
<Preference <Preference
android:key="work_policy_info" android:key="work_policy_info"

View File

@@ -51,13 +51,24 @@
<PreferenceCategory <PreferenceCategory
android:key="security_settings_face_manage_category" android:key="security_settings_face_manage_category"
android:title="@string/security_settings_face_settings_require_category"> android:title="@string/security_settings_face_settings_require_category">
<SwitchPreference
android:key="security_settings_face_require_attention"
android:title="@string/security_settings_face_settings_require_attention"
android:summary="@string/security_settings_face_settings_require_attention_details"
app:keywords="@string/keywords_face_unlock"
app:controller="com.android.settings.biometrics.face.FaceSettingsAttentionPreferenceController"/>
<SwitchPreference <SwitchPreference
android:key="security_settings_face_require_confirmation" android:key="security_settings_face_require_confirmation"
android:title="@string/security_settings_face_settings_require_confirmation" android:title="@string/security_settings_face_settings_require_confirmation"
android:summary="@string/security_settings_face_settings_require_confirmation_details" android:summary="@string/security_settings_face_settings_require_confirmation_details"
app:keywords="@string/keywords_face_unlock" app:keywords="@string/keywords_face_unlock"
app:controller="com.android.settings.biometrics.face.FaceSettingsConfirmPreferenceController"/> app:controller="com.android.settings.biometrics.face.FaceSettingsConfirmPreferenceController"/>
</PreferenceCategory>
<PreferenceCategory
android:key="security_settings_face_button_category"
android:title="@string/security_settings_face_preference_title">
<com.android.settingslib.widget.LayoutPreference <com.android.settingslib.widget.LayoutPreference
android:key="security_settings_face_delete_faces_container" android:key="security_settings_face_delete_faces_container"
android:selectable="false" android:selectable="false"

View File

@@ -24,7 +24,6 @@ import android.provider.SearchIndexableResource;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.display.BrightnessLevelPreferenceController; import com.android.settings.display.BrightnessLevelPreferenceController;
import com.android.settings.display.CameraGesturePreferenceController; import com.android.settings.display.CameraGesturePreferenceController;
import com.android.settings.display.DarkUIPreferenceController;
import com.android.settings.display.LiftToWakePreferenceController; import com.android.settings.display.LiftToWakePreferenceController;
import com.android.settings.display.NightDisplayPreferenceController; import com.android.settings.display.NightDisplayPreferenceController;
import com.android.settings.display.NightModePreferenceController; import com.android.settings.display.NightModePreferenceController;
@@ -67,7 +66,6 @@ public class DisplaySettings extends DashboardFragment {
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
use(DarkUIPreferenceController.class).setParentFragment(this);
} }
@Override @Override

View File

@@ -52,6 +52,7 @@ public class Settings extends SettingsActivity {
public static class LocalePickerActivity extends SettingsActivity { /* empty */ } public static class LocalePickerActivity extends SettingsActivity { /* empty */ }
public static class LanguageAndInputSettingsActivity extends SettingsActivity { /* empty */ } public static class LanguageAndInputSettingsActivity extends SettingsActivity { /* empty */ }
public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ } public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ }
public static class DarkThemeSettingsActivity extends SettingsActivity { /* empty */ }
public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ } public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ }
public static class NightDisplaySettingsActivity extends SettingsActivity { /* empty */ } public static class NightDisplaySettingsActivity extends SettingsActivity { /* empty */ }
public static class NightDisplaySuggestionActivity extends NightDisplaySettingsActivity { /* empty */ } public static class NightDisplaySuggestionActivity extends NightDisplaySettingsActivity { /* empty */ }

View File

@@ -26,6 +26,7 @@ import android.widget.TextView;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollIntroduction; import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -40,12 +41,15 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
private static final String TAG = "FaceIntro"; private static final String TAG = "FaceIntro";
private FaceManager mFaceManager; private FaceManager mFaceManager;
private FaceFeatureProvider mFaceFeatureProvider;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mFaceManager = Utils.getFaceManagerOrNull(this); mFaceManager = Utils.getFaceManagerOrNull(this);
mFaceFeatureProvider = FeatureFactory.getFactory(getApplicationContext())
.getFaceFeatureProvider();
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class); mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
if (WizardManagerHelper.isAnySetupWizard(getIntent())) { if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
@@ -87,6 +91,12 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
}); });
} }
final TextView footer2 = findViewById(R.id.face_enroll_introduction_footer_part_2);
final int footer2TextResource =
mFaceFeatureProvider.isAttentionSupported(getApplicationContext())
? R.string.security_settings_face_enroll_introduction_footer_part_2
: R.string.security_settings_face_settings_footer_attention_not_supported;
footer2.setText(footer2TextResource);
} }
@Override @Override

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.biometrics.face;
import android.content.Context;
/** Feature provider for face unlock */
public interface FaceFeatureProvider {
/** Returns true if attention checking is supported. */
boolean isAttentionSupported(Context context);
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.biometrics.face;
import android.content.Context;
import android.provider.Settings;
public class FaceFeatureProviderImpl implements FaceFeatureProvider {
@Override
public boolean isAttentionSupported(Context context) {
return true;
}
}

View File

@@ -37,6 +37,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
@@ -60,13 +61,16 @@ public class FaceSettings extends DashboardFragment {
private FaceManager mFaceManager; private FaceManager mFaceManager;
private int mUserId; private int mUserId;
private byte[] mToken; private byte[] mToken;
private FaceSettingsAttentionPreferenceController mAttentionController;
private FaceSettingsRemoveButtonPreferenceController mRemoveController; private FaceSettingsRemoveButtonPreferenceController mRemoveController;
private FaceSettingsEnrollButtonPreferenceController mEnrollController; private FaceSettingsEnrollButtonPreferenceController mEnrollController;
private FaceSettingsLockscreenBypassPreferenceController mLockscreenController;
private List<AbstractPreferenceController> mControllers; private List<AbstractPreferenceController> mControllers;
private List<Preference> mTogglePreferences; private List<Preference> mTogglePreferences;
private Preference mRemoveButton; private Preference mRemoveButton;
private Preference mEnrollButton; private Preference mEnrollButton;
private FaceFeatureProvider mFaceFeatureProvider;
private boolean mConfirmingPassword; private boolean mConfirmingPassword;
@@ -117,6 +121,7 @@ public class FaceSettings extends DashboardFragment {
mFaceManager = getPrefContext().getSystemService(FaceManager.class); mFaceManager = getPrefContext().getSystemService(FaceManager.class);
mUserId = getActivity().getIntent().getIntExtra( mUserId = getActivity().getIntent().getIntExtra(
Intent.EXTRA_USER_ID, UserHandle.myUserId()); Intent.EXTRA_USER_ID, UserHandle.myUserId());
mFaceFeatureProvider = FeatureFactory.getFactory(getContext()).getFaceFeatureProvider();
if (mUserManager.getUserInfo(mUserId).isManagedProfile()) { if (mUserManager.getUserInfo(mUserId).isManagedProfile()) {
getActivity().setTitle(getActivity().getResources().getString( getActivity().setTitle(getActivity().getResources().getString(
@@ -125,11 +130,12 @@ public class FaceSettings extends DashboardFragment {
Preference keyguardPref = findPreference(FaceSettingsKeyguardPreferenceController.KEY); Preference keyguardPref = findPreference(FaceSettingsKeyguardPreferenceController.KEY);
Preference appPref = findPreference(FaceSettingsAppPreferenceController.KEY); Preference appPref = findPreference(FaceSettingsAppPreferenceController.KEY);
Preference attentionPref = findPreference(FaceSettingsAttentionPreferenceController.KEY);
Preference confirmPref = findPreference(FaceSettingsConfirmPreferenceController.KEY); Preference confirmPref = findPreference(FaceSettingsConfirmPreferenceController.KEY);
Preference bypassPref = Preference bypassPref =
findPreference(FaceSettingsLockscreenBypassPreferenceController.KEY); findPreference(mLockscreenController.getPreferenceKey());
mTogglePreferences = new ArrayList<>( mTogglePreferences = new ArrayList<>(
Arrays.asList(keyguardPref, appPref, confirmPref, bypassPref)); Arrays.asList(keyguardPref, appPref, attentionPref, confirmPref, bypassPref));
mRemoveButton = findPreference(FaceSettingsRemoveButtonPreferenceController.KEY); mRemoveButton = findPreference(FaceSettingsRemoveButtonPreferenceController.KEY);
mEnrollButton = findPreference(FaceSettingsEnrollButtonPreferenceController.KEY); mEnrollButton = findPreference(FaceSettingsEnrollButtonPreferenceController.KEY);
@@ -147,6 +153,7 @@ public class FaceSettings extends DashboardFragment {
// Don't show keyguard controller for work profile settings. // Don't show keyguard controller for work profile settings.
if (mUserManager.isManagedProfile(mUserId)) { if (mUserManager.isManagedProfile(mUserId)) {
removePreference(FaceSettingsKeyguardPreferenceController.KEY); removePreference(FaceSettingsKeyguardPreferenceController.KEY);
removePreference(mLockscreenController.getPreferenceKey());
} }
if (savedInstanceState != null) { if (savedInstanceState != null) {
@@ -154,6 +161,14 @@ public class FaceSettings extends DashboardFragment {
} }
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
mLockscreenController = use(FaceSettingsLockscreenBypassPreferenceController.class);
mLockscreenController.setUserId(mUserId);
}
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -173,12 +188,17 @@ public class FaceSettings extends DashboardFragment {
finish(); finish();
} }
} else { } else {
mAttentionController.setToken(mToken);
mEnrollController.setToken(mToken); mEnrollController.setToken(mToken);
} }
final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId); final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId);
mEnrollButton.setVisible(!hasEnrolled); mEnrollButton.setVisible(!hasEnrolled);
mRemoveButton.setVisible(hasEnrolled); mRemoveButton.setVisible(hasEnrolled);
if (!mFaceFeatureProvider.isAttentionSupported(getContext())) {
removePreference(FaceSettingsAttentionPreferenceController.KEY);
}
} }
@Override @Override
@@ -193,6 +213,7 @@ public class FaceSettings extends DashboardFragment {
mToken = data.getByteArrayExtra( mToken = data.getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
if (mToken != null) { if (mToken != null) {
mAttentionController.setToken(mToken);
mEnrollController.setToken(mToken); mEnrollController.setToken(mToken);
} }
} }
@@ -236,7 +257,9 @@ public class FaceSettings extends DashboardFragment {
mControllers = buildPreferenceControllers(context, getSettingsLifecycle()); mControllers = buildPreferenceControllers(context, getSettingsLifecycle());
// There's no great way of doing this right now :/ // There's no great way of doing this right now :/
for (AbstractPreferenceController controller : mControllers) { for (AbstractPreferenceController controller : mControllers) {
if (controller instanceof FaceSettingsRemoveButtonPreferenceController) { if (controller instanceof FaceSettingsAttentionPreferenceController) {
mAttentionController = (FaceSettingsAttentionPreferenceController) controller;
} else if (controller instanceof FaceSettingsRemoveButtonPreferenceController) {
mRemoveController = (FaceSettingsRemoveButtonPreferenceController) controller; mRemoveController = (FaceSettingsRemoveButtonPreferenceController) controller;
mRemoveController.setListener(mRemovalListener); mRemoveController.setListener(mRemovalListener);
mRemoveController.setActivity((SettingsActivity) getActivity()); mRemoveController.setActivity((SettingsActivity) getActivity());
@@ -255,6 +278,7 @@ public class FaceSettings extends DashboardFragment {
controllers.add(new FaceSettingsVideoPreferenceController(context)); controllers.add(new FaceSettingsVideoPreferenceController(context));
controllers.add(new FaceSettingsKeyguardPreferenceController(context)); controllers.add(new FaceSettingsKeyguardPreferenceController(context));
controllers.add(new FaceSettingsAppPreferenceController(context)); controllers.add(new FaceSettingsAppPreferenceController(context));
controllers.add(new FaceSettingsAttentionPreferenceController(context));
controllers.add(new FaceSettingsRemoveButtonPreferenceController(context)); controllers.add(new FaceSettingsRemoveButtonPreferenceController(context));
controllers.add(new FaceSettingsFooterPreferenceController(context)); controllers.add(new FaceSettingsFooterPreferenceController(context));
controllers.add(new FaceSettingsConfirmPreferenceController(context)); controllers.add(new FaceSettingsConfirmPreferenceController(context));

View File

@@ -23,6 +23,7 @@ import androidx.preference.Preference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AnnotationSpan; import com.android.settings.utils.AnnotationSpan;
import com.android.settingslib.HelpUtils; import com.android.settingslib.HelpUtils;
import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.FooterPreference;
@@ -34,8 +35,11 @@ public class FaceSettingsFooterPreferenceController extends BasePreferenceContro
private static final String ANNOTATION_URL = "url"; private static final String ANNOTATION_URL = "url";
private FaceFeatureProvider mProvider;
public FaceSettingsFooterPreferenceController(Context context, String preferenceKey) { public FaceSettingsFooterPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey); super(context, preferenceKey);
mProvider = FeatureFactory.getFactory(context).getFaceFeatureProvider();
} }
public FaceSettingsFooterPreferenceController(Context context) { public FaceSettingsFooterPreferenceController(Context context) {
@@ -55,7 +59,12 @@ public class FaceSettingsFooterPreferenceController extends BasePreferenceContro
mContext, mContext.getString(R.string.help_url_face), getClass().getName()); mContext, mContext.getString(R.string.help_url_face), getClass().getName());
final AnnotationSpan.LinkInfo linkInfo = final AnnotationSpan.LinkInfo linkInfo =
new AnnotationSpan.LinkInfo(mContext, ANNOTATION_URL, helpIntent); new AnnotationSpan.LinkInfo(mContext, ANNOTATION_URL, helpIntent);
final int footerRes = mProvider.isAttentionSupported(mContext)
? R.string.security_settings_face_settings_footer
: R.string.security_settings_face_settings_footer_attention_not_supported;
preference.setTitle(AnnotationSpan.linkify( preference.setTitle(AnnotationSpan.linkify(
mContext.getText(R.string.security_settings_face_settings_footer), linkInfo)); mContext.getText(footerRes), linkInfo));
} }
} }

View File

@@ -30,8 +30,6 @@ import com.android.internal.annotations.VisibleForTesting;
public class FaceSettingsLockscreenBypassPreferenceController public class FaceSettingsLockscreenBypassPreferenceController
extends FaceSettingsPreferenceController { extends FaceSettingsPreferenceController {
static final String KEY = "security_lockscreen_bypass";
@VisibleForTesting @VisibleForTesting
protected FaceManager mFaceManager; protected FaceManager mFaceManager;
private UserManager mUserManager; private UserManager mUserManager;

View File

@@ -60,6 +60,7 @@ import java.util.Map;
public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceController implements public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop, OnDestroy, CachedBluetoothDevice.Callback { LifecycleObserver, OnStart, OnStop, OnDestroy, CachedBluetoothDevice.Callback {
private static final String TAG = "AdvancedBtHeaderCtrl"; private static final String TAG = "AdvancedBtHeaderCtrl";
private static final int LOW_BATTERY_LEVEL = 20;
@VisibleForTesting @VisibleForTesting
LayoutPreference mLayoutPreference; LayoutPreference mLayoutPreference;
@@ -180,12 +181,18 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
Drawable createBtBatteryIcon(Context context, int level, boolean charging) { Drawable createBtBatteryIcon(Context context, int level, boolean charging) {
final BatteryMeterView.BatteryMeterDrawable drawable = final BatteryMeterView.BatteryMeterDrawable drawable =
new BatteryMeterView.BatteryMeterDrawable(context, new BatteryMeterView.BatteryMeterDrawable(context,
context.getColor(R.color.meter_background_color)); context.getColor(R.color.meter_background_color),
context.getResources().getDimensionPixelSize(
R.dimen.advanced_bluetooth_battery_meter_width),
context.getResources().getDimensionPixelSize(
R.dimen.advanced_bluetooth_battery_meter_height));
drawable.setBatteryLevel(level); drawable.setBatteryLevel(level);
final int attr = level > LOW_BATTERY_LEVEL || charging
? android.R.attr.colorControlNormal
: android.R.attr.colorError;
drawable.setColorFilter(new PorterDuffColorFilter( drawable.setColorFilter(new PorterDuffColorFilter(
com.android.settings.Utils.getColorAttrDefaultColor(context, com.android.settings.Utils.getColorAttrDefaultColor(context, attr),
android.R.attr.colorControlNormal), PorterDuff.Mode.SRC));
PorterDuff.Mode.SRC_IN));
drawable.setCharging(charging); drawable.setCharging(charging);
return drawable; return drawable;

View File

@@ -136,6 +136,6 @@ public class BluetoothSliceBuilder {
final Intent intent = new Intent(ACTION_BLUETOOTH_SLICE_CHANGED) final Intent intent = new Intent(ACTION_BLUETOOTH_SLICE_CHANGED)
.setClass(context, SliceBroadcastReceiver.class); .setClass(context, SliceBroadcastReceiver.class);
return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent,
PendingIntent.FLAG_CANCEL_CURRENT); PendingIntent.FLAG_UPDATE_CURRENT);
} }
} }

View File

@@ -17,6 +17,7 @@ package com.android.settings.core;
import android.annotation.LayoutRes; import android.annotation.LayoutRes;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@@ -26,6 +27,7 @@ import android.content.pm.PackageManager;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -61,6 +63,10 @@ public class SettingsBaseActivity extends FragmentActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (isLockTaskModePinned() && !isSettingsRunOnTop()) {
Log.w(TAG, "Devices lock task mode pinned.");
finish();
}
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this)); getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
@@ -148,6 +154,20 @@ public class SettingsBaseActivity extends FragmentActivity {
} }
} }
private boolean isLockTaskModePinned() {
final ActivityManager activityManager =
getApplicationContext().getSystemService(ActivityManager.class);
return activityManager.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
}
private boolean isSettingsRunOnTop() {
final ActivityManager activityManager =
getApplicationContext().getSystemService(ActivityManager.class);
final String taskPkgName = activityManager.getRunningTasks(1 /* maxNum */)
.get(0 /* index */).baseActivity.getPackageName();
return TextUtils.equals(getPackageName(), taskPkgName);
}
/** /**
* @return whether or not the enabled state actually changed. * @return whether or not the enabled state actually changed.
*/ */

View File

@@ -74,6 +74,7 @@ import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings; import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings;
import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard; import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard;
import com.android.settings.display.NightDisplaySettings; import com.android.settings.display.NightDisplaySettings;
import com.android.settings.display.darkmode.DarkModeSettingsFragment;
import com.android.settings.dream.DreamSettings; import com.android.settings.dream.DreamSettings;
import com.android.settings.enterprise.EnterprisePrivacySettings; import com.android.settings.enterprise.EnterprisePrivacySettings;
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
@@ -281,7 +282,8 @@ public class SettingsGateway {
PreviouslyConnectedDeviceDashboardFragment.class.getName(), PreviouslyConnectedDeviceDashboardFragment.class.getName(),
BatterySaverScheduleSettings.class.getName(), BatterySaverScheduleSettings.class.getName(),
MobileNetworkListFragment.class.getName(), MobileNetworkListFragment.class.getName(),
GlobalActionsPanelSettings.class.getName() GlobalActionsPanelSettings.class.getName(),
DarkModeSettingsFragment.class.getName()
}; };
public static final String[] SETTINGS_FOR_RESTRICTED = { public static final String[] SETTINGS_FOR_RESTRICTED = {

View File

@@ -111,7 +111,7 @@ public class MainlineModuleVersionPreferenceController extends BasePreferenceCon
try { try {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern,
Locale.getDefault()); Locale.getDefault());
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); simpleDateFormat.setTimeZone(TimeZone.getDefault());
return Optional.of(simpleDateFormat.parse(text)); return Optional.of(simpleDateFormat.parse(text));
} catch (ParseException e) { } catch (ParseException e) {
// ignore and try next pattern // ignore and try next pattern

View File

@@ -21,6 +21,7 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.PowerManager; import android.os.PowerManager;
import android.provider.Settings; import android.provider.Settings;
@@ -28,7 +29,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController; import com.android.settings.core.TogglePreferenceController;
@@ -44,7 +44,7 @@ public class DarkUIPreferenceController extends TogglePreferenceController imple
public static final int DIALOG_SEEN = 1; public static final int DIALOG_SEEN = 1;
@VisibleForTesting @VisibleForTesting
SwitchPreference mPreference; Preference mPreference;
private UiModeManager mUiModeManager; private UiModeManager mUiModeManager;
private PowerManager mPowerManager; private PowerManager mPowerManager;
@@ -68,7 +68,8 @@ public class DarkUIPreferenceController extends TogglePreferenceController imple
@Override @Override
public boolean isChecked() { public boolean isChecked() {
return mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES; return (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
} }
@Override @Override
@@ -90,17 +91,13 @@ public class DarkUIPreferenceController extends TogglePreferenceController imple
Settings.Secure.DARK_MODE_DIALOG_SEEN, 0) == DIALOG_SEEN; Settings.Secure.DARK_MODE_DIALOG_SEEN, 0) == DIALOG_SEEN;
if (!dialogSeen && isChecked) { if (!dialogSeen && isChecked) {
showDarkModeDialog(); showDarkModeDialog();
return false;
} }
mUiModeManager.setNightMode(isChecked return mUiModeManager.setNightModeActivated(isChecked);
? UiModeManager.MODE_NIGHT_YES
: UiModeManager.MODE_NIGHT_NO);
return true;
} }
private void showDarkModeDialog() { private void showDarkModeDialog() {
final DarkUIInfoDialogFragment frag = new DarkUIInfoDialogFragment(); final DarkUIInfoDialogFragment frag = new DarkUIInfoDialogFragment();
if (mFragment.getFragmentManager() != null) { if (mFragment != null && mFragment.getFragmentManager() != null) {
frag.show(mFragment.getFragmentManager(), getClass().getName()); frag.show(mFragment.getFragmentManager(), getClass().getName());
} }
} }
@@ -113,12 +110,10 @@ public class DarkUIPreferenceController extends TogglePreferenceController imple
boolean isBatterySaver = isPowerSaveMode(); boolean isBatterySaver = isPowerSaveMode();
mPreference.setEnabled(!isBatterySaver); mPreference.setEnabled(!isBatterySaver);
if (isBatterySaver) { if (isBatterySaver) {
int stringId = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES int stringId = isChecked()
? R.string.dark_ui_mode_disabled_summary_dark_theme_on ? R.string.dark_ui_mode_disabled_summary_dark_theme_on
: R.string.dark_ui_mode_disabled_summary_dark_theme_off; : R.string.dark_ui_mode_disabled_summary_dark_theme_off;
mPreference.setSummary(mContext.getString(stringId)); mPreference.setSummary(mContext.getString(stringId));
} else {
mPreference.setSummary(null);
} }
} }
@@ -127,22 +122,17 @@ public class DarkUIPreferenceController extends TogglePreferenceController imple
return mPowerManager.isPowerSaveMode(); return mPowerManager.isPowerSaveMode();
} }
@VisibleForTesting
void setUiModeManager(UiModeManager uiModeManager) {
mUiModeManager = uiModeManager;
}
public void setParentFragment(Fragment fragment) {
mFragment = fragment;
}
@Override @Override
public void onStart() { public void onStart() {
mContext.registerReceiver(mReceiver, mContext.registerReceiver(mReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
} }
// used by AccessibilitySettings
public void setParentFragment(Fragment fragment) {
mFragment = fragment;
}
@Override @Override
public void onStop() { public void onStop() {
mContext.unregisterReceiver(mReceiver); mContext.unregisterReceiver(mReceiver);

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.app.UiModeManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.PowerManager;
import android.view.View;
import android.widget.Button;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.widget.LayoutPreference;
/**
* Controller for activate/deactivate night mode button
*/
public class DarkModeActivationPreferenceController extends BasePreferenceController {
private final UiModeManager mUiModeManager;
private PowerManager mPowerManager;
private Button mTurnOffButton;
private Button mTurnOnButton;
public DarkModeActivationPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
mPowerManager = context.getSystemService(PowerManager.class);
mUiModeManager = context.getSystemService(UiModeManager.class);
}
@Override
public final void updateState(Preference preference) {
final boolean batterySaver = mPowerManager.isPowerSaveMode();
if (batterySaver) {
mTurnOnButton.setVisibility(View.GONE);
mTurnOffButton.setVisibility(View.GONE);
return;
}
final boolean active = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
updateNightMode(active);
}
private void updateNightMode(boolean active) {
final int autoMode = mUiModeManager.getNightMode();
String buttonText;
if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
buttonText = mContext.getString(active
? R.string.dark_ui_activation_off_auto
: R.string.dark_ui_activation_on_auto);
} else {
buttonText = mContext.getString(active
? R.string.dark_ui_activation_off_manual
: R.string.dark_ui_activation_on_manual);
}
if (active) {
mTurnOnButton.setVisibility(View.GONE);
mTurnOffButton.setVisibility(View.VISIBLE);
mTurnOffButton.setText(buttonText);
} else {
mTurnOnButton.setVisibility(View.VISIBLE);
mTurnOffButton.setVisibility(View.GONE);
mTurnOnButton.setText(buttonText);
}
}
@Override
public CharSequence getSummary() {
final boolean isActivated = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
final int autoMode = mUiModeManager.getNightMode();
if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
return mContext.getString(isActivated
? R.string.dark_ui_summary_on_auto_mode_auto
: R.string.dark_ui_summary_off_auto_mode_auto);
} else {
return mContext.getString(isActivated
? R.string.dark_ui_summary_on_auto_mode_never
: R.string.dark_ui_summary_off_auto_mode_never);
}
}
private final View.OnClickListener mListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
final boolean active = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
mUiModeManager.setNightModeActivated(!active);
updateNightMode(!active);
}
};
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final LayoutPreference preference = screen.findPreference(getPreferenceKey());
mTurnOnButton = preference.findViewById(R.id.dark_ui_turn_on_button);
mTurnOnButton.setOnClickListener(mListener);
mTurnOffButton = preference.findViewById(R.id.dark_ui_turn_off_button);
mTurnOffButton.setOnClickListener(mListener);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
/**
* Observes changes for dark night settings*/
public class DarkModeObserver {
private static final String TAG = "DarkModeObserver";
private ContentObserver mContentObserver;
private final BroadcastReceiver mBatterySaverReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mCallback.run();
}
};
private Runnable mCallback;
private Context mContext;
public DarkModeObserver(Context context) {
mContext = context;
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
final String setting = uri == null ? null : uri.getLastPathSegment();
if (setting != null && mCallback != null) {
switch (setting) {
case Settings.Secure.UI_NIGHT_MODE:
mCallback.run();
break;
default:
}
}
}
};
}
/**
* subscribe callback when night mode changed in the database
*
* @param callback the callback that gets triggered when subscribed
*/
public void subscribe(Runnable callback) {
callback.run();
mCallback = callback;
final Uri uri = Settings.Secure.getUriFor(Settings.Secure.UI_NIGHT_MODE);
mContext.getContentResolver().registerContentObserver(uri, false, mContentObserver);
final IntentFilter batteryFilter = new IntentFilter();
batteryFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
mContext.registerReceiver(
mBatterySaverReceiver, batteryFilter);
}
/**
* unsubscribe from dark ui database changes
*/
public void unsubscribe() {
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
try {
mContext.unregisterReceiver(mBatterySaverReceiver);
} catch (IllegalArgumentException e) {
/* Ignore: unregistering an unregistered receiver */
Log.w(TAG, e.getMessage());
}
// NO-OP
mCallback = null;
}
@VisibleForTesting
protected void setContentObserver(ContentObserver co) {
mContentObserver = co;
}
@VisibleForTesting
protected ContentObserver getContentObserver() {
return mContentObserver;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.app.UiModeManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.PowerManager;
import android.util.AttributeSet;
import com.android.settings.R;
import com.android.settings.widget.MasterSwitchPreference;
/**
* component for the display settings dark ui summary*/
public class DarkModePreference extends MasterSwitchPreference {
private UiModeManager mUiModeManager;
private DarkModeObserver mDarkModeObserver;
private PowerManager mPowerManager;
private Runnable mCallback;
public DarkModePreference(Context context, AttributeSet attrs) {
super(context, attrs);
mDarkModeObserver = new DarkModeObserver(context);
mUiModeManager = context.getSystemService(UiModeManager.class);
mPowerManager = context.getSystemService(PowerManager.class);
mCallback = () -> {
final boolean batterySaver = mPowerManager.isPowerSaveMode();
final boolean active = (getContext().getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
setSwitchEnabled(!batterySaver);
updateSummary(batterySaver, active);
};
mDarkModeObserver.subscribe(mCallback);
}
@Override
public void onAttached() {
super.onAttached();
mDarkModeObserver.subscribe(mCallback);
}
@Override
public void onDetached() {
super.onDetached();
mDarkModeObserver.unsubscribe();
}
private void updateSummary(boolean batterySaver, boolean active) {
if (batterySaver) {
final int stringId = active ? R.string.dark_ui_mode_disabled_summary_dark_theme_on
: R.string.dark_ui_mode_disabled_summary_dark_theme_off;
setSummary(getContext().getString(stringId));
return;
}
final boolean auto = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO;
String detail;
if (active) {
detail = getContext().getString(auto
? R.string.dark_ui_summary_on_auto_mode_auto
: R.string.dark_ui_summary_on_auto_mode_never);
} else {
detail = getContext().getString(auto
? R.string.dark_ui_summary_off_auto_mode_auto
: R.string.dark_ui_summary_off_auto_mode_never);
}
String summary = getContext().getString(active
? R.string.dark_ui_summary_on
: R.string.dark_ui_summary_off, detail);
setSummary(summary);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.app.UiModeManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.PowerManager;
import androidx.preference.DropDownPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
/**
* Controller for the dark ui option dropdown
*/
public class DarkModeScheduleSelectorController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
private final UiModeManager mUiModeManager;
private PowerManager mPowerManager;
private DropDownPreference mPreference;
private String mCurrentMode;
public DarkModeScheduleSelectorController(Context context, String key) {
super(context, key);
mUiModeManager = context.getSystemService(UiModeManager.class);
mPowerManager = context.getSystemService(PowerManager.class);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public int getAvailabilityStatus() {
return BasePreferenceController.AVAILABLE;
}
@Override
public final void updateState(Preference preference) {
final boolean batterySaver = mPowerManager.isPowerSaveMode();
mPreference.setEnabled(!batterySaver);
mCurrentMode =
mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO
? mContext.getString(R.string.dark_ui_auto_mode_auto)
: mContext.getString(R.string.dark_ui_auto_mode_never);
mPreference.setValue(mCurrentMode);
}
@Override
public final boolean onPreferenceChange(Preference preference, Object newValue) {
String newMode = (String) newValue;
if (newMode == mCurrentMode) {
return false;
}
mCurrentMode = newMode;
if (mCurrentMode == mContext.getString(R.string.dark_ui_auto_mode_never)) {
boolean active = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
int mode = active ? UiModeManager.MODE_NIGHT_YES
: UiModeManager.MODE_NIGHT_NO;
mUiModeManager.setNightMode(mode);
} else if (mCurrentMode ==
mContext.getString(R.string.dark_ui_auto_mode_auto)) {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_AUTO);
}
return true;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.content.Context;
import android.os.Bundle;
import android.app.settings.SettingsEnums;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
/**
* Settings screen for Dark UI Mode
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class DarkModeSettingsFragment extends DashboardFragment {
private static final String TAG = "DarkModeSettingsFragment";
private DarkModeObserver mContentObserver;
private Runnable mCallback = () -> {
updatePreferenceStates();
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = getContext();
mContentObserver = new DarkModeObserver(context);
}
@Override
public void onStart() {
super.onStart();
// Listen for changes only while visible.
mContentObserver.subscribe(mCallback);
}
@Override
public void onStop() {
super.onStop();
// Stop listening for state changes.
mContentObserver.unsubscribe();
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.dark_mode_settings;
}
@Override
public int getHelpResource() {
return R.string.help_url_dark_theme;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DARK_UI_SETTINGS;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider();
}

View File

@@ -55,8 +55,6 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
TextView mBatteryPercentText; TextView mBatteryPercentText;
@VisibleForTesting @VisibleForTesting
TextView mSummary1; TextView mSummary1;
@VisibleForTesting
TextView mSummary2;
private Activity mActivity; private Activity mActivity;
private PreferenceFragmentCompat mHost; private PreferenceFragmentCompat mHost;
@@ -90,7 +88,6 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
.findViewById(R.id.battery_header_icon); .findViewById(R.id.battery_header_icon);
mBatteryPercentText = mBatteryLayoutPref.findViewById(R.id.battery_percent); mBatteryPercentText = mBatteryLayoutPref.findViewById(R.id.battery_percent);
mSummary1 = mBatteryLayoutPref.findViewById(R.id.summary1); mSummary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
mSummary2 = mBatteryLayoutPref.findViewById(R.id.summary2);
quickUpdateHeaderPreference(); quickUpdateHeaderPreference();
} }
@@ -115,9 +112,6 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
} else { } else {
mSummary1.setText(info.remainingLabel); mSummary1.setText(info.remainingLabel);
} }
// Clear this just to be sure we don't get UI jank on re-entering this view from another
// activity.
mSummary2.setText("");
mBatteryMeterView.setBatteryLevel(info.batteryLevel); mBatteryMeterView.setBatteryLevel(info.batteryLevel);
mBatteryMeterView.setCharging(!info.discharging); mBatteryMeterView.setCharging(!info.discharging);

View File

@@ -117,6 +117,13 @@ public class BatteryMeterView extends ImageView {
.getDimensionPixelSize(R.dimen.battery_meter_height); .getDimensionPixelSize(R.dimen.battery_meter_height);
} }
public BatteryMeterDrawable(Context context, int frameColor, int width, int height) {
super(context, frameColor);
mIntrinsicWidth = width;
mIntrinsicHeight = height;
}
@Override @Override
public int getIntrinsicWidth() { public int getIntrinsicWidth() {
return mIntrinsicWidth; return mIntrinsicWidth;

View File

@@ -19,7 +19,6 @@ package com.android.settings.fuelgauge;
import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType; import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.net.Uri; import android.net.Uri;
@@ -27,7 +26,6 @@ import android.os.BatteryStats;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.provider.SearchIndexableResource; import android.provider.SearchIndexableResource;
import android.provider.Settings;
import android.provider.Settings.Global; import android.provider.Settings.Global;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.view.Menu; import android.view.Menu;
@@ -162,7 +160,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
final TextView percentRemaining = final TextView percentRemaining =
mBatteryLayoutPref.findViewById(R.id.battery_percent); mBatteryLayoutPref.findViewById(R.id.battery_percent);
final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1); final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
final TextView summary2 = mBatteryLayoutPref.findViewById(R.id.summary2);
BatteryInfo oldInfo = batteryInfos.get(0); BatteryInfo oldInfo = batteryInfos.get(0);
BatteryInfo newInfo = batteryInfos.get(1); BatteryInfo newInfo = batteryInfos.get(1);
percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel)); percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel));
@@ -170,14 +167,13 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
// set the text to the old estimate (copied from battery info). Note that this // set the text to the old estimate (copied from battery info). Note that this
// can sometimes say 0 time remaining because battery stats requires the phone // can sometimes say 0 time remaining because battery stats requires the phone
// be unplugged for a period of time before being willing ot make an estimate. // be unplugged for a period of time before being willing ot make an estimate.
summary1.setText(mPowerFeatureProvider.getOldEstimateDebugString( final String OldEstimateString = mPowerFeatureProvider.getOldEstimateDebugString(
Formatter.formatShortElapsedTime(getContext(), Formatter.formatShortElapsedTime(getContext(),
PowerUtil.convertUsToMs(oldInfo.remainingTimeUs)))); PowerUtil.convertUsToMs(oldInfo.remainingTimeUs)));
final String NewEstimateString = mPowerFeatureProvider.getEnhancedEstimateDebugString(
// for this one we can just set the string directly
summary2.setText(mPowerFeatureProvider.getEnhancedEstimateDebugString(
Formatter.formatShortElapsedTime(getContext(), Formatter.formatShortElapsedTime(getContext(),
PowerUtil.convertUsToMs(newInfo.remainingTimeUs)))); PowerUtil.convertUsToMs(newInfo.remainingTimeUs)));
summary1.setText(OldEstimateString + "\n" + NewEstimateString);
batteryView.setBatteryLevel(oldInfo.batteryLevel); batteryView.setBatteryLevel(oldInfo.batteryLevel);
batteryView.setCharging(!oldInfo.discharging); batteryView.setCharging(!oldInfo.discharging);

View File

@@ -33,6 +33,7 @@ import androidx.fragment.app.FragmentTransaction;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.accounts.AvatarViewMixin; import com.android.settings.accounts.AvatarViewMixin;
import com.android.settings.core.HideNonSystemOverlayMixin;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
@@ -54,8 +55,8 @@ public class SettingsHomepageActivity extends FragmentActivity {
.initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE); .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);
final ImageView avatarView = findViewById(R.id.account_avatar); final ImageView avatarView = findViewById(R.id.account_avatar);
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(this, avatarView); getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
getLifecycle().addObserver(avatarViewMixin); getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
if (!getSystemService(ActivityManager.class).isLowRamDevice()) { if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
// Only allow contextual feature on high ram devices. // Only allow contextual feature on high ram devices.

View File

@@ -37,12 +37,11 @@ import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUt
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.AsyncLoaderCompat; import com.android.settingslib.utils.AsyncLoaderCompat;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@@ -56,7 +55,6 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
private static final String TAG = "ContextualCardLoader"; private static final String TAG = "ContextualCardLoader";
private static final long ELIGIBILITY_CHECKER_TIMEOUT_MS = 250; private static final long ELIGIBILITY_CHECKER_TIMEOUT_MS = 250;
private final ExecutorService mExecutorService;
private final ContentObserver mObserver = new ContentObserver( private final ContentObserver mObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) { new Handler(Looper.getMainLooper())) {
@Override @Override
@@ -76,7 +74,6 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
ContextualCardLoader(Context context) { ContextualCardLoader(Context context) {
super(context); super(context);
mContext = context.getApplicationContext(); mContext = context.getApplicationContext();
mExecutorService = Executors.newCachedThreadPool();
} }
@Override @Override
@@ -163,8 +160,8 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
final List<Future<ContextualCard>> eligibleCards = new ArrayList<>(); final List<Future<ContextualCard>> eligibleCards = new ArrayList<>();
for (ContextualCard card : candidates) { for (ContextualCard card : candidates) {
final EligibleCardChecker future = new EligibleCardChecker(mContext, card); final EligibleCardChecker checker = new EligibleCardChecker(mContext, card);
eligibleCards.add(mExecutorService.submit(future)); eligibleCards.add(ThreadUtils.postOnBackgroundThread(checker));
} }
// Collect future and eligible cards // Collect future and eligible cards
for (Future<ContextualCard> cardFuture : eligibleCards) { for (Future<ContextualCard> cardFuture : eligibleCards) {

View File

@@ -78,6 +78,12 @@ public class SettingsContextualCardProvider extends ContextualCardProvider {
.setCardName(CustomSliceRegistry.FACE_ENROLL_SLICE_URI.toString()) .setCardName(CustomSliceRegistry.FACE_ENROLL_SLICE_URI.toString())
.setCardCategory(ContextualCard.Category.DEFAULT) .setCardCategory(ContextualCard.Category.DEFAULT)
.build(); .build();
final ContextualCard darkThemeCard =
ContextualCard.newBuilder()
.setSliceUri(CustomSliceRegistry.DARK_THEME_SLICE_URI.toString())
.setCardName(CustomSliceRegistry.DARK_THEME_SLICE_URI.toString())
.setCardCategory(ContextualCard.Category.IMPORTANT)
.build();
final ContextualCardList cards = ContextualCardList.newBuilder() final ContextualCardList cards = ContextualCardList.newBuilder()
.addCard(wifiCard) .addCard(wifiCard)
.addCard(connectedDeviceCard) .addCard(connectedDeviceCard)
@@ -86,6 +92,7 @@ public class SettingsContextualCardProvider extends ContextualCardProvider {
.addCard(notificationChannelCard) .addCard(notificationChannelCard)
.addCard(contextualAdaptiveSleepCard) .addCard(contextualAdaptiveSleepCard)
.addCard(contextualFaceSettingsCard) .addCard(contextualFaceSettingsCard)
.addCard(darkThemeCard)
.build(); .build();
return cards; return cards;

View File

@@ -23,13 +23,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.homepage.contextualcards.ContextualCard; import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@@ -42,7 +41,6 @@ public class ConditionManager {
private static final long DISPLAYABLE_CHECKER_TIMEOUT_MS = 20; private static final long DISPLAYABLE_CHECKER_TIMEOUT_MS = 20;
private final ExecutorService mExecutorService;
private final Context mAppContext; private final Context mAppContext;
private final ConditionListener mListener; private final ConditionListener mListener;
@@ -50,7 +48,6 @@ public class ConditionManager {
public ConditionManager(Context context, ConditionListener listener) { public ConditionManager(Context context, ConditionListener listener) {
mAppContext = context.getApplicationContext(); mAppContext = context.getApplicationContext();
mExecutorService = Executors.newCachedThreadPool();
mCardControllers = new ArrayList<>(); mCardControllers = new ArrayList<>();
mListener = listener; mListener = listener;
initCandidates(); initCandidates();
@@ -64,8 +61,8 @@ public class ConditionManager {
final List<Future<ContextualCard>> displayableCards = new ArrayList<>(); final List<Future<ContextualCard>> displayableCards = new ArrayList<>();
// Check displayable future // Check displayable future
for (ConditionalCardController card : mCardControllers) { for (ConditionalCardController card : mCardControllers) {
final DisplayableChecker future = new DisplayableChecker(getController(card.getId())); final DisplayableChecker checker = new DisplayableChecker(getController(card.getId()));
displayableCards.add(mExecutorService.submit(future)); displayableCards.add(ThreadUtils.postOnBackgroundThread(checker));
} }
// Collect future and add displayable cards // Collect future and add displayable cards
for (Future<ContextualCard> cardFuture : displayableCards) { for (Future<ContextualCard> cardFuture : displayableCards) {

View File

@@ -0,0 +1,185 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.homepage.contextualcards.slices;
import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
import static android.provider.Settings.Global.LOW_POWER_MODE;
import android.annotation.ColorInt;
import android.app.PendingIntent;
import android.app.UiModeManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBackgroundWorker;
import java.io.IOException;
public class DarkThemeSlice implements CustomSliceable {
private static final String TAG = "DarkThemeSlice";
private static final int BATTERY_LEVEL_THRESHOLD = 50;
private static final int DELAY_TIME_EXECUTING_DARK_THEME = 200;
// Keep the slice even Dark theme mode changed when it is on HomePage
@VisibleForTesting
static boolean sKeepSliceShow;
@VisibleForTesting
static long sActiveUiSession = -1000;
private final Context mContext;
private final UiModeManager mUiModeManager;
private final PowerManager mPowerManager;
public DarkThemeSlice(Context context) {
mContext = context;
mUiModeManager = context.getSystemService(UiModeManager.class);
mPowerManager = context.getSystemService(PowerManager.class);
}
@Override
public Slice getSlice() {
final long currentUiSession = FeatureFactory.getFactory(mContext)
.getSlicesFeatureProvider().getUiSessionToken();
if (currentUiSession != sActiveUiSession) {
sActiveUiSession = currentUiSession;
sKeepSliceShow = false;
}
// Dark theme slice will disappear when battery saver is ON.
if (mPowerManager.isPowerSaveMode() || (!sKeepSliceShow && !isAvailable(mContext))) {
return new ListBuilder(mContext, CustomSliceRegistry.DARK_THEME_SLICE_URI,
ListBuilder.INFINITY)
.setIsError(true)
.build();
}
sKeepSliceShow = true;
final PendingIntent toggleAction = getBroadcastIntent(mContext);
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
final IconCompat icon =
IconCompat.createWithResource(mContext, R.drawable.dark_theme);
return new ListBuilder(mContext, CustomSliceRegistry.DARK_THEME_SLICE_URI,
ListBuilder.INFINITY)
.setAccentColor(color)
.addRow(new ListBuilder.RowBuilder()
.setTitle(mContext.getText(R.string.dark_theme_slice_title))
.setTitleItem(icon, ICON_IMAGE)
.setSubtitle(mContext.getText(R.string.dark_theme_slice_subtitle))
.setPrimaryAction(
SliceAction.createToggle(toggleAction, null /* actionTitle */,
isDarkThemeMode(mContext))))
.build();
}
@Override
public Uri getUri() {
return CustomSliceRegistry.DARK_THEME_SLICE_URI;
}
@Override
public void onNotifyChange(Intent intent) {
final boolean isChecked = intent.getBooleanExtra(android.app.slice.Slice.EXTRA_TOGGLE_STATE,
false);
// make toggle transition more smooth before dark theme takes effect
new Handler(Looper.getMainLooper()).postDelayed(() -> {
mUiModeManager.setNightModeActivated(isChecked);
}, DELAY_TIME_EXECUTING_DARK_THEME);
}
@Override
public Intent getIntent() {
return null;
}
@Override
public Class getBackgroundWorkerClass() {
return DarkThemeWorker.class;
}
@VisibleForTesting
boolean isAvailable(Context context) {
// checking dark theme mode.
if (isDarkThemeMode(context)) {
return false;
}
// checking the current battery level
final BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
final int level = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
Log.d(TAG, "battery level=" + level);
return level <= BATTERY_LEVEL_THRESHOLD;
}
@VisibleForTesting
boolean isDarkThemeMode(Context context) {
final int currentNightMode =
context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
}
public static class DarkThemeWorker extends SliceBackgroundWorker<Void> {
private final Context mContext;
private final ContentObserver mContentObserver =
new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean bChanged) {
if (mContext.getSystemService(PowerManager.class).isPowerSaveMode()) {
notifySliceChange();
}
}
};
public DarkThemeWorker(Context context, Uri uri) {
super(context, uri);
mContext = context;
}
@Override
protected void onSlicePinned() {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(LOW_POWER_MODE), false /* notifyForDescendants */,
mContentObserver);
}
@Override
protected void onSliceUnpinned() {
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
}
@Override
public void close() throws IOException {
}
}
}

View File

@@ -20,8 +20,6 @@ import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static com.android.settings.notification.ChannelListPreferenceController.ARG_FROM_SETTINGS;
import android.app.Application; import android.app.Application;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationChannelGroup; import android.app.NotificationChannelGroup;
@@ -62,13 +60,12 @@ import com.android.settings.slices.SliceBuilderUtils;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@@ -135,7 +132,6 @@ public class NotificationChannelSlice implements CustomSliceable {
}; };
protected final Context mContext; protected final Context mContext;
private final ExecutorService mExecutorService;
@VisibleForTesting @VisibleForTesting
NotificationBackend mNotificationBackend; NotificationBackend mNotificationBackend;
private NotificationBackend.AppRow mAppRow; private NotificationBackend.AppRow mAppRow;
@@ -145,7 +141,6 @@ public class NotificationChannelSlice implements CustomSliceable {
public NotificationChannelSlice(Context context) { public NotificationChannelSlice(Context context) {
mContext = context; mContext = context;
mNotificationBackend = new NotificationBackend(); mNotificationBackend = new NotificationBackend();
mExecutorService = Executors.newCachedThreadPool();
} }
@Override @Override
@@ -376,9 +371,9 @@ public class NotificationChannelSlice implements CustomSliceable {
// Create tasks to get notification data for multi-channel packages. // Create tasks to get notification data for multi-channel packages.
final List<Future<NotificationBackend.AppRow>> appRowTasks = new ArrayList<>(); final List<Future<NotificationBackend.AppRow>> appRowTasks = new ArrayList<>();
for (PackageInfo packageInfo : packageInfoList) { for (PackageInfo packageInfo : packageInfoList) {
final NotificationMultiChannelAppRow future = new NotificationMultiChannelAppRow( final NotificationMultiChannelAppRow appRow = new NotificationMultiChannelAppRow(
mContext, mNotificationBackend, packageInfo); mContext, mNotificationBackend, packageInfo);
appRowTasks.add(mExecutorService.submit(future)); appRowTasks.add(ThreadUtils.postOnBackgroundThread(appRow));
} }
// Get the package which has sent at least ~10 notifications and not turn off channels. // Get the package which has sent at least ~10 notifications and not turn off channels.

View File

@@ -22,11 +22,8 @@ import android.os.Looper;
import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.network.MobileDataContentObserver; import com.android.settings.network.MobileDataContentObserver;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -41,14 +38,14 @@ public class MmsMessagePreferenceController extends TelephonyTogglePreferenceCon
private TelephonyManager mTelephonyManager; private TelephonyManager mTelephonyManager;
private SubscriptionManager mSubscriptionManager; private SubscriptionManager mSubscriptionManager;
private MobileDataContentObserver mMobileDataContentObserver; private MobileDataContentObserver mMobileDataContentObserver;
private SwitchPreference mPreference; private PreferenceScreen mScreen;
public MmsMessagePreferenceController(Context context, String key) { public MmsMessagePreferenceController(Context context, String key) {
super(context, key); super(context, key);
mSubscriptionManager = context.getSystemService(SubscriptionManager.class); mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
mMobileDataContentObserver = new MobileDataContentObserver( mMobileDataContentObserver = new MobileDataContentObserver(
new Handler(Looper.getMainLooper())); new Handler(Looper.getMainLooper()));
mMobileDataContentObserver.setOnMobileDataChangedListener(()->updateState(mPreference)); mMobileDataContentObserver.setOnMobileDataChangedListener(()->refreshPreference());
} }
@Override @Override
@@ -79,15 +76,9 @@ public class MmsMessagePreferenceController extends TelephonyTogglePreferenceCon
@Override @Override
public void displayPreference(PreferenceScreen screen) { public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen); super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey()); mScreen = screen;
} }
@Override
public void updateState(Preference preference) {
super.updateState(preference);
preference.setVisible(isAvailable());
((SwitchPreference) preference).setChecked(isChecked());
}
public void init(int subId) { public void init(int subId) {
mSubId = subId; mSubId = subId;
@@ -104,4 +95,10 @@ public class MmsMessagePreferenceController extends TelephonyTogglePreferenceCon
public boolean isChecked() { public boolean isChecked() {
return mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS); return mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS);
} }
private void refreshPreference() {
if (mScreen != null) {
super.displayPreference(mScreen);
}
}
} }

View File

@@ -228,16 +228,23 @@ public class MobileNetworkUtils {
final String currentCountry = tm.getNetworkCountryIso().toLowerCase(); final String currentCountry = tm.getNetworkCountryIso().toLowerCase();
final String supportedCountries = final String supportedCountries =
Settings.Global.getString(cr, Settings.Global.EUICC_SUPPORTED_COUNTRIES); Settings.Global.getString(cr, Settings.Global.EUICC_SUPPORTED_COUNTRIES);
final String unsupportedCountries =
Settings.Global.getString(cr, Settings.Global.EUICC_UNSUPPORTED_COUNTRIES);
boolean inEsimSupportedCountries = false; boolean inEsimSupportedCountries = false;
if (TextUtils.isEmpty(currentCountry)) {
inEsimSupportedCountries = true; if (TextUtils.isEmpty(supportedCountries)) {
} else if (!TextUtils.isEmpty(supportedCountries)) { // White list is empty, use blacklist.
final List<String> supportedCountryList = Log.d(TAG, "Using blacklist unsupportedCountries=" + unsupportedCountries);
Arrays.asList(TextUtils.split(supportedCountries.toLowerCase(), ",")); inEsimSupportedCountries = !isEsimUnsupportedCountry(currentCountry,
if (supportedCountryList.contains(currentCountry)) { unsupportedCountries);
inEsimSupportedCountries = true; } else {
} Log.d(TAG, "Using whitelist supportedCountries=" + supportedCountries);
inEsimSupportedCountries = isEsimSupportedCountry(currentCountry, supportedCountries);
} }
Log.d(TAG, "inEsimSupportedCountries=" + inEsimSupportedCountries);
final boolean esimIgnoredDevice = final boolean esimIgnoredDevice =
Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ",")) Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ","))
.contains(SystemProperties.get(KEY_CID, null)); .contains(SystemProperties.get(KEY_CID, null));
@@ -613,4 +620,24 @@ public class MobileNetworkUtils {
} }
return tm.getNetworkOperatorName(); return tm.getNetworkOperatorName();
} }
private static boolean isEsimSupportedCountry(String country, String countriesListString) {
if (TextUtils.isEmpty(country)) {
return true;
} else if (TextUtils.isEmpty(countriesListString)) {
return false;
}
final List<String> supportedCountries =
Arrays.asList(TextUtils.split(countriesListString.toLowerCase(), ","));
return supportedCountries.contains(country);
}
private static boolean isEsimUnsupportedCountry(String country, String countriesListString) {
if (TextUtils.isEmpty(country) || TextUtils.isEmpty(countriesListString)) {
return false;
}
final List<String> unsupportedCountries =
Arrays.asList(TextUtils.split(countriesListString.toLowerCase(), ","));
return unsupportedCountries.contains(country);
}
} }

View File

@@ -26,6 +26,7 @@ import com.android.settings.R;
import com.android.settings.accounts.AccountFeatureProvider; import com.android.settings.accounts.AccountFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.aware.AwareFeatureProvider; import com.android.settings.aware.AwareFeatureProvider;
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
@@ -133,6 +134,8 @@ public abstract class FeatureFactory {
public abstract AwareFeatureProvider getAwareFeatureProvider(); public abstract AwareFeatureProvider getAwareFeatureProvider();
public abstract FaceFeatureProvider getFaceFeatureProvider();
public static final class FactoryNotFoundException extends RuntimeException { public static final class FactoryNotFoundException extends RuntimeException {
public FactoryNotFoundException(Throwable throwable) { public FactoryNotFoundException(Throwable throwable) {
super("Unable to create factory. Did you misconfigure Proguard?", throwable); super("Unable to create factory. Did you misconfigure Proguard?", throwable);

View File

@@ -30,6 +30,8 @@ import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProviderImpl; import com.android.settings.applications.ApplicationFeatureProviderImpl;
import com.android.settings.aware.AwareFeatureProvider; import com.android.settings.aware.AwareFeatureProvider;
import com.android.settings.aware.AwareFeatureProviderImpl; import com.android.settings.aware.AwareFeatureProviderImpl;
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.biometrics.face.FaceFeatureProviderImpl;
import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider;
import com.android.settings.bluetooth.BluetoothFeatureProviderImpl; import com.android.settings.bluetooth.BluetoothFeatureProviderImpl;
import com.android.settings.connecteddevice.dock.DockUpdaterFeatureProviderImpl; import com.android.settings.connecteddevice.dock.DockUpdaterFeatureProviderImpl;
@@ -84,6 +86,7 @@ public class FeatureFactoryImpl extends FeatureFactory {
private ContextualCardFeatureProvider mContextualCardFeatureProvider; private ContextualCardFeatureProvider mContextualCardFeatureProvider;
private BluetoothFeatureProvider mBluetoothFeatureProvider; private BluetoothFeatureProvider mBluetoothFeatureProvider;
private AwareFeatureProvider mAwareFeatureProvider; private AwareFeatureProvider mAwareFeatureProvider;
private FaceFeatureProvider mFaceFeatureProvider;
@Override @Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) { public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -255,4 +258,12 @@ public class FeatureFactoryImpl extends FeatureFactory {
} }
return mAwareFeatureProvider; return mAwareFeatureProvider;
} }
@Override
public FaceFeatureProvider getFaceFeatureProvider() {
if (mFaceFeatureProvider == null) {
mFaceFeatureProvider = new FaceFeatureProviderImpl();
}
return mFaceFeatureProvider;
}
} }

View File

@@ -32,6 +32,7 @@ import androidx.fragment.app.FragmentManager;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.HideNonSystemOverlayMixin;
/** /**
* Dialog Activity to host Settings Slices. * Dialog Activity to host Settings Slices.
@@ -62,6 +63,7 @@ public class SettingsPanelActivity extends FragmentActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
createOrUpdatePanel(true /* shouldForceCreation */); createOrUpdatePanel(true /* shouldForceCreation */);
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
} }
@Override @Override

View File

@@ -1,243 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.privacy;
import static android.Manifest.permission_group.CAMERA;
import static android.Manifest.permission_group.LOCATION;
import static android.Manifest.permission_group.MICROPHONE;
import static java.util.concurrent.TimeUnit.DAYS;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.permission.PermissionControllerManager;
import android.permission.RuntimePermissionUsageInfo;
import android.provider.DeviceConfig;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.Utils;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.widget.BarChartInfo;
import com.android.settingslib.widget.BarChartPreference;
import com.android.settingslib.widget.BarViewInfo;
import java.util.ArrayList;
import java.util.List;
public class PermissionBarChartPreferenceController extends BasePreferenceController implements
PermissionControllerManager.OnPermissionUsageResultCallback, LifecycleObserver, OnCreate,
OnStart, OnSaveInstanceState {
private static final String TAG = "BarChartPreferenceCtl";
private static final String KEY_PERMISSION_USAGE = "usage_infos";
@VisibleForTesting
List<RuntimePermissionUsageInfo> mOldUsageInfos;
private PackageManager mPackageManager;
private PrivacyDashboardFragment mParent;
private BarChartPreference mBarChartPreference;
public PermissionBarChartPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mOldUsageInfos = new ArrayList<>();
mPackageManager = context.getPackageManager();
}
public void setFragment(PrivacyDashboardFragment fragment) {
mParent = fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mOldUsageInfos = savedInstanceState.getParcelableArrayList(KEY_PERMISSION_USAGE);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelableList(KEY_PERMISSION_USAGE, mOldUsageInfos);
}
@Override
public int getAvailabilityStatus() {
return UNSUPPORTED_ON_DEVICE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mBarChartPreference = screen.findPreference(getPreferenceKey());
final BarChartInfo info = new BarChartInfo.Builder()
.setTitle(R.string.permission_bar_chart_title)
.setDetails(R.string.permission_bar_chart_details)
.setEmptyText(R.string.permission_bar_chart_empty_text)
.setDetailsOnClickListener((View v) -> {
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
mContext.startActivity(intent);
})
.build();
mBarChartPreference.initializeBarChart(info);
if (!mOldUsageInfos.isEmpty()) {
mBarChartPreference.setBarViewInfos(createBarViews(mOldUsageInfos));
}
}
@Override
public void onStart() {
if (!isAvailable()) {
return;
}
// We don't hide chart when we have existing data.
mBarChartPreference.updateLoadingState(mOldUsageInfos.isEmpty() /* isLoading */);
// But we still need to hint user with progress bar that we are updating new usage data.
mParent.setLoadingEnabled(true /* enabled */);
retrievePermissionUsageData();
}
@Override
public void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> usageInfos) {
usageInfos.sort((x, y) -> {
int usageDiff = y.getAppAccessCount() - x.getAppAccessCount();
if (usageDiff != 0) {
return usageDiff;
}
String xName = x.getName();
String yName = y.getName();
if (xName.equals(LOCATION)) {
return -1;
} else if (yName.equals(LOCATION)) {
return 1;
} else if (xName.equals(MICROPHONE)) {
return -1;
} else if (yName.equals(MICROPHONE)) {
return 1;
} else if (xName.equals(CAMERA)) {
return -1;
} else if (yName.equals(CAMERA)) {
return 1;
}
return x.getName().compareTo(y.getName());
});
// If the result is different, we need to update bar views.
if (!areSamePermissionGroups(usageInfos)) {
mBarChartPreference.setBarViewInfos(createBarViews(usageInfos));
mOldUsageInfos = usageInfos;
}
mBarChartPreference.updateLoadingState(false /* isLoading */);
mParent.setLoadingEnabled(false /* enabled */);
}
private void retrievePermissionUsageData() {
mContext.getSystemService(PermissionControllerManager.class).getPermissionUsages(
false /* countSystem */, (int) DAYS.toMillis(1),
mContext.getMainExecutor() /* executor */, this /* callback */);
}
private BarViewInfo[] createBarViews(List<RuntimePermissionUsageInfo> usageInfos) {
if (usageInfos.isEmpty()) {
return null;
}
final BarViewInfo[] barViewInfos = new BarViewInfo[
Math.min(BarChartPreference.MAXIMUM_BAR_VIEWS, usageInfos.size())];
for (int index = 0; index < barViewInfos.length; index++) {
final RuntimePermissionUsageInfo permissionGroupInfo = usageInfos.get(index);
final int count = permissionGroupInfo.getAppAccessCount();
final CharSequence permLabel = getPermissionGroupLabel(permissionGroupInfo.getName());
barViewInfos[index] = new BarViewInfo(
getPermissionGroupIcon(permissionGroupInfo.getName()), count, permLabel,
mContext.getResources().getQuantityString(R.plurals.permission_bar_chart_label,
count, count), permLabel);
// Set the click listener for each bar view.
// The listener will navigate user to permission usage app.
barViewInfos[index].setClickListener((View v) -> {
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permissionGroupInfo.getName());
intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
mContext.startActivity(intent);
});
}
return barViewInfos;
}
private Drawable getPermissionGroupIcon(String permissionGroup) {
Drawable icon = null;
try {
icon = mPackageManager.getPermissionGroupInfo(permissionGroup, 0)
.loadIcon(mPackageManager);
icon.setTintList(Utils.getColorAttr(mContext, android.R.attr.textColorSecondary));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Cannot find group icon for " + permissionGroup, e);
}
return icon;
}
private CharSequence getPermissionGroupLabel(String permissionGroup) {
CharSequence label = null;
try {
label = mPackageManager.getPermissionGroupInfo(permissionGroup, 0)
.loadLabel(mPackageManager);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Cannot find group label for " + permissionGroup, e);
}
return label;
}
private boolean areSamePermissionGroups(List<RuntimePermissionUsageInfo> newUsageInfos) {
if (newUsageInfos.size() != mOldUsageInfos.size()) {
return false;
}
for (int index = 0; index < newUsageInfos.size(); index++) {
final RuntimePermissionUsageInfo newInfo = newUsageInfos.get(index);
final RuntimePermissionUsageInfo oldInfo = mOldUsageInfos.get(index);
if (!newInfo.getName().equals(oldInfo.getName()) ||
newInfo.getAppAccessCount() != oldInfo.getAppAccessCount()) {
return false;
}
}
return true;
}
}

View File

@@ -18,14 +18,12 @@ package com.android.settings.privacy;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.os.Bundle;
import android.provider.SearchIndexableResource; import android.provider.SearchIndexableResource;
import android.view.View; import android.view.View;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.LockScreenNotificationPreferenceController; import com.android.settings.notification.LockScreenNotificationPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
@@ -75,34 +73,6 @@ public class PrivacyDashboardFragment extends DashboardFragment {
return buildPreferenceControllers(context, getSettingsLifecycle()); return buildPreferenceControllers(context, getSettingsLifecycle());
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(PermissionBarChartPreferenceController.class).setFragment(this /* fragment */);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Utils.setActionBarShadowAnimation(getActivity(), getSettingsLifecycle(), getListView());
initLoadingBar();
}
@VisibleForTesting
void initLoadingBar() {
mProgressHeader = setPinnedHeaderView(R.layout.progress_header);
mProgressAnimation = mProgressHeader.findViewById(R.id.progress_bar_animation);
setLoadingEnabled(false);
}
@VisibleForTesting
void setLoadingEnabled(boolean enabled) {
if (mProgressHeader != null && mProgressAnimation != null) {
mProgressHeader.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
mProgressAnimation.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
}
}
private static List<AbstractPreferenceController> buildPreferenceControllers( private static List<AbstractPreferenceController> buildPreferenceControllers(
Context context, Lifecycle lifecycle) { Context context, Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>(); final List<AbstractPreferenceController> controllers = new ArrayList<>();

View File

@@ -37,6 +37,7 @@ import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
import com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice; import com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice;
import com.android.settings.homepage.contextualcards.slices.ContextualAdaptiveSleepSlice; import com.android.settings.homepage.contextualcards.slices.ContextualAdaptiveSleepSlice;
import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice; import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice;
import com.android.settings.homepage.contextualcards.slices.DarkThemeSlice;
import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice; import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice;
import com.android.settings.homepage.contextualcards.slices.LowStorageSlice; import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice; import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice;
@@ -342,6 +343,16 @@ public class CustomSliceRegistry {
.appendPath("media_output_indicator") .appendPath("media_output_indicator")
.build(); .build();
/**
* Backing Uri for the Dark theme Slice.
*/
public static final Uri DARK_THEME_SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("dark_theme")
.build();
@VisibleForTesting @VisibleForTesting
static final Map<Uri, Class<? extends CustomSliceable>> sUriToSlice; static final Map<Uri, Class<? extends CustomSliceable>> sUriToSlice;
@@ -367,6 +378,7 @@ public class CustomSliceRegistry {
sUriToSlice.put(NOTIFICATION_CHANNEL_SLICE_URI, NotificationChannelSlice.class); sUriToSlice.put(NOTIFICATION_CHANNEL_SLICE_URI, NotificationChannelSlice.class);
sUriToSlice.put(STORAGE_SLICE_URI, StorageSlice.class); sUriToSlice.put(STORAGE_SLICE_URI, StorageSlice.class);
sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class); sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
sUriToSlice.put(DARK_THEME_SLICE_URI, DarkThemeSlice.class);
} }
public static Class<? extends CustomSliceable> getSliceClassByUri(Uri uri) { public static Class<? extends CustomSliceable> getSliceClassByUri(Uri uri) {

View File

@@ -95,7 +95,7 @@ public interface CustomSliceable extends Sliceable {
.setData(getUri()) .setData(getUri())
.setClass(context, SliceBroadcastReceiver.class); .setClass(context, SliceBroadcastReceiver.class);
return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent,
PendingIntent.FLAG_CANCEL_CURRENT); PendingIntent.FLAG_UPDATE_CURRENT);
} }
@Override @Override

View File

@@ -175,7 +175,7 @@ public class SliceBuilderUtils {
.putExtra(EXTRA_SLICE_KEY, data.getKey()) .putExtra(EXTRA_SLICE_KEY, data.getKey())
.putExtra(EXTRA_SLICE_PLATFORM_DEFINED, data.isPlatformDefined()); .putExtra(EXTRA_SLICE_PLATFORM_DEFINED, data.isPlatformDefined());
return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent,
PendingIntent.FLAG_CANCEL_CURRENT); PendingIntent.FLAG_UPDATE_CURRENT);
} }
/** /**

View File

@@ -310,7 +310,7 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS); mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS);
if (mFilterWifiTracker == null) { if (mFilterWifiTracker == null) {
mFilterWifiTracker = new FilterWifiTracker(getActivity(), getSettingsLifecycle()); mFilterWifiTracker = new FilterWifiTracker(getContext(), getSettingsLifecycle());
} }
mFilterWifiTracker.onResume(); mFilterWifiTracker.onResume();
} }
@@ -476,11 +476,13 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
private final class FilterWifiTracker { private final class FilterWifiTracker {
private final List<String> mAccessPointKeys; private final List<String> mAccessPointKeys;
private final WifiTracker mWifiTracker; private final WifiTracker mWifiTracker;
private final Context mContext;
public FilterWifiTracker(Context context, Lifecycle lifecycle) { public FilterWifiTracker(Context context, Lifecycle lifecycle) {
mWifiTracker = WifiTrackerFactory.create(context, mWifiListener, mWifiTracker = WifiTrackerFactory.create(context, mWifiListener,
lifecycle, /* includeSaved */ true, /* includeScans */ true); lifecycle, /* includeSaved */ true, /* includeScans */ true);
mAccessPointKeys = new ArrayList<>(); mAccessPointKeys = new ArrayList<>();
mContext = context;
} }
/** /**
@@ -489,7 +491,7 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
*/ */
public void updateKeys(List<ScanResult> scanResults) { public void updateKeys(List<ScanResult> scanResults) {
for (ScanResult scanResult : scanResults) { for (ScanResult scanResult : scanResults) {
final String key = AccessPoint.getKey(scanResult); final String key = AccessPoint.getKey(mContext, scanResult);
if (!mAccessPointKeys.contains(key)) { if (!mAccessPointKeys.contains(key)) {
mAccessPointKeys.add(key); mAccessPointKeys.add(key);
} }

View File

@@ -583,35 +583,6 @@ public class WifiConfigController implements TextWatcher,
} }
} }
/**
* Special handling for WPA2/WPA3 and OWE in Transition mode: The key
* SECURITY_PSK_SAE_TRANSITION and SECURITY_OWE_TRANSITION are pseudo keys which result by the
* scan results, but never appears in the saved networks.
* A saved network is either WPA3 for supporting devices or WPA2 for non-supporting devices,
* or, OWE for supporting devices or Open for non-supporting devices.
*
* @param accessPointSecurity Access point current security type
* @return Converted security type (if required)
*/
private int convertSecurityTypeForMatching(int accessPointSecurity) {
if (accessPointSecurity == AccessPoint.SECURITY_PSK_SAE_TRANSITION) {
if (mWifiManager.isWpa3SaeSupported()) {
return AccessPoint.SECURITY_SAE;
} else {
return AccessPoint.SECURITY_PSK;
}
}
if (accessPointSecurity == AccessPoint.SECURITY_OWE_TRANSITION) {
if (mWifiManager.isEnhancedOpenSupported()) {
return AccessPoint.SECURITY_OWE;
} else {
return AccessPoint.SECURITY_NONE;
}
}
return accessPointSecurity;
}
public WifiConfiguration getConfig() { public WifiConfiguration getConfig() {
if (mMode == WifiConfigUiBase.MODE_VIEW) { if (mMode == WifiConfigUiBase.MODE_VIEW) {
return null; return null;
@@ -634,8 +605,6 @@ public class WifiConfigController implements TextWatcher,
config.shared = mSharedCheckBox.isChecked(); config.shared = mSharedCheckBox.isChecked();
mAccessPointSecurity = convertSecurityTypeForMatching(mAccessPointSecurity);
switch (mAccessPointSecurity) { switch (mAccessPointSecurity) {
case AccessPoint.SECURITY_NONE: case AccessPoint.SECURITY_NONE:
config.allowedKeyManagement.set(KeyMgmt.NONE); config.allowedKeyManagement.set(KeyMgmt.NONE);
@@ -960,8 +929,7 @@ public class WifiConfigController implements TextWatcher,
private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) { private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) {
if (mAccessPointSecurity == AccessPoint.SECURITY_NONE || if (mAccessPointSecurity == AccessPoint.SECURITY_NONE ||
mAccessPointSecurity == AccessPoint.SECURITY_OWE || mAccessPointSecurity == AccessPoint.SECURITY_OWE) {
mAccessPointSecurity == AccessPoint.SECURITY_OWE_TRANSITION) {
mView.findViewById(R.id.security_fields).setVisibility(View.GONE); mView.findViewById(R.id.security_fields).setVisibility(View.GONE);
return; return;
} }

View File

@@ -494,9 +494,7 @@ public class WifiSettings extends RestrictedSettingsFragment
if (isSavedNetwork) { if (isSavedNetwork) {
connect(mSelectedAccessPoint.getConfig(), isSavedNetwork); connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
} else if ((mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) || } else if ((mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) ||
(mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_OWE) || (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_OWE)) {
(mSelectedAccessPoint.getSecurity()
== AccessPoint.SECURITY_OWE_TRANSITION)) {
/** Bypass dialog for unsecured networks */ /** Bypass dialog for unsecured networks */
mSelectedAccessPoint.generateOpenNetworkConfig(); mSelectedAccessPoint.generateOpenNetworkConfig();
connect(mSelectedAccessPoint.getConfig(), isSavedNetwork); connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
@@ -750,8 +748,7 @@ public class WifiSettings extends RestrictedSettingsFragment
preference.setOrder(index); preference.setOrder(index);
if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr()) if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
&& (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE && && (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE &&
accessPoint.getSecurity() != AccessPoint.SECURITY_OWE && accessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) {
accessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) { if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) {
onPreferenceTreeClick(preference); onPreferenceTreeClick(preference);
mOpenSsid = null; mOpenSsid = null;

View File

@@ -268,8 +268,7 @@ public class WifiUtils {
if (accessPoint.isOsuProvider()) { if (accessPoint.isOsuProvider()) {
return CONNECT_TYPE_OSU_PROVISION; return CONNECT_TYPE_OSU_PROVISION;
} else if ((accessPoint.getSecurity() == AccessPoint.SECURITY_NONE) || } else if ((accessPoint.getSecurity() == AccessPoint.SECURITY_NONE) ||
(accessPoint.getSecurity() == AccessPoint.SECURITY_OWE) || (accessPoint.getSecurity() == AccessPoint.SECURITY_OWE)) {
(accessPoint.getSecurity() == AccessPoint.SECURITY_OWE_TRANSITION)) {
return CONNECT_TYPE_OPEN_NETWORK; return CONNECT_TYPE_OPEN_NETWORK;
} else if (accessPoint.isSaved() && config != null } else if (accessPoint.isSaved() && config != null
&& config.getNetworkSelectionStatus() != null && config.getNetworkSelectionStatus() != null

View File

@@ -206,7 +206,7 @@ public class WifiDppUtils {
setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration); setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
// For a transition mode Wi-Fi AP, creates a QR code that's compatible with more devices // For a transition mode Wi-Fi AP, creates a QR code that's compatible with more devices
if (accessPoint.getSecurity() == AccessPoint.SECURITY_PSK_SAE_TRANSITION) { if (accessPoint.isPskSaeTransitionMode()) {
intent.putExtra(EXTRA_WIFI_SECURITY, WifiQrCode.SECURITY_WPA_PSK); intent.putExtra(EXTRA_WIFI_SECURITY, WifiQrCode.SECURITY_WPA_PSK);
} }
@@ -406,7 +406,6 @@ public class WifiDppUtils {
} }
break; break;
case AccessPoint.SECURITY_PSK: case AccessPoint.SECURITY_PSK:
case AccessPoint.SECURITY_PSK_SAE_TRANSITION:
return true; return true;
default: default:
} }
@@ -419,8 +418,6 @@ public class WifiDppUtils {
case AccessPoint.SECURITY_PSK: case AccessPoint.SECURITY_PSK:
case AccessPoint.SECURITY_WEP: case AccessPoint.SECURITY_WEP:
case AccessPoint.SECURITY_NONE: case AccessPoint.SECURITY_NONE:
case AccessPoint.SECURITY_PSK_SAE_TRANSITION:
case AccessPoint.SECURITY_OWE_TRANSITION:
return true; return true;
case AccessPoint.SECURITY_SAE: case AccessPoint.SECURITY_SAE:
if (wifiManager.isWpa3SaeSupported()) { if (wifiManager.isWpa3SaeSupported()) {

View File

@@ -16,25 +16,9 @@
package com.android.settings.display; package com.android.settings.display;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.PowerManager;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceScreen; import com.android.settings.display.darkmode.DarkModePreference;
import androidx.preference.SwitchPreference;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -43,6 +27,10 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class DarkUIPreferenceControllerTest { public class DarkUIPreferenceControllerTest {
@@ -57,7 +45,7 @@ public class DarkUIPreferenceControllerTest {
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mController = spy(new DarkUIPreferenceController(mContext, "dark_ui_mode")); mController = spy(new DarkUIPreferenceController(mContext, "dark_ui_mode"));
mController.setParentFragment(mFragment); mController.setParentFragment(mFragment);
mController.mPreference = new SwitchPreference(mContext); mController.mPreference = new DarkModePreference(mContext, null /* AttributeSet attrs */);
mController.onStart(); mController.onStart();
} }

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.app.UiModeManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.PowerManager;
import android.view.View;
import android.widget.Button;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.widget.LayoutPreference;
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 static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.eq;
@RunWith(RobolectricTestRunner.class)
public class DarkModeActivationPreferenceControllerTest {
private DarkModeActivationPreferenceController mController;
private String mPreferenceKey = "key";
@Mock
private LayoutPreference mPreference;
@Mock
private PreferenceScreen mScreen;
@Mock
private Resources res;
@Mock
private Context mContext;
@Mock
private UiModeManager mService;
@Mock
private Button mTurnOffButton;
@Mock
private Button mTurnOnButton;
@Mock
private PowerManager mPM;
private Configuration configNightYes = new Configuration();
private Configuration configNightNo = new Configuration();;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mService = mock(UiModeManager.class);
when(mContext.getResources()).thenReturn(res);
when(mContext.getSystemService(UiModeManager.class)).thenReturn(mService);
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPM);
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
when(mPreference.findViewById(
eq(R.id.dark_ui_turn_on_button))).thenReturn(mTurnOnButton);
when(mPreference.findViewById(
eq(R.id.dark_ui_turn_off_button))).thenReturn(mTurnOffButton);
when(mService.setNightModeActivated(anyBoolean())).thenReturn(true);
when(mContext.getString(
R.string.dark_ui_activation_off_auto)).thenReturn("off_auto");
when(mContext.getString(
R.string.dark_ui_activation_on_auto)).thenReturn("on_auto");
when(mContext.getString(
R.string.dark_ui_activation_off_manual)).thenReturn("off_manual");
when(mContext.getString(
R.string.dark_ui_activation_on_manual)).thenReturn("on_manual");
when(mContext.getString(
R.string.dark_ui_summary_off_auto_mode_auto)).thenReturn("summary_off_auto");
when(mContext.getString(
R.string.dark_ui_summary_on_auto_mode_auto)).thenReturn("summary_on_auto");
when(mContext.getString(
R.string.dark_ui_summary_off_auto_mode_never)).thenReturn("summary_off_manual");
when(mContext.getString(
R.string.dark_ui_summary_on_auto_mode_never)).thenReturn("summary_on_manual");
mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey);
mController.displayPreference(mScreen);
configNightNo.uiMode = Configuration.UI_MODE_NIGHT_NO;
configNightYes.uiMode = Configuration.UI_MODE_NIGHT_YES;
}
@Test
public void nightMode_toggleButton_offManual() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
when(res.getConfiguration()).thenReturn(configNightYes);
mController.updateState(mPreference);
verify(mTurnOnButton).setVisibility(eq(View.GONE));
verify(mTurnOffButton).setVisibility(eq(View.VISIBLE));
verify(mTurnOffButton).setText(eq(mContext.getString(
R.string.dark_ui_activation_off_manual)));
}
@Test
public void nightMode_toggleButton_onAutoWhenModeIsYes() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
when(res.getConfiguration()).thenReturn(configNightNo);
mController.updateState(mPreference);
verify(mTurnOffButton).setVisibility(eq(View.GONE));
verify(mTurnOnButton).setVisibility(eq(View.VISIBLE));
verify(mTurnOnButton).setText(eq(mContext.getString(
R.string.dark_ui_activation_on_manual)));
}
@Test
public void nightMode_toggleButton_onAutoWhenModeIsAuto() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
when(res.getConfiguration()).thenReturn(configNightNo);
mController.updateState(mPreference);
verify(mTurnOffButton).setVisibility(eq(View.GONE));
verify(mTurnOnButton).setVisibility(eq(View.VISIBLE));
verify(mTurnOnButton).setText(eq(mContext.getString(
R.string.dark_ui_activation_on_auto)));
}
@Test
public void nightModeSummary_buttonText_onManual() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_NO);
when(res.getConfiguration()).thenReturn(configNightYes);
assertEquals(mController.getSummary(), mContext.getString(
R.string.dark_ui_summary_on_auto_mode_never));
}
@Test
public void nightModeSummary_buttonText_offAuto() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
when(res.getConfiguration()).thenReturn(configNightNo);
assertEquals(mController.getSummary(), mContext.getString(
R.string.dark_ui_summary_off_auto_mode_auto));
}
@Test
public void buttonVisisbility_hideButton_offWhenInPowerSaveMode() {
when(mPM.isPowerSaveMode()).thenReturn(true);
mController.updateState(mPreference);
verify(mTurnOffButton).setVisibility(eq(View.GONE));
verify(mTurnOnButton).setVisibility(eq(View.GONE));
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.provider.Settings;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@RunWith(RobolectricTestRunner.class)
public class DarkModeObserverTest {
private Context mContext;
private ContentObserver mContentObserver;
private DarkModeObserver mDarkModeObserver;
private Runnable mCallback;
private Uri mUri = Settings.Secure.getUriFor(Settings.Secure.UI_NIGHT_MODE);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mDarkModeObserver = new DarkModeObserver(mContext);
mContentObserver = mDarkModeObserver.getContentObserver();
mCallback = mock(Runnable.class);
}
@Test
public void callbackTest_subscribedCallbackCalled() {
mDarkModeObserver.subscribe(mCallback);
mContentObserver.onChange(false, mUri);
Mockito.verify(mCallback, times(2)).run();
}
@Test
public void callbackTest_unsubscribedCallNotbackCalled() {
mDarkModeObserver.subscribe(mCallback);
mContentObserver.onChange(false, mUri);
mDarkModeObserver.unsubscribe();
mContentObserver.onChange(false, mUri);
Mockito.verify(mCallback, times(2)).run();
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.display.darkmode;
import android.app.UiModeManager;
import android.content.Context;
import android.os.PowerManager;
import androidx.preference.DropDownPreference;
import androidx.preference.PreferenceScreen;
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 com.android.settings.R;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
public class DarkModeScheduleSelectorControllerTest {
private DarkModeScheduleSelectorController mController;
private String mPreferenceKey = "key";
@Mock
private DropDownPreference mPreference;
@Mock
private PreferenceScreen mScreen;
private Context mContext;
@Mock
private UiModeManager mUiService;
@Mock
private PowerManager mPM;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(UiModeManager.class)).thenReturn(mUiService);
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPM);
when(mContext.getString(R.string.dark_ui_auto_mode_never)).thenReturn("never");
when(mContext.getString(R.string.dark_ui_auto_mode_auto)).thenReturn("auto");
mPreference = spy(new DropDownPreference(mContext));
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
when(mUiService.setNightModeActivated(anyBoolean())).thenReturn(true);
mController = new DarkModeScheduleSelectorController(mContext, mPreferenceKey);
}
@Test
public void nightMode_preferenceChange_preferenceChangeTrueWhenChangedOnly() {
when(mUiService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
mController.displayPreference(mScreen);
boolean changed = mController
.onPreferenceChange(mScreen, mContext.getString(R.string.dark_ui_auto_mode_auto));
assertTrue(changed);
changed = mController
.onPreferenceChange(mScreen, mContext.getString(R.string.dark_ui_auto_mode_auto));
assertFalse(changed);
}
@Test
public void nightMode_updateStateNone_dropDownValueChangedToNone() {
when(mUiService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
mController.displayPreference(mScreen);
mController.updateState(mPreference);
verify(mPreference).setValue(mContext.getString(R.string.dark_ui_auto_mode_never));
}
@Test
public void nightMode_updateStateNone_dropDownValueChangedToAuto() {
when(mUiService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
mController.displayPreference(mScreen);
mController.updateState(mPreference);
verify(mPreference).setValue(mContext.getString(R.string.dark_ui_auto_mode_auto));
}
@Test
public void batterySaver_dropDown_disabledSelector() {
when(mPM.isPowerSaveMode()).thenReturn(true);
mController.displayPreference(mScreen);
mController.updateState(mPreference);
verify(mPreference).setEnabled(eq(false));
}
}

View File

@@ -85,7 +85,6 @@ public class BatteryHeaderPreferenceControllerTest {
private BatteryMeterView mBatteryMeterView; private BatteryMeterView mBatteryMeterView;
private TextView mBatteryPercentText; private TextView mBatteryPercentText;
private TextView mSummary; private TextView mSummary;
private TextView mSummary2;
private LayoutPreference mBatteryLayoutPref; private LayoutPreference mBatteryLayoutPref;
private Intent mBatteryIntent; private Intent mBatteryIntent;
private LifecycleOwner mLifecycleOwner; private LifecycleOwner mLifecycleOwner;
@@ -102,7 +101,6 @@ public class BatteryHeaderPreferenceControllerTest {
mBatteryPercentText = new TextView(mContext); mBatteryPercentText = new TextView(mContext);
mSummary = new TextView(mContext); mSummary = new TextView(mContext);
ShadowEntityHeaderController.setUseMock(mEntityHeaderController); ShadowEntityHeaderController.setUseMock(mEntityHeaderController);
mSummary2 = new TextView(mContext);
mBatteryIntent = new Intent(); mBatteryIntent = new Intent();
mBatteryIntent.putExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL); mBatteryIntent.putExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL);
@@ -126,7 +124,6 @@ public class BatteryHeaderPreferenceControllerTest {
mController.mBatteryMeterView = mBatteryMeterView; mController.mBatteryMeterView = mBatteryMeterView;
mController.mBatteryPercentText = mBatteryPercentText; mController.mBatteryPercentText = mBatteryPercentText;
mController.mSummary1 = mSummary; mController.mSummary1 = mSummary;
mController.mSummary2 = mSummary2;
} }
@After @After
@@ -190,7 +187,6 @@ public class BatteryHeaderPreferenceControllerTest {
@Test @Test
public void quickUpdateHeaderPreference_onlyUpdateBatteryLevelAndChargingState() { public void quickUpdateHeaderPreference_onlyUpdateBatteryLevelAndChargingState() {
mSummary.setText(BATTERY_STATUS); mSummary.setText(BATTERY_STATUS);
mSummary2.setText(BATTERY_STATUS);
mController.quickUpdateHeaderPreference(); mController.quickUpdateHeaderPreference();
@@ -198,7 +194,6 @@ public class BatteryHeaderPreferenceControllerTest {
assertThat(mBatteryMeterView.getCharging()).isTrue(); assertThat(mBatteryMeterView.getCharging()).isTrue();
assertThat(mBatteryPercentText.getText().toString()).isEqualTo("60 %"); assertThat(mBatteryPercentText.getText().toString()).isEqualTo("60 %");
assertThat(mSummary.getText()).isEqualTo(BATTERY_STATUS); assertThat(mSummary.getText()).isEqualTo(BATTERY_STATUS);
assertThat(mSummary2.getText()).isEqualTo(BATTERY_STATUS);
} }
@Test @Test

View File

@@ -265,20 +265,17 @@ public class PowerUsageSummaryTest {
} }
}).when(mFeatureFactory.powerUsageFeatureProvider).getEnhancedEstimateDebugString(any()); }).when(mFeatureFactory.powerUsageFeatureProvider).getEnhancedEstimateDebugString(any());
doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary2);
doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary1); doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary1);
mFragment.onLongClick(new View(mRealContext)); mFragment.onLongClick(new View(mRealContext));
TextView summary1 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary1); TextView summary1 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary1);
TextView summary2 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary2);
Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushBackgroundThreadScheduler();
assertThat(summary2.getText().toString()).contains(NEW_ML_EST_SUFFIX); assertThat(summary1.getText().toString()).contains(NEW_ML_EST_SUFFIX);
assertThat(summary1.getText().toString()).contains(OLD_EST_SUFFIX); assertThat(summary1.getText().toString()).contains(OLD_EST_SUFFIX);
} }
@Test @Test
public void debugMode() { public void debugMode() {
doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isEstimateDebugEnabled(); doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isEstimateDebugEnabled();
doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary2);
mFragment.restartBatteryInfoLoader(); mFragment.restartBatteryInfoLoader();
ArgumentCaptor<View.OnLongClickListener> listener = ArgumentCaptor.forClass( ArgumentCaptor<View.OnLongClickListener> listener = ArgumentCaptor.forClass(

View File

@@ -16,20 +16,42 @@
package com.android.settings.homepage; package com.android.settings.homepage;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Build;
import android.view.View; import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.HideNonSystemOverlayMixin;
import com.android.settings.homepage.contextualcards.slices.BatteryFixSliceTest;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric; import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class SettingsHomepageActivityTest { public class SettingsHomepageActivityTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test @Test
public void setHomepageContainerPaddingTop_shouldBeSetPaddingTop() { public void setHomepageContainerPaddingTop_shouldBeSetPaddingTop() {
@@ -55,4 +77,55 @@ public class SettingsHomepageActivityTest {
assertThat(frameLayout.getLayoutTransition()).isNotNull(); assertThat(frameLayout.getLayoutTransition()).isNotNull();
} }
@Test
@Config(shadows = {
BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void onStart_isNotDebuggable_shouldHideSystemOverlay() {
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false);
final ActivityController<SettingsHomepageActivity> activityController =
Robolectric.buildActivity(SettingsHomepageActivity.class).create();
final SettingsHomepageActivity activity = spy(activityController.get());
final Window window = mock(Window.class);
when(activity.getWindow()).thenReturn(window);
activity.getLifecycle().addObserver(new HideNonSystemOverlayMixin(activity));
activityController.start();
verify(window).addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
}
@Test
@Config(shadows = {
BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class,
})
public void onStop_isNotDebuggable_shouldRemoveHideSystemOverlay() {
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false);
final ActivityController<SettingsHomepageActivity> activityController =
Robolectric.buildActivity(SettingsHomepageActivity.class).create();
final SettingsHomepageActivity activity = spy(activityController.get());
final Window window = mock(Window.class);
when(activity.getWindow()).thenReturn(window);
activity.getLifecycle().addObserver(new HideNonSystemOverlayMixin(activity));
activityController.start();
verify(window).addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
when(window.getAttributes()).thenReturn(layoutParams);
activityController.stop();
final ArgumentCaptor<WindowManager.LayoutParams> paramCaptor = ArgumentCaptor.forClass(
WindowManager.LayoutParams.class);
verify(window).setAttributes(paramCaptor.capture());
assertThat(paramCaptor.getValue().privateFlags
& SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(0);
}
} }

View File

@@ -0,0 +1,171 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.homepage.contextualcards.slices;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.PowerManager;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
import androidx.slice.SliceProvider;
import androidx.slice.widget.SliceLiveData;
import com.android.settings.R;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SlicesFeatureProviderImpl;
import com.android.settings.testutils.FakeFeatureFactory;
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;
@RunWith(RobolectricTestRunner.class)
public class DarkThemeSliceTest {
@Mock
private BatteryManager mBatteryManager;
@Mock
private PowerManager mPowerManager;
private Context mContext;
private DarkThemeSlice mDarkThemeSlice;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mFeatureFactory.slicesFeatureProvider = new SlicesFeatureProviderImpl();
mFeatureFactory.slicesFeatureProvider.newUiSession();
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
when(mPowerManager.isPowerSaveMode()).thenReturn(false);
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
mDarkThemeSlice = spy(new DarkThemeSlice(mContext));
mDarkThemeSlice.sKeepSliceShow = false;
}
@Test
public void getUri_shouldBeDarkThemeSliceUri() {
final Uri uri = mDarkThemeSlice.getUri();
assertThat(uri).isEqualTo(CustomSliceRegistry.DARK_THEME_SLICE_URI);
}
@Test
public void isAvailable_inDarkThemeMode_returnFalse() {
doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext);
assertThat(mDarkThemeSlice.isAvailable(mContext)).isFalse();
}
@Test
public void isAvailable_nonDarkThemeBatteryCapacityEq100_returnFalse() {
setBatteryCapacityLevel(100);
assertThat(mDarkThemeSlice.isAvailable(mContext)).isFalse();
}
@Test
public void isAvailable_nonDarkThemeBatteryCapacityLt50_returnTrue() {
setBatteryCapacityLevel(40);
assertThat(mDarkThemeSlice.isAvailable(mContext)).isTrue();
}
@Test
public void getSlice_batterySaver_returnErrorSlice() {
when(mPowerManager.isPowerSaveMode()).thenReturn(true);
final Slice mediaSlice = mDarkThemeSlice.getSlice();
final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
assertThat(metadata.isErrorSlice()).isTrue();
}
@Test
public void getSlice_notAvailable_returnErrorSlice() {
doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext);
final Slice mediaSlice = mDarkThemeSlice.getSlice();
final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
assertThat(metadata.isErrorSlice()).isTrue();
}
@Test
public void getSlice_newSession_notAvailable_returnErrorSlice() {
// previous displayed: yes
mDarkThemeSlice.sKeepSliceShow = true;
// Session: use original value + 1 to become a new session
mDarkThemeSlice.sActiveUiSession =
mFeatureFactory.slicesFeatureProvider.getUiSessionToken() + 1;
doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext);
final Slice mediaSlice = mDarkThemeSlice.getSlice();
final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
assertThat(metadata.isErrorSlice()).isTrue();
}
@Test
public void getSlice_previouslyDisplayed_isAvailable_returnSlice() {
mDarkThemeSlice.sActiveUiSession =
mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
mDarkThemeSlice.sKeepSliceShow = true;
setBatteryCapacityLevel(40);
assertThat(mDarkThemeSlice.getSlice()).isNotNull();
}
@Test
public void getSlice_isAvailable_returnSlice() {
setBatteryCapacityLevel(40);
assertThat(mDarkThemeSlice.getSlice()).isNotNull();
}
@Test
public void getSlice_isAvailable_showTitleSubtitle() {
setBatteryCapacityLevel(40);
final Slice slice = mDarkThemeSlice.getSlice();
final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
assertThat(metadata.getTitle()).isEqualTo(
mContext.getString(R.string.dark_theme_slice_title));
assertThat(metadata.getSubtitle()).isEqualTo(
mContext.getString(R.string.dark_theme_slice_subtitle));
}
private void setBatteryCapacityLevel(int power_level) {
doReturn(false).when(mDarkThemeSlice).isDarkThemeMode(mContext);
doReturn(mBatteryManager).when(mContext).getSystemService(BatteryManager.class);
when(mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY))
.thenReturn(power_level);
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.panel; package com.android.settings.panel;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.settings.panel.SettingsPanelActivity.KEY_MEDIA_PACKAGE_NAME; import static com.android.settings.panel.SettingsPanelActivity.KEY_MEDIA_PACKAGE_NAME;
import static com.android.settings.panel.SettingsPanelActivity.KEY_PANEL_TYPE_ARGUMENT; import static com.android.settings.panel.SettingsPanelActivity.KEY_PANEL_TYPE_ARGUMENT;
@@ -28,17 +30,23 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.Intent; import android.content.Intent;
import android.view.MotionEvent; import android.os.Build;
import android.view.Window;
import android.view.WindowManager;
import com.android.settings.core.HideNonSystemOverlayMixin;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric; import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class SettingsPanelActivityTest { public class SettingsPanelActivityTest {
@@ -50,6 +58,7 @@ public class SettingsPanelActivityTest {
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this);
mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mSettingsPanelActivity = spy( mSettingsPanelActivity = spy(
Robolectric.buildActivity(FakeSettingsPanelActivity.class).create().get()); Robolectric.buildActivity(FakeSettingsPanelActivity.class).create().get());
@@ -87,4 +96,47 @@ public class SettingsPanelActivityTest {
assertThat(activity.mBundle.getString(KEY_PANEL_TYPE_ARGUMENT)) assertThat(activity.mBundle.getString(KEY_PANEL_TYPE_ARGUMENT))
.isEqualTo("com.android.settings.panel.action.MEDIA_OUTPUT"); .isEqualTo("com.android.settings.panel.action.MEDIA_OUTPUT");
} }
@Test
public void onStart_isNotDebuggable_shouldHideSystemOverlay() {
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false);
final ActivityController<SettingsPanelActivity> activityController =
Robolectric.buildActivity(SettingsPanelActivity.class).create();
final SettingsPanelActivity activity = spy(activityController.get());
final Window window = mock(Window.class);
when(activity.getWindow()).thenReturn(window);
activity.getLifecycle().addObserver(new HideNonSystemOverlayMixin(activity));
activityController.start();
verify(window).addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
}
@Test
public void onStop_isNotDebuggable_shouldRemoveHideSystemOverlay() {
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false);
final ActivityController<SettingsPanelActivity> activityController =
Robolectric.buildActivity(SettingsPanelActivity.class).create();
final SettingsPanelActivity activity = spy(activityController.get());
final Window window = mock(Window.class);
when(activity.getWindow()).thenReturn(window);
activity.getLifecycle().addObserver(new HideNonSystemOverlayMixin(activity));
activityController.start();
verify(window).addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
when(window.getAttributes()).thenReturn(layoutParams);
activityController.stop();
final ArgumentCaptor<WindowManager.LayoutParams> paramCaptor = ArgumentCaptor.forClass(
WindowManager.LayoutParams.class);
verify(window).setAttributes(paramCaptor.capture());
assertThat(paramCaptor.getValue().privateFlags
& SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(0);
}
} }

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.privacy;
import static android.Manifest.permission_group.CALENDAR;
import static android.Manifest.permission_group.CAMERA;
import static android.Manifest.permission_group.CONTACTS;
import static android.Manifest.permission_group.LOCATION;
import static android.Manifest.permission_group.MICROPHONE;
import static android.Manifest.permission_group.PHONE;
import static android.Manifest.permission_group.SMS;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.permission.RuntimePermissionUsageInfo;
import android.provider.DeviceConfig;
import android.view.accessibility.AccessibilityManager;
import androidx.preference.PreferenceScreen;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.Utils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import com.android.settings.testutils.shadow.ShadowPermissionControllerManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.widget.BarChartInfo;
import com.android.settingslib.widget.BarChartPreference;
import com.android.settingslib.widget.BarViewInfo;
import org.junit.After;
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.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.shadows.androidx.fragment.FragmentController;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDeviceConfig.class, ShadowUserManager.class,
ShadowPermissionControllerManager.class})
public class PermissionBarChartPreferenceControllerTest {
@Mock
private PreferenceScreen mScreen;
@Mock
private LockPatternUtils mLockPatternUtils;
private PermissionBarChartPreferenceController mController;
private BarChartPreference mPreference;
private PrivacyDashboardFragment mFragment;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
final Context context = RuntimeEnvironment.application;
final UserManager userManager = context.getSystemService(UserManager.class);
final ShadowUserManager shadowUserManager = Shadow.extract(userManager);
final ShadowAccessibilityManager accessibilityManager = Shadow.extract(
AccessibilityManager.getInstance(context));
accessibilityManager.setEnabledAccessibilityServiceList(new ArrayList<>());
shadowUserManager.addProfile(new UserInfo(123, null, 0));
when(FakeFeatureFactory.setupForTest().securityFeatureProvider.getLockPatternUtils(
any(Context.class))).thenReturn(mLockPatternUtils);
mController = spy(new PermissionBarChartPreferenceController(context, "test_key"));
mFragment = spy(FragmentController.of(new PrivacyDashboardFragment())
.create().start().get());
mController.setFragment(mFragment);
mPreference = spy(new BarChartPreference(context));
when(mScreen.findPreference(mController.getPreferenceKey()))
.thenReturn((BarChartPreference) mPreference);
}
@After
public void tearDown() {
ShadowDeviceConfig.reset();
}
@Test
public void getAvailabilityStatus_permissionHubNotSet_shouldReturnUnsupported() {
// We have not yet set the property to show the Permissions Hub.
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.privacy;
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 static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserManager;
import android.permission.PermissionControllerManager;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowPermissionControllerManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
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.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.shadows.androidx.fragment.FragmentController;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class, ShadowPermissionControllerManager.class})
public class PrivacyDashboardFragmentTest {
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
private PermissionControllerManager mPCM;
private Context mContext;
private PrivacyDashboardFragment mFragment;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
final UserManager userManager = mContext.getSystemService(UserManager.class);
final ShadowUserManager shadowUserManager = Shadow.extract(userManager);
final ShadowAccessibilityManager accessibilityManager = Shadow.extract(
AccessibilityManager.getInstance(mContext));
accessibilityManager.setEnabledAccessibilityServiceList(new ArrayList<>());
shadowUserManager.addProfile(new UserInfo(123, null, 0));
when(FakeFeatureFactory.setupForTest().securityFeatureProvider.getLockPatternUtils(
any(Context.class))).thenReturn(mLockPatternUtils);
mFragment = spy(FragmentController.of(new PrivacyDashboardFragment())
.create().start().get());
}
@Test
public void onViewCreated_shouldSetActionBarShadowAnimation() {
mFragment.onViewCreated(new View(mContext), new Bundle());
assertThat(mFragment.getActivity().getActionBar().getElevation()).isEqualTo(0.f);
}
@Test
public void onViewCreated_shouldInitLinearProgressBar() {
mFragment.onViewCreated(new View(mContext), new Bundle());
verify(mFragment).initLoadingBar();
}
@Test
public void updateLinearProgressbar_isVisible_shouldShowProgressBar() {
mFragment.setLoadingEnabled(true /* enabled */);
assertThat(mFragment.mProgressHeader.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mFragment.mProgressAnimation.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void updateLinearProgressbar_isInVisible_shouldHideProgressBar() {
mFragment.setLoadingEnabled(false /* enabled */);
assertThat(mFragment.mProgressHeader.getVisibility()).isEqualTo(View.INVISIBLE);
assertThat(mFragment.mProgressAnimation.getVisibility()).isEqualTo(View.INVISIBLE);
}
}

View File

@@ -24,6 +24,7 @@ import android.content.Context;
import com.android.settings.accounts.AccountFeatureProvider; import com.android.settings.accounts.AccountFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.aware.AwareFeatureProvider; import com.android.settings.aware.AwareFeatureProvider;
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
@@ -68,6 +69,7 @@ public class FakeFeatureFactory extends FeatureFactory {
public final ContextualCardFeatureProvider mContextualCardFeatureProvider; public final ContextualCardFeatureProvider mContextualCardFeatureProvider;
public final BluetoothFeatureProvider mBluetoothFeatureProvider; public final BluetoothFeatureProvider mBluetoothFeatureProvider;
public final AwareFeatureProvider mAwareFeatureProvider; public final AwareFeatureProvider mAwareFeatureProvider;
public final FaceFeatureProvider mFaceFeatureProvider;
public PanelFeatureProvider panelFeatureProvider; public PanelFeatureProvider panelFeatureProvider;
public SlicesFeatureProvider slicesFeatureProvider; public SlicesFeatureProvider slicesFeatureProvider;
@@ -114,6 +116,7 @@ public class FakeFeatureFactory extends FeatureFactory {
panelFeatureProvider = mock(PanelFeatureProvider.class); panelFeatureProvider = mock(PanelFeatureProvider.class);
mBluetoothFeatureProvider = mock(BluetoothFeatureProvider.class); mBluetoothFeatureProvider = mock(BluetoothFeatureProvider.class);
mAwareFeatureProvider = mock(AwareFeatureProvider.class); mAwareFeatureProvider = mock(AwareFeatureProvider.class);
mFaceFeatureProvider = mock(FaceFeatureProvider.class);
} }
@Override @Override
@@ -215,4 +218,9 @@ public class FakeFeatureFactory extends FeatureFactory {
public AwareFeatureProvider getAwareFeatureProvider() { public AwareFeatureProvider getAwareFeatureProvider() {
return mAwareFeatureProvider; return mAwareFeatureProvider;
} }
@Override
public FaceFeatureProvider getFaceFeatureProvider() {
return mFaceFeatureProvider;
}
} }