Merge "Fix Settings test that uses OverlayInfo"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7625e78..4166d6f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -340,7 +340,7 @@
android:clearTaskOnLaunch="true"
android:excludeFromRecents="true"
android:exported="true"
- android:permission="android.permission.CONNECTIVITY_INTERNAL"
+ android:permission="android.permission.NETWORK_STACK"
android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert">
<intent-filter>
<action android:name="android.net.conn.PROMPT_UNVALIDATED" />
@@ -901,8 +901,7 @@
<activity android:name=".Settings$MyDeviceInfoActivity"
android:label="@string/about_settings"
- android:icon="@drawable/ic_homepage_about"
- android:parentActivityName="Settings">
+ android:icon="@drawable/ic_homepage_about">
<intent-filter android:priority="1">
<action android:name="android.settings.DEVICE_INFO_SETTINGS" />
<action android:name="android.settings.DEVICE_NAME" />
@@ -1633,8 +1632,9 @@
android:icon="@drawable/ic_suggestion_security">
<intent-filter android:priority="1">
<action android:name="android.intent.action.MAIN" />
- <category android:name="com.android.settings.suggested.category.DEFAULT" />
+ <category android:name="com.android.settings.suggested.category.FIRST_IMPRESSION" />
</intent-filter>
+ <meta-data android:name="com.android.settings.dismiss" android:value="14" />
<meta-data android:name="com.android.settings.title"
android:resource="@string/suggested_lock_settings_title" />
<meta-data android:name="com.android.settings.summary"
@@ -1648,7 +1648,7 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.suggested.category.FIRST_IMPRESSION" />
</intent-filter>
- <meta-data android:name="com.android.settings.dismiss" android:value="2," />
+ <meta-data android:name="com.android.settings.dismiss" android:value="14" />
<meta-data android:name="com.android.settings.require_feature"
android:value="android.hardware.fingerprint" />
<meta-data android:name="com.android.settings.title"
@@ -2390,6 +2390,19 @@
</activity>
<activity
+ android:name="Settings$ZenAccessDetailSettingsActivity"
+ android:label="@string/manage_zen_access_title"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.settings.NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="package" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails" />
+ </activity>
+
+ <activity
android:name="Settings$ZenAccessSettingsActivity"
android:label="@string/manage_zen_access_title"
android:parentActivityName="Settings">
@@ -2540,7 +2553,7 @@
<receiver android:name=".sim.SimSelectNotification">
<intent-filter>
- <action android:name="android.intent.action.SIM_STATE_CHANGED"/>
+ <action android:name="android.intent.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED"/>
</intent-filter>
</receiver>
@@ -2609,6 +2622,8 @@
android:name=".wifi.NetworkRequestDialogActivity"
android:theme="@style/Transparent"
android:excludeFromRecents="true"
+ android:launchMode="singleTop"
+ android:taskAffinity=".wifi.NetworkRequestDialogActivity"
android:exported="true"
android:permission="android.permission.NETWORK_SETTINGS">
<intent-filter>
@@ -2958,6 +2973,17 @@
</intent-filter>
</service>
+ <service
+ android:name=".development.qstile.DevelopmentTiles$SensorsOff"
+ android:label="@string/sensors_off_quick_settings_title"
+ android:icon="@drawable/tile_icon_sensors_off"
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
+ android:enabled="false">
+ <intent-filter>
+ <action android:name="android.service.quicksettings.action.QS_TILE" />
+ </intent-filter>
+ </service>
+
<activity
android:name=".HelpTrampoline"
android:exported="true"
@@ -3124,6 +3150,29 @@
<activity android:name=".homepage.contextualcards.ContextualCardFeedbackDialog"
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+
+ <activity
+ android:name="Settings$WifiCallingDisclaimerActivity"
+ android:label="@string/wifi_calling_settings_title"
+ android:taskAffinity="com.android.settings">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.wifi.calling.WifiCallingDisclaimerFragment" />
+ </activity>
+
+ <activity android:name="Settings$BatterySaverScheduleSettingsActivity"
+ android:label="@string/battery_saver_schedule_settings_title"
+ android:parentActivityName="Settings">
+ <intent-filter>
+ <action android:name="com.android.settings.BATTERY_SAVER_SCHEDULE_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings" />
+ </activity>
<!-- This is the longest AndroidManifest.xml ever. -->
</application>
</manifest>
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index e9f8d58..7ab18a4 100644
--- a/color-check-baseline.xml
+++ b/color-check-baseline.xml
@@ -37,54 +37,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:textColor="@color/bluetooth_dialog_text_color" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/layout/bluetooth_pin_confirm.xml"
- line="44"
- column="13"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:textColor="@color/bluetooth_dialog_text_color""
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/layout/bluetooth_pin_confirm.xml"
- line="67"
- column="13"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:textColor="@color/bluetooth_dialog_text_color" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/layout/bluetooth_pin_confirm.xml"
- line="77"
- column="13"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:background="@color/lock_pattern_background" />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -101,22 +53,6 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="black">#000</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values/colors.xml"
- line="18"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="switch_bar_background">#dadce0</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -133,8 +69,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="red">#F00</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="crypt_keeper_clock_background">#ff9a9a9a</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
line="19"
@@ -165,8 +101,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="blue">#00F</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="crypt_keeper_clock_foreground">#ff666666</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
line="20"
@@ -181,6 +117,22 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="crypt_keeper_clock_am_pm">#ff9a9a9a</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="21"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="homepage_accessibility_background">#783BE5</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -197,8 +149,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="bluetooth_dialog_text_color">#8a000000</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="crypt_keeper_password_background">#70606060</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
line="22"
@@ -229,8 +181,8 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="crypt_keeper_clock_background">#ff9a9a9a</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="divider_color">#20ffffff</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
line="24"
@@ -261,75 +213,11 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="crypt_keeper_clock_foreground">#ff666666</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values/colors.xml"
- line="25"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="crypt_keeper_clock_am_pm">#ff9a9a9a</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values/colors.xml"
- line="26"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="crypt_keeper_password_background">#70606060</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values/colors.xml"
- line="27"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="divider_color">#20ffffff</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/values/colors.xml"
- line="29"
- column="5"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <color name="setup_lock_pattern_view_success_color_dark">#ff84ffff</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="32"
+ line="27"
column="5"/>
</issue>
@@ -345,7 +233,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="34"
+ line="29"
column="5"/>
</issue>
@@ -361,7 +249,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="35"
+ line="30"
column="5"/>
</issue>
@@ -377,7 +265,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="36"
+ line="31"
column="5"/>
</issue>
@@ -393,7 +281,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="38"
+ line="33"
column="5"/>
</issue>
@@ -409,7 +297,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="39"
+ line="34"
column="5"/>
</issue>
@@ -425,7 +313,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="41"
+ line="36"
column="5"/>
</issue>
@@ -441,7 +329,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="46"
+ line="41"
column="5"/>
</issue>
@@ -457,7 +345,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="47"
+ line="42"
column="5"/>
</issue>
@@ -473,7 +361,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="49"
+ line="44"
column="5"/>
</issue>
@@ -489,7 +377,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="50"
+ line="45"
column="5"/>
</issue>
@@ -505,7 +393,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="54"
+ line="49"
column="5"/>
</issue>
@@ -521,7 +409,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="57"
+ line="52"
column="5"/>
</issue>
@@ -537,7 +425,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="60"
+ line="55"
column="5"/>
</issue>
@@ -553,7 +441,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="61"
+ line="56"
column="5"/>
</issue>
@@ -569,7 +457,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="62"
+ line="57"
column="5"/>
</issue>
@@ -585,7 +473,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="63"
+ line="58"
column="5"/>
</issue>
@@ -597,11 +485,11 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="switch_bar_background">#ff80868B</color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <color name="switch_bar_background">#757575</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="64"
+ line="59"
column="5"/>
</issue>
@@ -617,7 +505,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="66"
+ line="61"
column="5"/>
</issue>
@@ -633,7 +521,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="67"
+ line="62"
column="5"/>
</issue>
@@ -649,7 +537,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="68"
+ line="63"
column="5"/>
</issue>
@@ -665,7 +553,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="69"
+ line="64"
column="5"/>
</issue>
@@ -681,7 +569,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="70"
+ line="65"
column="5"/>
</issue>
@@ -697,7 +585,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="71"
+ line="66"
column="5"/>
</issue>
@@ -713,7 +601,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="72"
+ line="67"
column="5"/>
</issue>
@@ -729,7 +617,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="73"
+ line="68"
column="5"/>
</issue>
@@ -745,7 +633,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="74"
+ line="69"
column="5"/>
</issue>
@@ -761,7 +649,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="75"
+ line="70"
column="5"/>
</issue>
@@ -777,7 +665,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="76"
+ line="71"
column="5"/>
</issue>
@@ -793,7 +681,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="78"
+ line="73"
column="5"/>
</issue>
@@ -809,7 +697,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="81"
+ line="76"
column="5"/>
</issue>
@@ -825,7 +713,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="84"
+ line="79"
column="5"/>
</issue>
@@ -841,7 +729,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="87"
+ line="82"
column="5"/>
</issue>
@@ -857,7 +745,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="90"
+ line="85"
column="5"/>
</issue>
@@ -873,7 +761,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="91"
+ line="86"
column="5"/>
</issue>
@@ -889,7 +777,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="92"
+ line="87"
column="5"/>
</issue>
@@ -905,7 +793,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="93"
+ line="88"
column="5"/>
</issue>
@@ -921,7 +809,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="94"
+ line="89"
column="5"/>
</issue>
@@ -937,7 +825,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="95"
+ line="90"
column="5"/>
</issue>
@@ -953,7 +841,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="96"
+ line="91"
column="5"/>
</issue>
@@ -969,7 +857,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="97"
+ line="92"
column="5"/>
</issue>
@@ -985,7 +873,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="98"
+ line="93"
column="5"/>
</issue>
@@ -1001,7 +889,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="99"
+ line="94"
column="5"/>
</issue>
@@ -1017,7 +905,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="100"
+ line="95"
column="5"/>
</issue>
@@ -1033,7 +921,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="101"
+ line="96"
column="5"/>
</issue>
@@ -1049,7 +937,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="102"
+ line="97"
column="5"/>
</issue>
@@ -1065,7 +953,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="103"
+ line="98"
column="5"/>
</issue>
@@ -1077,11 +965,11 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <color name="homepage_about_background">#9FA8DA</color>"
+ errorLine1=" <color name="homepage_about_background">#6F86DA</color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="104"
+ line="99"
column="5"/>
</issue>
@@ -1097,7 +985,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="105"
+ line="100"
column="5"/>
</issue>
@@ -1113,7 +1001,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="106"
+ line="102"
column="5"/>
</issue>
@@ -1129,7 +1017,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="110"
+ line="108"
column="5"/>
</issue>
@@ -1145,7 +1033,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="115"
+ line="113"
column="5"/>
</issue>
@@ -1161,7 +1049,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="116"
+ line="114"
column="5"/>
</issue>
@@ -1177,7 +1065,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="117"
+ line="115"
column="5"/>
</issue>
@@ -1193,7 +1081,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="118"
+ line="116"
column="5"/>
</issue>
@@ -1209,7 +1097,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="119"
+ line="117"
column="5"/>
</issue>
@@ -1225,7 +1113,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="120"
+ line="118"
column="5"/>
</issue>
@@ -1241,7 +1129,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="123"
+ line="121"
column="5"/>
</issue>
@@ -1257,7 +1145,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="124"
+ line="122"
column="5"/>
</issue>
@@ -1273,7 +1161,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="125"
+ line="123"
column="5"/>
</issue>
@@ -1289,7 +1177,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="126"
+ line="124"
column="5"/>
</issue>
@@ -1305,7 +1193,55 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="127"
+ line="125"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="notification_block_color">#ffff0000</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="128"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="notification_silence_color">#fbbc04</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="129"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="notification_alert_color">#30a751</color>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="130"
column="5"/>
</issue>
@@ -1321,7 +1257,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="133"
+ line="136"
column="5"/>
</issue>
@@ -1337,7 +1273,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="136"
+ line="139"
column="5"/>
</issue>
@@ -1353,7 +1289,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="137"
+ line="140"
column="5"/>
</issue>
@@ -1369,7 +1305,231 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="138"
+ line="141"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_icon_1">#48a50e0e</color> <!-- 72% Material Red 900 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="144"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_icon_2">#480d652d</color> <!-- 72% Material Green 900 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="145"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_icon_3">#48e37400</color> <!-- 72% Material Yellow 900 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="146"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_icon_4">#48b06000</color> <!-- 72% Material Orange 900 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="147"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_icon_5">#489c166b</color> <!-- 72% Material Pink 900 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="148"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_icon_6">#48681da8</color> <!-- 72% Material Purple 900 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="149"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_icon_7">#48007b83</color> <!-- 72% Material Cyan 900 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="150"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_bg_1">#fad2cf</color> <!-- Material Red 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="152"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_bg_2">#ceead6</color> <!-- Material Green 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="153"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_bg_3">#feefc3</color> <!-- Material Yellow 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="154"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_bg_4">#fedfc8</color> <!-- Material Orange 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="155"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_bg_5">#fdcfe8</color> <!-- Material Pink 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="156"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_bg_6">#e9d2fd</color> <!-- Material Purple 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="157"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <color name="bt_color_bg_7">#cbf0f8</color> <!-- Material Cyan 100 -->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/values/colors.xml"
+ line="158"
column="5"/>
</issue>
@@ -1653,12 +1813,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_about_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_about_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_about.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1669,12 +1829,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_accessibility_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_accessibility_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_accessibility.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1685,12 +1845,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_accessibility_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_accessibility_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_accessibility.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1701,12 +1861,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_accounts_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_accounts_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_accounts.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1717,12 +1877,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_app_and_notification_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_app_and_notification_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_apps.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1733,12 +1893,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_battery_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_battery_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_battery.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1749,12 +1909,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_connected_device_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_connected_device_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_connected_device.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1765,12 +1925,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_display_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_display_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_display.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1781,28 +1941,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_generic_icon_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="res/drawable/ic_homepage_generic_background.xml"
- line="20"
- column="9"/>
- </issue>
-
- <issue
- id="HardCodedColor"
- severity="Error"
- message="Avoid using hardcoded color"
- category="Correctness"
- priority="4"
- summary="Using hardcoded color"
- explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_location_background"/>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_location_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_location.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1813,12 +1957,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_network_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_network_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_network.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1829,12 +1973,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_privacy_background"/>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_privacy_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_privacy.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1845,12 +1989,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_security_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_security_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_security.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1861,12 +2005,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_sound_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_sound_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_sound.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1877,12 +2021,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_storage_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_storage_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_storage.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1893,12 +2037,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_support_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_support_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_support.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1909,12 +2053,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_support_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_support_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_support.xml"
- line="23"
- column="17"/>
+ line="24"
+ column="13"/>
</issue>
<issue
@@ -1925,11 +2069,107 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" android:color="@color/homepage_system_background" />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" android:color="@color/homepage_system_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_homepage_system_dashboard.xml"
- line="23"
+ line="24"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" android:color="@color/notification_alert_color"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_notification_alert.xml"
+ line="27"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" android:fillColor="@color/notification_alert_color""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_notification_alert.xml"
+ line="39"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" android:color="@color/notification_block_color"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_notification_block.xml"
+ line="27"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" android:fillColor="@color/notification_block_color""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_notification_block.xml"
+ line="39"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" android:color="@color/notification_silence_color"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_notification_silence.xml"
+ line="27"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" android:fillColor="@color/notification_silence_color""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_notification_silence.xml"
+ line="39"
column="17"/>
</issue>
@@ -2437,12 +2677,12 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
- errorLine1=" <string name="sync_plug" msgid="3905078969081888738">""<font fgcolor="#ffffffff">"Welcome to Google sync!"</font>" \nA Google approach to syncing data to allow access to your contacts, appointments, and more from wherever you are."</string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" <string name="sync_plug" msgid="3905078969081888738">""<font fgcolor="#ffffffff">"Welcome to Google sync!"</font>" \nA Google approach to syncing data to allow access to your contacts, appointments, and more from wherever you are."</string>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rXC/strings.xml"
- line="2533"
- column="168"/>
+ line="2580"
+ column="169"/>
</issue>
<issue
@@ -2457,7 +2697,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rAU/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2473,7 +2713,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rCA/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2489,7 +2729,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rGB/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2505,7 +2745,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rIN/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2521,7 +2761,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/strings.xml"
- line="5885"
+ line="6020"
column="36"/>
</issue>
@@ -2537,7 +2777,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="174"
+ line="163"
column="41"/>
</issue>
@@ -2553,7 +2793,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="388"
+ line="380"
column="44"/>
</issue>
@@ -2569,7 +2809,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="394"
+ line="386"
column="44"/>
</issue>
@@ -2585,7 +2825,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="395"
+ line="387"
column="44"/>
</issue>
@@ -2601,7 +2841,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="430"
+ line="423"
column="34"/>
</issue>
@@ -2729,7 +2969,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="166"
+ line="169"
column="45"/>
</issue>
@@ -2745,7 +2985,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="167"
+ line="170"
column="49"/>
</issue>
@@ -2761,7 +3001,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="175"
+ line="178"
column="45"/>
</issue>
@@ -2777,7 +3017,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="176"
+ line="179"
column="49"/>
</issue>
@@ -2793,7 +3033,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="192"
+ line="195"
column="39"/>
</issue>
@@ -2809,7 +3049,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="193"
+ line="196"
column="40"/>
</issue>
@@ -2825,7 +3065,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="194"
+ line="197"
column="38"/>
</issue>
diff --git a/res/color/circle_outline_color.xml b/res/color/circle_outline_color.xml
new file mode 100644
index 0000000..eb4e83c
--- /dev/null
+++ b/res/color/circle_outline_color.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.14"
+ android:color="?android:attr/colorForeground"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/circle_outline.xml b/res/drawable/circle_outline.xml
new file mode 100644
index 0000000..1b2631d
--- /dev/null
+++ b/res/drawable/circle_outline.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <stroke
+ android:width="1dp"
+ android:color="@color/circle_outline_color"/>
+</shape>
diff --git a/res/drawable/face_enroll_introduction.xml b/res/drawable/face_enroll_introduction.xml
index 4493d66..6065f23 100644
--- a/res/drawable/face_enroll_introduction.xml
+++ b/res/drawable/face_enroll_introduction.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 The Android Open Source Project
~
@@ -15,9 +16,29 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
+ android:width="240dp"
+ android:height="240dp"
+ android:viewportWidth="300"
+ android:viewportHeight="300">
+
+ <path
+ android:fillColor="#DADCE0"
+ android:fillType="evenOdd"
+ android:pathData="M150,0C67.16,0,0,67.16,0,150s67.16,150,150,150 s150-67.16,150-150S232.84,0,150,0 M150,4c39,0,75.66,15.19,103.24,42.76C280.81,74.34,296,111,296,150s-15.19,75.66-42.76,103.24 C225.66,280.81,189,296,150,296s-75.66-15.19-103.24-42.76C19.19,225.66,4,189,4,150S19.19,74.34,46.76,46.76 C74.34,19.19,111,4,150,4" />
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="evenOdd"
+ android:pathData="M220,120c0,5.52-4.48,10-10,10s-10-4.48-10-10 s4.48-10,10-10S220,114.48,220,120" />
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="evenOdd"
+ android:pathData="M90,130c-5.52,0-10-4.48-10-10s4.48-10,10-10 s10,4.48,10,10S95.52,130,90,130" />
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="evenOdd"
+ android:pathData="M164.2,215.88c-3.83,3.25-8.79,5.22-14.2,5.22 c-12.13,0-22-9.87-22-22c0-0.03,0-0.06,0-0.09l-8-0.03c0,0.04,0,0.08,0,0.13c0,6.24,1.9,12.03,5.16,16.83 c5.4,7.95,14.51,13.17,24.84,13.17c1.04,0,2.06-0.05,3.08-0.15c6.18-0.63,11.81-3.14,16.3-6.95L164.2,215.88z" />
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:fillType="evenOdd"
+ android:pathData="M 153 140 L 153 166 L 137 166 L 137 174 L 161 174 L 161 140 Z" />
</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_face_enroll_introduction_detail.xml b/res/drawable/ic_face_enroll_introduction_detail.xml
new file mode 100644
index 0000000..7159148
--- /dev/null
+++ b/res/drawable/ic_face_enroll_introduction_detail.xml
@@ -0,0 +1,30 @@
+<?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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0 0h24v24H0V0z" />
+ <path
+ android:fillColor="#000000"
+ android:pathData="M11 15h2v2h-2zm0-8h2v6h-2zm0.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_face_header.xml b/res/drawable/ic_face_header.xml
index 0bf2c07..15ea7ae 100644
--- a/res/drawable/ic_face_header.xml
+++ b/res/drawable/ic_face_header.xml
@@ -1,4 +1,4 @@
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,10 +15,17 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="32dp"
android:width="32dp"
+ android:height="32dp"
android:tint="?android:attr/colorPrimary"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+
+ <path
+ android:fillColor="#000000"
+ android:pathData="M 12 13 C 13.1045694997 13 14 13.8954305003 14 15 C 14 16.1045694997 13.1045694997 17 12 17 C 10.8954305003 17 10 16.1045694997 10 15 C 10 13.8954305003 10.8954305003 13 12 13 Z" />
+ <path
+ android:fillColor="#000000"
+ android:pathData="M18.5,1C16.01,1,14,3.01,14,5.5V8H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10c0-1.1-0.9-2-2-2h-2V5.5 C16,4.12,17.12,3,18.5,3C19.88,3,21,4.12,21,5.5V6h2V5.5C23,3.01,20.99,1,18.5,1z M18,10v10H6V10H18z" />
+ <path android:pathData="M0,0h24v24H0V0z" />
</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_gray_scale_24dp.xml b/res/drawable/ic_gray_scale_24dp.xml
new file mode 100644
index 0000000..3fda134
--- /dev/null
+++ b/res/drawable/ic_gray_scale_24dp.xml
@@ -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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM11,19.93C7.06,19.44 4,16.08 4,12s3.05,-7.44 7,-7.93V19.93zM13,4.07C14.03,4.2 15,4.52 15.87,5H13V4.07zM13,7h5.24c0.25,0.31 0.48,0.65 0.68,1H13V7zM13,10h6.74c0.08,0.33 0.15,0.66 0.19,1H13V10zM13,19.93V19h2.87C15,19.48 14.03,19.8 13,19.93zM18.24,17H13v-1h5.92C18.72,16.35 18.49,16.69 18.24,17zM19.74,14H13v-1h6.93C19.89,13.34 19.82,13.67 19.74,14z"/>
+</vector>
+
diff --git a/res/drawable/ic_notification_alert.xml b/res/drawable/ic_notification_alert.xml
new file mode 100644
index 0000000..07e7b48
--- /dev/null
+++ b/res/drawable/ic_notification_alert.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/back">
+ <shape android:shape="oval">
+ <solid
+ android:color="@android:color/transparent" />
+ <size
+ android:height="48dp"
+ android:width="48dp"/>
+ <stroke android:width="1dp"
+ android:color="@color/notification_alert_color"/>
+ </shape>
+ </item>
+ <item
+ android:id="@+id/fore"
+ android:gravity="center">
+ <vector
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="@color/notification_alert_color"
+ android:pathData="M7.58 4.08L6.15 2.65C3.75 4.48 2.17 7.3 2.03 10.5h2c.15-2.65 1.51-4.97 3.55-6.42zm12.39 6.42h2c-.15-3.2-1.73-6.02-4.12-7.85l-1.42 1.43c2.02 1.45 3.39 3.77 3.54 6.42zM18 11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2v-5zm-6 11c.14 0 .27-.01.4-.04.65-.14 1.18-.58 1.44-1.18.1-.24.15-.5.15-.78h-4c.01 1.1.9 2 2.01 2z"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/ic_notification_block.xml b/res/drawable/ic_notification_block.xml
new file mode 100644
index 0000000..d4f0a2a
--- /dev/null
+++ b/res/drawable/ic_notification_block.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/back">
+ <shape android:shape="oval">
+ <solid
+ android:color="@android:color/transparent" />
+ <size
+ android:height="48dp"
+ android:width="48dp"/>
+ <stroke android:width="1dp"
+ android:color="@color/notification_block_color"/>
+ </shape>
+ </item>
+ <item
+ android:id="@+id/fore"
+ android:gravity="center">
+ <vector
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="@color/notification_block_color"
+ android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/ic_notification_silence.xml b/res/drawable/ic_notification_silence.xml
new file mode 100644
index 0000000..673340f
--- /dev/null
+++ b/res/drawable/ic_notification_silence.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/back">
+ <shape android:shape="oval">
+ <solid
+ android:color="@android:color/transparent" />
+ <size
+ android:height="48dp"
+ android:width="48dp"/>
+ <stroke android:width="1dp"
+ android:color="@color/notification_silence_color"/>
+ </shape>
+ </item>
+ <item
+ android:id="@+id/fore"
+ android:gravity="center">
+ <vector
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="@color/notification_silence_color"
+ android:pathData="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z" />
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/ic_qrcode_24dp.xml b/res/drawable/ic_qrcode_24dp.xml
index ff7806f..6928cb9 100644
--- a/res/drawable/ic_qrcode_24dp.xml
+++ b/res/drawable/ic_qrcode_24dp.xml
@@ -2,7 +2,8 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
- android:viewportHeight="24">
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FF000000"
android:pathData="M7,4v3H4V4H7M9,2H2v7h7V2L9,2z"/>
diff --git a/res/drawable/ic_scan_24dp.xml b/res/drawable/ic_scan_24dp.xml
index bcef8e3..c7b82d1 100644
--- a/res/drawable/ic_scan_24dp.xml
+++ b/res/drawable/ic_scan_24dp.xml
@@ -2,7 +2,8 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
- android:viewportHeight="24">
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FF000000"
android:pathData="M9,2l-7,0l0,2l7,0l0,-2z"/>
diff --git a/res/drawable/tile_icon_sensors_off.xml b/res/drawable/tile_icon_sensors_off.xml
new file mode 100644
index 0000000..890fa83
--- /dev/null
+++ b/res/drawable/tile_icon_sensors_off.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M21.966,2 L2,22"
+ android:strokeLineCap="round"
+ android:strokeColor="#000000"
+ android:fillColor="#00000000"
+ android:strokeWidth="1.6521436"
+ android:strokeLineJoin="miter"
+ android:strokeAlpha="1"/>
+ <path
+ android:pathData="M0.752,12L4.496,12l2.496,-6.25 2.496,12.5 2.496,-15 2.496,12.5 2.496,-7.5 1.248,3.75h4.992"
+ android:strokeLineCap="round"
+ android:strokeColor="#000000"
+ android:fillColor="#00000000"
+ android:strokeWidth="1.25090861"
+ android:strokeLineJoin="round"
+ android:strokeAlpha="1"/>
+</vector>
+
diff --git a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
index 3a0e659..c408a97 100644
--- a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
@@ -60,10 +60,10 @@
android:id="@+id/error_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
android:textAlignment="center"
- android:layout_marginTop="8dp"
- android:visibility="invisible"
- android:textColor="?android:attr/colorError"/>
+ android:textColor="?android:attr/colorError"
+ android:visibility="invisible"/>
</LinearLayout>
diff --git a/res/layout/advanced_bt_entity_sub.xml b/res/layout/advanced_bt_entity_sub.xml
index 07ea814..0f30583 100644
--- a/res/layout/advanced_bt_entity_sub.xml
+++ b/res/layout/advanced_bt_entity_sub.xml
@@ -26,9 +26,11 @@
android:id="@+id/header_icon"
android:layout_width="72dp"
android:layout_height="72dp"
- android:scaleType="fitCenter"
android:layout_gravity="center_horizontal"
- android:antialias="true"/>
+ android:antialias="true"
+ android:background="@drawable/circle_outline"
+ android:padding="8dp"
+ android:scaleType="fitCenter"/>
<TextView
android:id="@+id/header_title"
diff --git a/res/layout/face_enroll_introduction.xml b/res/layout/face_enroll_introduction.xml
index b5f13db..b55041a 100644
--- a/res/layout/face_enroll_introduction.xml
+++ b/res/layout/face_enroll_introduction.xml
@@ -27,7 +27,7 @@
<LinearLayout
style="@style/SudContentFrame"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
@@ -45,50 +45,87 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- <com.google.android.setupdesign.view.FillContentLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1">
-
- <com.google.android.setupdesign.view.IllustrationVideoView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/face_enroll_introduction_animation"
- style="@style/SudContentIllustration"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:sudVideo="@raw/face_enroll_introduction_animation" />
-
- </com.google.android.setupdesign.view.FillContentLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center">
- <Button
- android:id="@+id/accessibility_button"
- style="@style/SudGlifButton.Secondary"
- android:layout_gravity="center"
+ android:layout_height="wrap_content">
+
+ <com.google.android.setupdesign.view.IllustrationVideoView
+ android:id="@+id/illustration_normal"
+ style="@style/SudContentIllustration"
+ android:layout_width="240dp"
+ android:layout_height="240dp"
+ app:sudVideo="@raw/face_enroll_introduction_animation" />
+
+ <ImageView
+ android:id="@+id/illustration_accessibility"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/security_settings_face_enroll_introduction_accessibility" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
android:layout_gravity="center"
- android:id="@+id/accessibility_layout"
- android:orientation="vertical"
- android:visibility="invisible">
+ android:visibility="invisible"
+ android:background="@drawable/face_enroll_introduction" />
+
+ </FrameLayout>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="2"/>
+
+ <!-- Contains the buttons and extra information text at the bottom -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center_horizontal|bottom">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/accessibility_button"
+ style="@style/SudGlifButton.Secondary"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/security_settings_face_enroll_introduction_accessibility" />
<com.android.settings.biometrics.face.FaceEnrollAccessibilityToggle
android:id="@+id/toggle_diversity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:visibility="invisible"
FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_diversity"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/footer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/ic_face_enroll_introduction_detail">
+ </ImageView>
+ <Space
+ android:layout_width="8dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/security_settings_face_enroll_introduction_footer_message"/>
</LinearLayout>
- </FrameLayout>
+
+ </LinearLayout>
</LinearLayout>
diff --git a/res/layout/homepage_condition_header.xml b/res/layout/homepage_condition_header.xml
index a2796ec..5460be9 100644
--- a/res/layout/homepage_condition_header.xml
+++ b/res/layout/homepage_condition_header.xml
@@ -45,7 +45,8 @@
android:paddingTop="@dimen/homepage_condition_header_indicator_padding_top"
android:paddingStart="@dimen/homepage_condition_header_indicator_padding_start"
android:paddingEnd="@dimen/homepage_condition_header_indicator_padding_end"
- android:src="@*android:drawable/ic_expand_more"/>
+ android:src="@drawable/ic_expand_more_inverse"
+ android:tint="?android:attr/colorControlNormal"/>
</LinearLayout>
diff --git a/res/layout/homepage_slice_tile.xml b/res/layout/homepage_slice_tile.xml
index e95129e..ca8791f 100644
--- a/res/layout/homepage_slice_tile.xml
+++ b/res/layout/homepage_slice_tile.xml
@@ -31,7 +31,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:animateLayoutChanges="true"
+ android:importantForAccessibility="no"
style="@style/SliceViewStyle"/>
<!--dismissal view-->
diff --git a/res/layout/manage_applications_apps.xml b/res/layout/manage_applications_apps.xml
index 87db820..9c90f02 100644
--- a/res/layout/manage_applications_apps.xml
+++ b/res/layout/manage_applications_apps.xml
@@ -26,41 +26,34 @@
android:layout_height="match_parent"
settings:layout_behavior="com.android.settings.widget.FloatingAppBarScrollingViewBehavior">
- <LinearLayout
+ <FrameLayout
android:id="@+id/list_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
android:visibility="gone">
- <FrameLayout
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/apps_list"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ settings:fastScrollEnabled="true"
+ settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+ settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/apps_list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- settings:fastScrollEnabled="true"
- settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
- settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
- settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
- settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+ <TextView
+ android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:text="@string/no_applications"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:visibility="invisible"/>
- <TextView
- android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:layout_gravity="center"
- android:text="@string/no_applications"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:visibility="invisible"/>
-
- </FrameLayout>
-
- </LinearLayout>
+ </FrameLayout>
<include layout="@layout/loading_container"/>
diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml
index 9c1dd80..6368588 100644
--- a/res/layout/master_clear.xml
+++ b/res/layout/master_clear.xml
@@ -127,12 +127,9 @@
android:text="@string/erase_external_storage_description"/>
</LinearLayout>
</LinearLayout>
- <include
- layout="@layout/reset_esim_checkbox"
- android:layout_marginTop="40dp"
- android:id="@+id/erase_esim_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+
+ <include layout="@layout/reset_esim_checkbox"/>
+
</LinearLayout>
</ScrollView>
</com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/network_request_dialog_title.xml b/res/layout/network_request_dialog_title.xml
index f643e0f..b61a7db 100644
--- a/res/layout/network_request_dialog_title.xml
+++ b/res/layout/network_request_dialog_title.xml
@@ -25,14 +25,12 @@
<TextView
android:id="@+id/network_request_title_text"
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:paddingLeft="24dp"
android:paddingTop="18dp"
android:layout_weight="1"
android:textSize="18sp"
android:gravity="center_vertical"
- android:maxLines="1"
- android:ellipsize="end"
style="@style/info_label"/>
<ProgressBar
diff --git a/res/layout/notif_importance_preference.xml b/res/layout/notif_importance_preference.xml
new file mode 100644
index 0000000..5d79ff3
--- /dev/null
+++ b/res/layout/notif_importance_preference.xml
@@ -0,0 +1,104 @@
+<?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:id="@+id/app_entities_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/block"
+ android:layout_width="0dp"
+ android:layout_weight="33.33"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:layout_marginTop="16dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageButton
+ android:id="@+id/block_icon"
+ android:layout_width="@dimen/notification_importance_toggle_size"
+ android:layout_height="@dimen/notification_importance_toggle_size"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:src="@drawable/ic_notification_block"
+ android:contentDescription="@string/notification_block_title" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notification_block_title"
+ android:layout_marginTop="@dimen/notification_importance_toggle_marginTop"
+ android:layout_marginBottom="@dimen/notification_importance_toggle_marginBottom"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/silence"
+ android:layout_width="0dp"
+ android:layout_weight="33.33"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:layout_marginTop="16dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageButton
+ android:id="@+id/silence_icon"
+ android:layout_width="@dimen/notification_importance_toggle_size"
+ android:layout_height="@dimen/notification_importance_toggle_size"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:src="@drawable/ic_notification_silence"
+ android:contentDescription="@string/notification_silence_title" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notification_silence_title"
+ android:layout_marginTop="@dimen/notification_importance_toggle_marginTop"
+ android:layout_marginBottom="@dimen/notification_importance_toggle_marginBottom"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/alert"
+ android:layout_width="0dp"
+ android:layout_weight="33.33"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:layout_marginTop="16dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageButton
+ android:id="@+id/alert_icon"
+ android:layout_width="@dimen/notification_importance_toggle_size"
+ android:layout_height="@dimen/notification_importance_toggle_size"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:src="@drawable/ic_notification_alert"
+ android:contentDescription="@string/notification_alert_title" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notification_alert_title"
+ android:layout_marginTop="@dimen/notification_importance_toggle_marginTop"
+ android:layout_marginBottom="@dimen/notification_importance_toggle_marginBottom"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/preference_single_target.xml b/res/layout/preference_single_target.xml
new file mode 100644
index 0000000..b4a9de0
--- /dev/null
+++ b/res/layout/preference_single_target.xml
@@ -0,0 +1,96 @@
+<?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.
+ -->
+
+<!-- Based off preference_two_target.xml with Material ripple moved to parent for full ripple. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="56dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="48dp"
+ settings:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="64dp"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/res/layout/reset_esim_checkbox.xml b/res/layout/reset_esim_checkbox.xml
index 2195435..77b90a1 100644
--- a/res/layout/reset_esim_checkbox.xml
+++ b/res/layout/reset_esim_checkbox.xml
@@ -15,7 +15,9 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/erase_esim_container"
style="@style/SudDescription"
+ android:layout_marginTop="40dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@@ -41,10 +43,10 @@
android:orientation="vertical">
<TextView
- android:id="@+id/erase_esim_title"
style="@style/TextAppearance.SudGlifItemTitle"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:text="@string/reset_esim_title"/>
<TextView
style="@style/TextAppearance.SudGlifItemSummary"
diff --git a/res/layout/reset_network.xml b/res/layout/reset_network.xml
index 7eb5d2a..2c31d1a 100644
--- a/res/layout/reset_network.xml
+++ b/res/layout/reset_network.xml
@@ -40,10 +40,7 @@
android:textDirection="locale"
android:text="@string/reset_network_desc" />
- <include layout="@layout/reset_esim_checkbox"
- android:id="@+id/erase_esim_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <include layout="@layout/reset_esim_checkbox"/>
</LinearLayout>
diff --git a/res/layout/settings_homepage.xml b/res/layout/settings_homepage.xml
index e04b372..6e2b302 100644
--- a/res/layout/settings_homepage.xml
+++ b/res/layout/settings_homepage.xml
@@ -21,7 +21,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <androidx.recyclerview.widget.RecyclerView
+ <com.android.settings.homepage.contextualcards.FocusRecyclerView
android:id="@+id/card_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/res/layout/settings_homepage_container.xml b/res/layout/settings_homepage_container.xml
index 78a0278..9332463 100644
--- a/res/layout/settings_homepage_container.xml
+++ b/res/layout/settings_homepage_container.xml
@@ -25,6 +25,7 @@
android:id="@+id/main_content_scrollable_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:importantForAccessibility="no"
app:layout_behavior="com.android.settings.widget.FloatingAppBarScrollingViewBehavior">
<LinearLayout
diff --git a/res/layout/slice_preference_layout.xml b/res/layout/slice_preference_layout.xml
index ae58901..4cea9c0 100644
--- a/res/layout/slice_preference_layout.xml
+++ b/res/layout/slice_preference_layout.xml
@@ -25,5 +25,5 @@
<androidx.slice.widget.SliceView
android:id="@+id/slice_view"
android:layout_width="match_parent"
- android:layout_height="@dimen/slice_preference_group_height"/>
+ android:layout_height="wrap_content"/>
</FrameLayout>
diff --git a/res/layout/time_zone_search_header.xml b/res/layout/time_zone_search_header.xml
new file mode 100644
index 0000000..5c4e0ee
--- /dev/null
+++ b/res/layout/time_zone_search_header.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+<!-- similar to preference_material.xml but textview for emoji country flag
+instead of an ImageView -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:layout_marginTop="16dp"
+ android:textAppearance="@style/TextAppearance.CategoryTitle"
+ android:textColor="?android:attr/colorAccent"
+ android:paddingStart="@dimen/preference_no_icon_padding_start"/>
diff --git a/res/layout/wfc_disclaimer_fragment.xml b/res/layout/wfc_disclaimer_fragment.xml
new file mode 100644
index 0000000..00baae5
--- /dev/null
+++ b/res/layout/wfc_disclaimer_fragment.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginBottom="20dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="@string/wfc_disclaimer_title_text" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/disclaimer_item_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <Button
+ android:id="@+id/disagree_button"
+ style="@style/DisclaimerNegativeButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/wfc_disclaimer_disagree_text" />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/agree_button"
+ style="@style/DisclaimerPositiveButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/wfc_disclaimer_agree_button_text" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/wfc_simple_disclaimer_item.xml b/res/layout/wfc_simple_disclaimer_item.xml
new file mode 100644
index 0000000..ee43182
--- /dev/null
+++ b/res/layout/wfc_simple_disclaimer_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="20dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical">
+ <TextView
+ android:id="@+id/disclaimer_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead" />
+ <TextView
+ android:id="@+id/disclaimer_desc"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Body1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:layout_below="@id/disclaimer_title" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/wifi_button_preference_widget.xml b/res/layout/wifi_button_preference_widget.xml
index 1ecb98c..4b004e3 100644
--- a/res/layout/wifi_button_preference_widget.xml
+++ b/res/layout/wifi_button_preference_widget.xml
@@ -22,6 +22,4 @@
android:minWidth="@dimen/min_tap_target_size"
android:minHeight="@dimen/min_tap_target_size"
android:layout_gravity="center"
- android:background="@null"
- android:visibility="gone"
- android:contentDescription="@string/wifi_add_network" />
+ android:background="?android:attr/selectableItemBackground"/>
diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml
index 1ae3bf5..d5dbb17 100644
--- a/res/layout/wifi_dialog.xml
+++ b/res/layout/wifi_dialog.xml
@@ -50,29 +50,31 @@
android:text="@string/wifi_ssid"
android:textDirection="locale" />
- <RelativeLayout
+ <androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="wrap_content" >
+ android:layout_height="wrap_content">
<EditText android:id="@+id/ssid"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/ssid_scanner_button"
style="@style/wifi_item_edit_content"
android:hint="@string/wifi_ssid_hint"
android:singleLine="true"
- android:inputType="textNoSuggestions" />
+ android:inputType="textNoSuggestions"/>
+
<ImageButton
android:id="@+id/ssid_scanner_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="@dimen/min_tap_target_size"
android:minHeight="@dimen/min_tap_target_size"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:background="@null"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_scan_24dp"
- android:contentDescription="@string/wifi_add_network" />
- </RelativeLayout>
+ android:contentDescription="@string/wifi_dpp_scan_qr_code"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout android:id="@+id/ssid_too_long_warning"
android:layout_width="match_parent"
@@ -286,16 +288,18 @@
style="@style/wifi_item_label"
android:text="@string/wifi_password" />
- <RelativeLayout
+ <androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="wrap_content" >
+ android:layout_height="wrap_content">
<EditText android:id="@+id/password"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/password_scanner_button"
style="@style/wifi_item_edit_content"
android:singleLine="true"
- android:password="true" />
+ android:password="true"/>
<ImageButton
android:id="@+id/password_scanner_button"
@@ -303,12 +307,11 @@
android:layout_height="wrap_content"
android:minWidth="@dimen/min_tap_target_size"
android:minHeight="@dimen/min_tap_target_size"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:background="@null"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_scan_24dp"
- android:contentDescription="@string/wifi_add_network" />
- </RelativeLayout>
+ android:contentDescription="@string/wifi_dpp_scan_qr_code"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<LinearLayout android:id="@+id/show_password_layout"
diff --git a/res/layout/wifi_dpp_choose_saved_wifi_network_fragment.xml b/res/layout/wifi_dpp_choose_saved_wifi_network_fragment.xml
index ed288f0..3606ccd 100644
--- a/res/layout/wifi_dpp_choose_saved_wifi_network_fragment.xml
+++ b/res/layout/wifi_dpp_choose_saved_wifi_network_fragment.xml
@@ -22,28 +22,24 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ScrollView
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="0dp"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/footer">
- <LinearLayout
+ <include layout="@layout/wifi_dpp_fragment_header"/>
+
+ <LinearLayout android:id="@+id/wifi_network_list_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical">
+ android:layout_height="wrap_content"/>
- <include layout="@layout/wifi_dpp_fragment_header"/>
-
- <LinearLayout android:id="@+id/wifi_network_list_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@+id/header"/>
-
- </LinearLayout>
-
- </ScrollView>
+ </LinearLayout>
<include
+ android:id="@+id/footer"
layout="@layout/wifi_dpp_fragment_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/layout/wifi_dpp_fragment_header.xml b/res/layout/wifi_dpp_fragment_header.xml
index e8e71d1..364f360 100644
--- a/res/layout/wifi_dpp_fragment_header.xml
+++ b/res/layout/wifi_dpp_fragment_header.xml
@@ -40,27 +40,35 @@
android:src="@drawable/ic_devices_check_circle_green"
android:scaleType="fitCenter"/>
- <TextView
- android:id="@android:id/title"
- style="@style/TextAppearance.EntityHeaderTitle"
- android:layout_width="match_parent"
+ <!-- Add title_summary_container to group content for Talkback -->
+ <LinearLayout
+ android:id="@+id/title_summary_container"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:textAlignment="center"
- android:layout_marginTop="8dp"
- android:paddingStart="32dp"
- android:paddingEnd="32dp"/>
+ android:orientation="vertical"
+ android:focusable="true">
- <TextView
- android:id="@android:id/summary"
- style="@style/TextAppearance.EntityHeaderSummary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="false"
- android:gravity="center_horizontal"
- android:textAlignment="center"
- android:layout_marginTop="2dp"
- android:paddingStart="32dp"
- android:paddingEnd="32dp"/>
+ <TextView
+ android:id="@android:id/title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textAlignment="center"
+ android:layout_marginTop="8dp"
+ android:paddingStart="32dp"
+ android:paddingEnd="32dp"/>
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:gravity="center_horizontal"
+ android:textAlignment="center"
+ android:layout_marginTop="2dp"
+ android:paddingStart="32dp"
+ android:paddingEnd="32dp"/>
+ </LinearLayout>
</LinearLayout>
diff --git a/res/layout/wifi_dpp_qrcode_generator_fragment.xml b/res/layout/wifi_dpp_qrcode_generator_fragment.xml
index 2617aea..31bf7cc 100644
--- a/res/layout/wifi_dpp_qrcode_generator_fragment.xml
+++ b/res/layout/wifi_dpp_qrcode_generator_fragment.xml
@@ -40,6 +40,14 @@
android:layout_height="@dimen/qrcode_size"
android:src="@android:color/transparent"/>
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:textAlignment="center"
+ android:textAppearance="@android:style/TextAppearance.Material.Body1"
+ android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
</ScrollView>
diff --git a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
index 4fa8e8b..9bd742a 100644
--- a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
@@ -60,10 +60,10 @@
android:id="@+id/error_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
android:textAlignment="center"
- android:layout_marginTop="8dp"
- android:visibility="invisible"
- android:textColor="?android:attr/colorError"/>
+ android:textColor="?android:attr/colorError"
+ android:visibility="invisible"/>
</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index dc91f22..521d709 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1365,4 +1365,38 @@
<item>"9"</item>
</string-array>
+ <!-- WiFi calling mode array -->
+ <string-array name="wifi_calling_mode_summaries" translatable="false">
+ <item>@string/wifi_calling_mode_wifi_preferred_summary</item>
+ <item>@string/wifi_calling_mode_cellular_preferred_summary</item>
+ <item>@string/wifi_calling_mode_wifi_only_summary</item>
+ </string-array>
+
+ <!-- WiFi calling mode array without wifi only mode -->
+ <string-array name="wifi_calling_mode_summaries_without_wifi_only" translatable="false">
+ <item>@string/wifi_calling_mode_wifi_preferred_summary</item>
+ <item>@string/wifi_calling_mode_cellular_preferred_summary</item>
+ </string-array>
+
+ <!-- Bluetooth icon foreground colors -->
+ <integer-array name="bt_icon_fg_colors">
+ <item>@color/bt_color_icon_1</item>
+ <item>@color/bt_color_icon_2</item>
+ <item>@color/bt_color_icon_3</item>
+ <item>@color/bt_color_icon_4</item>
+ <item>@color/bt_color_icon_5</item>
+ <item>@color/bt_color_icon_6</item>
+ <item>@color/bt_color_icon_7</item>
+ </integer-array>
+
+ <!-- Bluetooth icon background colors -->
+ <integer-array name="bt_icon_bg_colors">
+ <item>@color/bt_color_bg_1</item>
+ <item>@color/bt_color_bg_2</item>
+ <item>@color/bt_color_bg_3</item>
+ <item>@color/bt_color_bg_4</item>
+ <item>@color/bt_color_bg_5</item>
+ <item>@color/bt_color_bg_6</item>
+ <item>@color/bt_color_bg_7</item>
+ </integer-array>
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index c7bf1c7..e099cf0 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -132,6 +132,11 @@
<attr name="aspectRatio" format="float" />
</declare-styleable>
+ <declare-styleable name="ListWithEntrySummaryPreference">
+ <!-- Summaries of entry -->
+ <attr name="entrySummaries" format="reference" />
+ </declare-styleable>
+
<!-- For UsageView -->
<declare-styleable name="UsageView">
<attr name="android:colorAccent" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 7b55a2b..59ed4c4 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -56,7 +56,7 @@
<color name="material_blue_700">#3367D6</color>
<color name="material_grey_100">#f5f5f5</color>
<color name="material_grey_200">#ffffff</color>
- <color name="switch_bar_background">#ff80868B</color>
+ <color name="switch_bar_background">#757575</color>
<color name="message_text_incoming">#ffffffff</color>
<color name="message_text_outgoing">#ff323232</color>
@@ -96,7 +96,7 @@
<color name="homepage_support_background">#26459C</color>
<color name="homepage_generic_icon_background">#1A73E8</color>
<color name="homepage_location_background">#2EC7DC</color>
- <color name="homepage_about_background">#9FA8DA</color>
+ <color name="homepage_about_background">#6F86DA</color>
<color name="homepage_privacy_background">#5E97F6</color>
<color name="homepage_card_stroke_color">#1f000000</color>
@@ -124,6 +124,11 @@
<color name="face_anim_particle_color_4">#fffdd835</color> <!-- Material Yellow 600 -->
<color name="face_anim_particle_error">#ff9e9e9e</color> <!-- Material Gray 500 -->
+ <!-- notification settings -->
+ <color name="notification_block_color">#ffff0000</color>
+ <color name="notification_silence_color">#fbbc04</color>
+ <color name="notification_alert_color">#30a751</color>
+
<!-- launcher icon color -->
<color name="icon_launcher_setting_color">@*android:color/accent_device_default_light</color>
@@ -135,4 +140,20 @@
<color name="qr_focused_corner_line_color">#ff1a73e8</color>
<color name="qr_background_color">#b3ffffff</color> <!-- 70% white transparency -->
<!-- End of QR code scanner colors -->
+
+ <color name="bt_color_icon_1">#48a50e0e</color> <!-- 72% Material Red 900 -->
+ <color name="bt_color_icon_2">#480d652d</color> <!-- 72% Material Green 900 -->
+ <color name="bt_color_icon_3">#48e37400</color> <!-- 72% Material Yellow 900 -->
+ <color name="bt_color_icon_4">#48b06000</color> <!-- 72% Material Orange 900 -->
+ <color name="bt_color_icon_5">#489c166b</color> <!-- 72% Material Pink 900 -->
+ <color name="bt_color_icon_6">#48681da8</color> <!-- 72% Material Purple 900 -->
+ <color name="bt_color_icon_7">#48007b83</color> <!-- 72% Material Cyan 900 -->
+
+ <color name="bt_color_bg_1">#fad2cf</color> <!-- Material Red 100 -->
+ <color name="bt_color_bg_2">#ceead6</color> <!-- Material Green 100 -->
+ <color name="bt_color_bg_3">#feefc3</color> <!-- Material Yellow 100 -->
+ <color name="bt_color_bg_4">#fedfc8</color> <!-- Material Orange 100 -->
+ <color name="bt_color_bg_5">#fdcfe8</color> <!-- Material Pink 100 -->
+ <color name="bt_color_bg_6">#e9d2fd</color> <!-- Material Purple 100 -->
+ <color name="bt_color_bg_7">#cbf0f8</color> <!-- Material Cyan 100 -->
</resources>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index e6ada66..805469a 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -356,4 +356,7 @@
<!-- Slice Uri to query nearby devices. -->
<string name="config_nearby_devices_slice_uri" translatable="false">content://com.google.android.gms.nearby.fastpair/device_status_list_item</string>
+
+ <!-- Grayscale settings intent -->
+ <string name="config_grayscale_settings_intent" translate="false"></string>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index aeeb403..248fb81 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -68,6 +68,9 @@
<dimen name="notification_app_icon_size">64dp</dimen>
<dimen name="notification_app_icon_badge_size">20dp</dimen>
<dimen name="notification_app_icon_badge_margin">4dp</dimen>
+ <dimen name="notification_importance_toggle_size">48dp</dimen>
+ <dimen name="notification_importance_toggle_marginTop">8dp</dimen>
+ <dimen name="notification_importance_toggle_marginBottom">16dp</dimen>
<dimen name="zen_schedule_rule_checkbox_padding">7dp</dimen>
<dimen name="zen_schedule_day_margin">17dp</dimen>
@@ -279,6 +282,15 @@
<dimen name="password_requirement_textsize">14sp</dimen>
+ <!-- Visible vertical space we want to show below password edittext field when ime is shown.
+ The unit is sp as it is related to the text size of password requirement item. -->
+ <dimen name="visible_vertical_space_below_password">20sp</dimen>
+
+ <!-- Select dialog -->
+ <dimen name="select_dialog_padding_start">20dp</dimen>
+ <dimen name="select_dialog_item_margin_start">12dp</dimen>
+ <dimen name="select_dialog_summary_padding_bottom">8dp</dimen>
+
<!-- Padding between the donut and the storage summary. -->
<dimen name="storage_summary_padding_end">16dp</dimen>
<!-- Text size of the big number in the donut. -->
@@ -380,7 +392,7 @@
<dimen name="homepage_condition_header_indicator_padding_top">4dp</dimen>
<dimen name="homepage_condition_header_indicator_padding_start">16dp</dimen>
<dimen name="homepage_condition_header_indicator_padding_end">16dp</dimen>
- <dimen name="homepage_condition_footer_height">44dp</dimen>
+ <dimen name="homepage_condition_footer_height">48dp</dimen>
<dimen name="homepage_condition_footer_padding_top">10dp</dimen>
<dimen name="homepage_condition_footer_padding_end">10dp</dimen>
<dimen name="homepage_condition_header_icon_width_height">24dp</dimen>
@@ -392,7 +404,4 @@
<!-- Wi-Fi DPP fragment icon size -->
<dimen name="wifi_dpp_fragment_icon_width_height">48dp</dimen>
-
- <!-- Height for slice preference, which contains 6 items at most -->
- <dimen name="slice_preference_group_height">360dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f3fe1f0..ceaedf9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -898,8 +898,10 @@
<string name="security_settings_face_enroll_introduction_message">Use your face to unlock your phone, authorize purchases, or sign in to apps.</string>
<!-- Introduction detail message shown in face enrollment dialog, when face unlock is disabled by device admin [CHAR LIMIT=NONE] -->
<string name="security_settings_face_enroll_introduction_message_unlock_disabled">Use your face to unlock your phone or approve purchases.\n\nNote: You can\u2019t use your face to unlock this device. For more information, contact your organization\u2019s admin.</string>
- <!-- Introduction detail message shwon in face enrollment screen in setup wizard. [CHAR LIMIT=NONE] -->
+ <!-- Introduction detail message shown in face enrollment screen in setup wizard. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_enroll_introduction_message_setup">Use your face to unlock your phone, authorize purchases, or sign in to apps</string>
+ <!-- Introduction detail message shown in face enrollment that provides extra detais. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_enroll_introduction_footer_message"></string>
<!-- Title shown in face enrollment dialog [CHAR LIMIT=40] -->
<string name="security_settings_face_enroll_repeat_title">Center your face in the circle</string>
<!-- Button text to skip enrollment of face [CHAR LIMIT=40] -->
@@ -941,6 +943,11 @@
<!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_footer">Your face can be used to unlock your device and access apps.
<annotation id="url">Learn more</annotation></string>
+ <!-- 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>
+ <!-- Dialog contents shown when the user removes an enrollment [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_remove_dialog_details">Data recorded by face unlock will be permanently and securely deleted. After removal, you will need your PIN, pattern, or password to unlock your phone, sign in to apps, and confirm payments.</string>
+
<!-- Fingerprint enrollment and settings --><skip />
<!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
@@ -1577,6 +1584,9 @@
<!-- Label for ChoosePassword/PIN Clear button that clears all text entered by the user so far. -->
<string name="lockpassword_clear_label">Clear</string>
+ <!-- Toast for a failed password change attempt when the old credential has been changed. [CHAR LIMIT=120]-->
+ <string name="lockpassword_credential_changed">Screen lock was already changed. Try again with the new screen lock.</string>
+
<!-- Label for LockPatternTutorial Cancel button -->
<string name="lockpattern_tutorial_cancel_label">Cancel</string>
@@ -2122,7 +2132,9 @@
<!-- Title for the fragment to share Wi-Fi [CHAR LIMIT=50] -->
<string name="wifi_dpp_share_wifi">Share Wi\u2011Fi</string>
<!-- Hint for the user to use another device to scan QR code on screen to join Wi-Fi [CHAR LIMIT=NONE] -->
- <string name="wifi_dpp_scan_qr_code_with_another_device">Scan this QR code with another device to join \u201c<xliff:g id="ssid" example="OfficeWifi">%1$s</xliff:g>\u201d</string>
+ <string name="wifi_dpp_scan_qr_code_with_another_device">Scan this QR code to connect to \u201c<xliff:g id="ssid" example="OfficeWifi">%1$s</xliff:g>\u201d and share the password</string>
+ <!-- Hint for the user to use another device to scan QR code on screen to join a open Wi-Fi [CHAR LIMIT=NONE] -->
+ <string name="wifi_dpp_scan_open_network_qr_code_with_another_device">Scan this QR code to connect to \u201c<xliff:g id="ssid" example="OfficeWifi">%1$s</xliff:g>\u201d</string>
<!-- Hint for QR code detection [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_could_not_detect_valid_qr_code">Couldn\u2019t read QR code. Re-center code and try again</string>
<!-- Hint for Wi-Fi DPP handshake failure [CHAR LIMIT=NONE] -->
@@ -2157,6 +2169,18 @@
<string name="wifi_dpp_sharing_wifi_with_this_device">Sharing Wi\u2011Fi with this device\u2026</string>
<!-- Hint for Wi-Fi DPP handshake running [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_connecting">Connecting\u2026</string>
+ <!-- Title for the fragment to show that the QR code is for sharing Wi-Fi hotspot network [CHAR LIMIT=50] -->
+ <string name="wifi_dpp_share_hotspot">Share hotspot</string>
+ <!-- Title for Wi-Fi DPP lockscreen title [CHAR LIMIT=50] -->
+ <string name="wifi_dpp_lockscreen_title">Verify it\u0027s you</string>
+ <!-- Hint for Wi-Fi password [CHAR LIMIT=50] -->
+ <string name="wifi_dpp_wifi_password">Wi\u2011Fi password: <xliff:g id="password" example="my password">%1$s</xliff:g></string>
+ <!-- Hint for Wi-Fi hotspot password [CHAR LIMIT=50] -->
+ <string name="wifi_dpp_hotspot_password">Hotspot password: <xliff:g id="password" example="my password">%1$s</xliff:g></string>
+ <!-- Label for "Connect to this network using a QR code" [CHAR LIMIT=50] -->
+ <string name="wifi_dpp_add_device">Add device</string>
+ <!-- Hint for "Add device" [CHAR LIMIT=NONE] -->
+ <string name="wifi_dpp_connect_network_using_qr_code">Connect to this network using a QR code</string>
<!-- Label for the try again button [CHAR LIMIT=20]-->
<string name="retry">Retry</string>
<!-- Label for the check box to share a network with other users on the same device -->
@@ -2209,6 +2233,8 @@
<!-- Dialog text to tell the user that the selected network does not have Internet access. -->
<string name="no_internet_access_text">This network has no internet access. Stay connected?</string>
+ <!-- Dialog text to tell the user that the selected network has incomplete Internet access. [CHAR LIMIT=100] -->
+ <string name="partial_connectivity_text">Some apps and services may not work due to limited connectivity. Use anyway?</string>
<string name="no_internet_access_remember">Don\u2019t ask again for this network</string>
<!-- Dialog text to tell the user that the selected network has lost Internet access, and asking the user whether they want to avoid this network. -->
@@ -2221,8 +2247,18 @@
<!-- Button label to connect to a Wi-Fi network -->
<string name="wifi_connect">Connect</string>
+ <!-- Turned on notification for Wi-Fi [CHAR LIMIT=40] -->
+ <string name="wifi_turned_on_message">Wi\u2011Fi turned on</string>
+ <!-- Connected to notification for Wi-Fi [CHAR LIMIT=NONE] -->
+ <string name="wifi_connected_to_message">@string/bluetooth_connected_summary</string>
+ <!-- Button label to connecting progress to a Wi-Fi network [CHAR LIMIT=20] -->
+ <string name="wifi_connecting">Connecting\u2026</string>
+ <!-- Button label to disconnect to a Wi-Fi network [CHAR LIMIT=NONE] -->
+ <string name="wifi_disconnect">@string/bluetooth_device_context_disconnect</string>
<!-- Failured notification for connect -->
<string name="wifi_failed_connect_message">Failed to connect to network</string>
+ <!-- Not in range notification for connect [CHAR LIMIT=40] -->
+ <string name="wifi_not_in_range_message">Network not in range</string>
<!-- Button label to delete a Wi-Fi network -->
<string name="wifi_forget">Forget</string>
<!-- Button label to modify a Wi-Fi network -->
@@ -2378,8 +2414,6 @@
<string name="wifi_hotspot_configure_ap_text_summary">AndroidAP WPA2 PSK hotspot</string>
<!-- Default access point SSID used for tethering -->
<string name="wifi_tether_configure_ssid_default">AndroidHotspot</string>
- <!-- Summary text when hotspot is disabled because airplane mode is on [CHAR LIMIT=80]-->
- <string name="wifi_tether_disabled_by_airplane">Unavailable because airplane mode is turned on</string>
<!-- Do not translate. Used for diagnostic screens, precise translation is not necessary
Wi-Fi Testing on the diagnostic screen-->
@@ -2476,7 +2510,7 @@
<!-- Title of WFC preference item [CHAR LIMIT=30] -->
<string name="wifi_calling_mode_title">Calling preference</string>
<!-- Title of WFC preference selection dialog [CHAR LIMIT=30] -->
- <string name="wifi_calling_mode_dialog_title">Wi-Fi calling mode</string>
+ <string name="wifi_calling_mode_dialog_title">Calling preference</string>
<!-- Title of WFC roaming preference item [CHAR LIMIT=45] -->
<string name="wifi_calling_roaming_mode_title">Roaming preference</string>
<!-- Summary of WFC roaming preference item [CHAR LIMIT=NONE]-->
@@ -2484,9 +2518,9 @@
<!-- WFC mode dialog [CHAR LIMIT=45] -->
<string name="wifi_calling_roaming_mode_dialog_title">Roaming preference</string>
<string-array name="wifi_calling_mode_choices">
- <item>Wi-Fi preferred</item>
- <item>Mobile preferred</item>
- <item>Wi-Fi only</item>
+ <item>@*android:string/wfc_mode_wifi_preferred_summary</item>
+ <item>@*android:string/wfc_mode_cellular_preferred_summary</item>
+ <item>@*android:string/wfc_mode_wifi_only_summary</item>
</string-array>
<string-array name="wifi_calling_mode_choices_v2">
<item>Wi-Fi</item>
@@ -2499,8 +2533,8 @@
<item>"0"</item>
</string-array>
<string-array name="wifi_calling_mode_choices_without_wifi_only">
- <item>Wi-Fi preferred</item>
- <item>Mobile preferred</item>
+ <item>@*android:string/wfc_mode_wifi_preferred_summary</item>
+ <item>@*android:string/wfc_mode_cellular_preferred_summary</item>
</string-array>
<string-array name="wifi_calling_mode_choices_v2_without_wifi_only">
<item>Wi-Fi</item>
@@ -2510,6 +2544,14 @@
<item>"2"</item>
<item>"1"</item>
</string-array>
+
+ <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]-->
+ <string name="wifi_calling_mode_wifi_preferred_summary">If Wi\u2011Fi is unavailable, use mobile network</string>
+ <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]-->
+ <string name="wifi_calling_mode_cellular_preferred_summary">If mobile network is unavailable, use Wi\u2011Fi</string>
+ <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]-->
+ <string name="wifi_calling_mode_wifi_only_summary">Call over Wi\u2011Fi. If Wi\u2011Fi is lost, call will end.</string>
+
<!-- Wi-Fi Calling settings. Text displayed when Wi-Fi Calling is off -->
<string name="wifi_calling_off_explanation">When Wi-Fi calling is on, your phone can route calls via Wi-Fi networks or your carrier\u2019s network, depending on your preference and which signal is stronger. Before turning on this feature, check with your carrier regarding fees and other details.<xliff:g id="additional_text" example="Learn More">%1$s</xliff:g></string>
<!-- Wi-Fi Calling settings. Additional text displayed when Wi-Fi Calling is off. Default empty. [CHAR LIMIT=NONE] -->
@@ -2968,6 +3010,8 @@
<string name="device_status_summary" product="default">Phone number, signal, etc.</string>
<!-- Main settings screen item's title to go into the storage settings screen [CHAR LIMIT=25] -->
<string name="storage_settings" >Storage</string>
+ <!-- Settings title that shows user how much storage and cache space an App is taking [CHAR LIMIT=50] -->
+ <string name="storage_settings_for_app" >Storage & cache</string>
<!-- Main settings screen item's title to go into the storage & USB settings screen [CHAR LIMIT=25] -->
<string name="storage_usb_settings" >Storage</string>
<!-- Storage settings screen title -->
@@ -3004,14 +3048,14 @@
<string name="status_prl_version">PRL version</string>
<!-- About phone screen, title for MEID for multi-sim devices -->
<string name="meid_multi_sim">MEID (sim slot %1$d)</string>
- <!-- The status text when both Wi-Fi scanning and Bluetooth scanning are on. [CHAR LIMIT=120] -->
- <string name="scanning_status_text_wifi_on_ble_on">Both Wi\u2011Fi and Bluetooth are allowed to determine location</string>
- <!-- The status text when Wi-Fi scanning is on and Bluetooth scanning are off. [CHAR LIMIT=120] -->
- <string name="scanning_status_text_wifi_on_ble_off">Only Wi\u2011Fi is allowed to determine location</string>
- <!-- The status text when Wi-Fi scanning is off and Bluetooth scanning are on. [CHAR LIMIT=120] -->
- <string name="scanning_status_text_wifi_off_ble_on">Only Bluetooth is allowed to determine location</string>
- <!-- The status text when both Wi-Fi scanning and Bluetooth scanning are off. [CHAR LIMIT=120] -->
- <string name="scanning_status_text_wifi_off_ble_off">Neither Wi\u2011Fi nor Bluetooth is allowed to determine location</string>
+ <!-- The status text when both Wi-Fi scanning and Bluetooth scanning are on. [CHAR LIMIT=100] -->
+ <string name="scanning_status_text_wifi_on_ble_on">Both Wi\u2011Fi and Bluetooth scanning are on</string>
+ <!-- The status text when Wi-Fi scanning is on and Bluetooth scanning are off. [CHAR LIMIT=100] -->
+ <string name="scanning_status_text_wifi_on_ble_off">Wi\u2011Fi scanning is on, Bluetooth scanning is off</string>
+ <!-- The status text when Wi-Fi scanning is off and Bluetooth scanning are on. [CHAR LIMIT=100] -->
+ <string name="scanning_status_text_wifi_off_ble_on">Bluetooth scanning is on, Wi\u2011Fi scanning is off</string>
+ <!-- The status text when both Wi-Fi scanning and Bluetooth scanning are off. [CHAR LIMIT=100] -->
+ <string name="scanning_status_text_wifi_off_ble_off">Both Wi\u2011Fi and Bluetooth scanning are off</string>
<!-- About phone, status item title. The phone MEID number of the current LTE/CDMA device. [CHAR LIMIT=30] -->
<string name="status_meid_number">MEID</string>
<!-- About phone, status item title. The ICCID of the current LTE device. [CHAR LIMIT=30] -->
@@ -3563,10 +3607,10 @@
<string name="reset_network_title">Reset Wi-Fi, mobile & Bluetooth</string>
<!-- SD card & phone storage settings screen, message on screen after user selects Reset network settings [CHAR LIMIT=NONE] -->
<string name="reset_network_desc">This will reset all network settings, including:\n\n<li>Wi\u2011Fi</li>\n<li>Mobile data</li>\n<li>Bluetooth</li>"</string>
- <!-- SD card & phone storage settings screen, title for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
- <string name="reset_esim_title">Also reset eSIM</string>
+ <!-- SD card & phone storage settings screen, title for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=30] -->
+ <string name="reset_esim_title">Erase eSIMs</string>
<!-- SD card & phone storage settings screen, message for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
- <string name="reset_esim_desc">Erase all eSIMs on the phone. You\u2019ll have to contact your carrier to redownload your eSIMs. This will not cancel your mobile service plan.</string>
+ <string name="reset_esim_desc">You\u2019ll have to contact your carrier to download replacement eSIMs. This won\u2019t cancel any mobile service plans.</string>
<!-- SD card & phone storage settings screen, button on screen after user selects Reset network settings -->
<string name="reset_network_button_text">Reset settings</string>
<!-- SD card & phone storage settings screen, message on screen after user selects Reset settings button -->
@@ -3615,12 +3659,6 @@
<string name="erase_external_storage_description" product="nosdcard">Erase all the data on the internal USB storage, such as music or photos</string>
<!-- SD card & phone storage settings screen, description for check box to erase USB storage [CHAR LIMIT=NONE] -->
<string name="erase_external_storage_description" product="default">Erase all the data on the SD card, such as music or photos</string>
- <!-- SD card & phone storage settings screen, label for check box to erase all the carriers information on the embedded SIM card [CHAR LIMIT=30] -->
- <string name="erase_esim_storage">Erase eSIM</string>
- <!-- SD card & phone storage settings screen, description for check box to erase eSIMs for default devices [CHAR LIMIT=NONE] -->
- <string name="erase_esim_storage_description" product="default">Erase all eSIMs on the phone. This will not cancel your mobile service plan.</string>
- <!-- SD card & phone storage settings screen, description for check box to erase eSIMs for tablets [CHAR LIMIT=NONE] -->
- <string name="erase_esim_storage_description" product="tablet">Erase all eSIMs on the tablet. This will not cancel your mobile service plan.</string>
<!-- SD card & phone storage settings screen, button on screen after user selects Factory data reset -->
<string name="master_clear_button_text" product="tablet">Erase all data</string>
<!-- SD card & phone storage settings screen, button on screen after user selects Factory data reset -->
@@ -3736,13 +3774,25 @@
<string name="location_app_level_permissions">App permission</string>
<!-- Summary for app permission on Location settings page when location is off [CHAR LIMIT=NONE] -->
<string name="location_app_permission_summary_location_off">Location is off</string>
- <!-- Summary for Location settings when location is on, explaining how many apps have location permission [CHAR LIMIT=NONE]-->
+ <!--
+ Summary for Location settings when location is on, explaining how many apps have unlimited
+ location permission.
+
+ "Unlimited access" means the app can access the device location even when it's not being used
+ (on background), while "limited" means the app can only access the device location when the user
+ is using it (foreground only).
+
+ Please note that the distinction between singular and plural of this sentence only depends on
+ the quantity of "background_location_app_count" ("has" vs "have"). The quantity of
+ "total_location_app_count" is almost always greater than 1, so "apps" is always in plural form.
+
+ [CHAR LIMIT=NONE]-->
<plurals name="location_app_permission_summary_location_on">
<item quantity="one">
<xliff:g id="background_location_app_count">%1$d</xliff:g>
of
<xliff:g id="total_location_app_count">%2$d</xliff:g>
- app has unlimited access</item>
+ apps has unlimited access</item>
<item quantity="other">
<xliff:g id="background_location_app_count">%1$d</xliff:g>
of
@@ -3753,8 +3803,12 @@
<string name="location_category_recent_location_access">Recent location access</string>
<!-- [CHAR LIMIT=30] Location settings screen, button to bring the user to view the details of recent location access -->
<string name="location_recent_location_access_view_details">View details</string>
- <!-- Location settings screen, displayed when there's no recent app accessing location -->
+ <!-- Location settings screen, displayed when there's no recent app accessing location
+ (for TV) [CHAR LIMIT=100] -->
<string name="location_no_recent_apps">No apps have requested location recently</string>
+ <!-- Location settings screen, displayed when there's no recent app accessing location
+ (for phones and tablets) [CHAR LIMIT=100] -->
+ <string name="location_no_recent_accesses">No apps recently accessed location</string>
<!-- [CHAR LIMIT=30] Location settings screen, recent location requests high battery use-->
<string name="location_high_battery_use">High battery use</string>
<!-- [CHAR LIMIT=30] Location settings screen, recent location requests low battery use-->
@@ -3904,8 +3958,6 @@
<string name="lockpassword_choose_your_pattern_header_for_face">To use face authentication, set pattern</string>
<!-- Header on first screen of choose password/PIN as backup for face authentication flow. If this string cannot be translated in under 40 characters, please translate "Set face authentication backup" [CHAR LIMIT=40] -->
<string name="lockpassword_choose_your_pin_header_for_face">To use face authentication, set PIN</string>
- <!-- Message on Wi-Fi Sharing screen [CHAR LIMIT=NONE] -->
- <string name="wifi_sharing_message">Your Wi\u2011Fi name and password for \"<xliff:g id="SSID" example="GoogleGuest">%1$s</xliff:g>\" will be shared.</string>
<!-- Message to be used to explain the user that he needs to enter his pattern to continue a
particular operation. [CHAR LIMIT=70]-->
@@ -4155,6 +4207,8 @@
<string name="uninstall_all_users_text">Uninstall for all users</string>
<!-- [CHAR LIMIT=NONE] Manage applications, individual application info screen, button label under Storage heading. Button to install an application for the user. -->
<string name="install_text">Install</string>
+ <!-- [CHAR LIMIT=25] Manage applications, individual application info screen, button label under Storage heading. Button to disable an existing application. -->
+ <string name="disable_text">Disable</string>
<!-- [CHAR LIMIT=25] Manage applications, individual application info screen, button label under Storage heading. Button to re-enable an existing application. -->
<string name="enable_text">Enable</string>
<!-- Manage applications, individual application info screen, button label under Storage heading. Button to clear all data associated with tis app (for example, remove all cached emails for an Email app) -->
@@ -4293,9 +4347,9 @@
<!-- Manage applications. application installation location summary -->
<string name="app_install_location_summary">Change the preferred installation location for new apps</string>
<!-- [CHAR LIMIT=30] Manage applications, label for option to disable app -->
- <string name="app_disable_dlg_positive">Uninstall app</string>
- <!-- [CHAR LIMIT=200] Manage applications, text for dialog when disabling apps -->
- <string name="app_disable_dlg_text">If you uninstall this app, Android and other apps may no longer function as intended.</string>
+ <string name="app_disable_dlg_positive">Disable app</string>
+ <!-- [CHAR LIMIT=NONE] Manage applications, text for dialog when disabling apps -->
+ <string name="app_disable_dlg_text">If you disable this app, Android and other apps may no longer function as intended. Keep in mind, you can\u2019t delete this app since it came pre-installed on your device. By disabling, you turn this app off and hide it on your device.</string>
<!-- [CHAR LIMIT=30] Manage applications, title for dialog when disabling notifications for an app -->
<string name="app_disable_notifications_dlg_title">Turn off notifications?</string>
<!-- Manage applications, individual application info screen, section heading for information about the app installer [CHAR_LIMIT=25] -->
@@ -4497,8 +4551,8 @@
<string name="keyboard_shortcuts_helper">Keyboard shortcuts helper</string>
<!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] -->
<string name="keyboard_shortcuts_helper_summary">Display available shortcuts</string>
- <!-- Title for the 'Work profile input & assistance' preference category inside Languages and inputs'. [CHAR LIMIT=50] -->
- <string name="language_and_input_for_work_category_title">Work profile input & assistance</string>
+ <!-- Title for the 'Work profile keyboards & tools' preference category inside Languages and inputs'. [CHAR LIMIT=50] -->
+ <string name="language_and_input_for_work_category_title">Work profile keyboards & tools</string>
<!-- Title for the 'Virtual keyboards for work' preference. [CHAR LIMIT=45] -->
<string name="virtual_keyboards_for_work_title">Virtual keyboard for work</string>
@@ -5222,14 +5276,16 @@
<string name="battery_tip_high_usage_title" product="tablet">Tablet used more than usual</string>
<!-- Title for the battery high usage tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_high_usage_title" product="device">Device used more than usual</string>
- <!-- Summary for the battery high usage tip, which presents battery may run out soon [CHAR LIMIT=NONE] -->
- <string name="battery_tip_high_usage_summary">Battery may run out soon</string>
+ <!-- Summary for the battery high usage tip, which presents battery may run out earlier [CHAR LIMIT=NONE] -->
+ <string name="battery_tip_high_usage_summary">Battery may run out earlier than usual</string>
<!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
- <string name="battery_tip_dialog_message" product="default">Your phone has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
+ <string name="battery_tip_dialog_message" product="default">Your phone has been used more than usual. Your battery may run out sooner than expected.\n\nMost used apps since full charge:</string>
<!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
- <string name="battery_tip_dialog_message" product="tablet">Your tablet has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
+ <string name="battery_tip_dialog_message" product="tablet">Your tablet has been used more than usual. Your battery may run out sooner than expected.\n\nMost used apps since full charge:</string>
<!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
- <string name="battery_tip_dialog_message" product="device">Your device has been used more than usual. Your battery may run out sooner than expected.\n\nTop <xliff:g id="number">%1$d</xliff:g> apps you used since full charge:</string>
+ <string name="battery_tip_dialog_message" product="device">Your device has been used more than usual. Your battery may run out sooner than expected.\n\nMost used apps since full charge:</string>
+ <!-- Footer message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
+ <string name="battery_tip_dialog_message_footer">Includes high-power background activity</string>
<!-- Title for restricted app preference, showing how many app need to be restricted [CHAR LIMIT=NONE] -->
<plurals name="battery_tip_restrict_title">
<item quantity="one">Restrict %1$d app</item>
@@ -5612,9 +5668,9 @@
<string name="battery_saver_turn_on_automatically_pct">at <xliff:g id="percent">%1$s</xliff:g> battery</string>
<!-- [CHAR_LIMIT=40] Battery percentage: Title -->
- <string name="battery_info">Battery Information</string>
+ <string name="battery_percentage">Battery percentage</string>
<!-- [CHAR_LIMIT=NONE] Battery percentage: Description for preference -->
- <string name="battery_info_description">Show percentage and time left before charge is needed</string>
+ <string name="battery_percentage_description">Show battery percentage in status bar</string>
<!-- Process Stats strings -->
<skip />
@@ -7022,7 +7078,7 @@
handle actions such as open web page, making phone calls, default SMS apps [CHAR LIMIT=40]-->
<string name="app_default_dashboard_title">Default apps</string>
<!-- Summary text for system preference tile, showing important setting items under system setting [CHAR LIMIT=NONE]-->
- <string name="system_dashboard_summary">Languages, time, backup, updates</string>
+ <string name="system_dashboard_summary">Languages, gestures, time, backup</string>
<!-- Search strings -->
<!-- Text to describe the search results fragment title [CHAR LIMIT=16] -->
@@ -7124,7 +7180,7 @@
<string name="keywords_screen_timeout">screen, lock time, timeout, lockscreen</string>
<!-- List of synonyms for Storage settings (everything related to storage on the device), used to match in settings search [CHAR LIMIT=NONE] -->
- <string name="keywords_storage_settings">memory, data, delete, clear, free, space</string>
+ <string name="keywords_storage_settings">memory, cache, data, delete, clear, free, space</string>
<!-- List of synonyms for the Bluetooth setting, used to match in settings search [CHAR LIMIT=NONE] -->
<string name="keywords_bluetooth_settings">connected, device, headphones, headset, speaker, wireless, pair, earbuds, music, media </string>
@@ -7641,6 +7697,18 @@
<string name="profile_section_header">Work notifications</string>
<!-- Configure Notifications: setting title [CHAR LIMIT=80] -->
+ <string name="asst_capability_prioritizer_title">Automatic Notification Prioritizer</string>
+
+ <!-- Configure Notifications: setting summary [CHAR LIMIT=200] -->
+ <string name="asst_capability_prioritizer_summary">Automatically silence and demote less important notifications</string>
+
+ <!-- Configure Notifications: setting title [CHAR LIMIT=80] -->
+ <string name="asst_capabilities_actions_replies_title">Smart actions and replies</string>
+
+ <!-- Configure Notifications: setting summary [CHAR LIMIT=200] -->
+ <string name="asst_capabilities_actions_replies_summary">Automatically add contextual notification actions and quick replies to notifications</string>
+
+ <!-- Configure Notifications: setting title [CHAR LIMIT=80] -->
<string name="hide_silent_icons_title">Hide silent notification status icons</string>
<!-- Configure Notifications: setting summary [CHAR LIMIT=NONE] -->
@@ -7650,7 +7718,23 @@
<string name="notification_badging_title">Allow notification dots</string>
<!-- Configure Notifications: Title for the notification bubbles option. [CHAR LIMIT=60] -->
- <string name="notification_bubbles_title">Allow notification bubbles</string>
+ <string name="notification_bubbles_title">Allow bubbles</string>
+ <!-- Configure Notifications: Summary for the notification bubbles option. [CHAR LIMIT=NONE] -->
+ <string name="notification_bubbles_summary">Allow apps to show some notifications as bubbles</string>
+ <!-- Feature education for bubbles. [CHAR LIMIT=NONE] -->
+ <string name="bubbles_feature_education">Some notifications and other content can appear as bubbles on the screen. To open a bubble, tap it. To dismiss it, drag it down the screen.</string>
+ <!-- Title for the toggle shown on the app-level bubbles page [CHAR LIMIT=60] -->
+ <string name="bubbles_app_toggle_title">Bubbles</string>
+ <!-- Description for the toggle shown on the app-level bubbles page [CHAR LIMIT=NONE] -->
+ <string name="bubbles_app_toggle_summary">Allow <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> to show some notifications as bubbles</string>
+ <!-- Title of the dialog shown when the user has disabled bubbles at the feature level but tries to enable it for an app. [CHAR LIMIT=NONE] -->
+ <string name="bubbles_feature_disabled_dialog_title">Turn on bubbles</string>
+ <!-- Description of the dialog shown when the user has disabled bubbles at the feature level but tries to enable it for an app. [CHAR LIMIT=NONE] -->
+ <string name="bubbles_feature_disabled_dialog_text">Before you can turn on bubbles for this app, you need to turn on bubbles for your device</string>
+ <!-- Button of the dialog shown when the user has disabled bubbles at the feature level but tries to enable it for an app. [CHAR LIMIT=60]-->
+ <string name="bubbles_feature_disabled_button_go_to_bubbles">Go to Bubbles</string>
+ <!-- Button to cancel out of the dialog shown when the user has disabled bubbles at the feature level but tries to enable it for an app. [CHAR LIMIT=60] -->
+ <string name="bubbles_feature_disabled_button_cancel">Cancel</string>
<!-- Configure notifications: title for swipe direction [CHAR LIMIT=60] -->
<string name="swipe_direction_title">Swipe actions</string>
@@ -7753,7 +7837,7 @@
summary on the channel page-->
<!-- [CHAR LIMIT=100] Notification Importance title: min importance level title -->
- <string name="notification_importance_min_title">Low</string>
+ <string name="notification_importance_min_title">Minimize</string>
<!-- [CHAR LIMIT=100] Notification Importance title: low importance level title -->
<string name="notification_importance_low_title">Medium</string>
@@ -7762,7 +7846,16 @@
<string name="notification_importance_default_title">High</string>
<!-- [CHAR LIMIT=100] Notification Importance title: high importance level title -->
- <string name="notification_importance_high_title">Urgent</string>
+ <string name="notification_importance_high_title">Pop on screen</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance title -->
+ <string name="notification_block_title">Block</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance title -->
+ <string name="notification_silence_title">Show silently</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance title -->
+ <string name="notification_alert_title">Alert</string>
<!-- [CHAR LIMIT=40] Notification importance title. This setting controls how notifications in older apps may alert the user (eg, sound, visual, vibrate). -->
<string name="allow_interruption">Allow interruptions</string>
@@ -7902,6 +7995,9 @@
<!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] -->
<string name="manage_zen_access_title">Do Not Disturb access</string>
+ <!-- Button title that grants 'Do Not Disturb' permission to an app [CHAR_LIMIT=60]-->
+ <string name="zen_access_detail_switch">Allow Do Not Disturb</string>
+
<!-- Sound & notification > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
<string name="zen_access_empty_text">No installed apps have requested Do Not Disturb access</string>
@@ -9288,6 +9384,12 @@
<!-- Summary of condition that night display is on (renamed "Night Light" with title caps). [CHAR LIMIT=NONE] -->
<string name="condition_night_display_summary">Screen tinted amber</string>
+ <!-- Title of condition that gray scale is on [CHAR LIMIT=NONE] -->
+ <string name="condition_grayscale_title">Greyscale</string>
+
+ <!-- Summary of condition that gray scale is on [CHAR LIMIT=NONE] -->
+ <string name="condition_grayscale_summary">Display only in grey color</string>
+
<!-- Summary for the condition section on the dashboard, representing number of conditions. [CHAR LIMIT=10] -->
<string name="condition_summary" translatable="false"><xliff:g name="count" example="3">%1$d</xliff:g></string>
@@ -9726,16 +9828,21 @@
<!-- [CHAR LIMIT=25] Title of developer tile to toggle winscope trace -->
<string name="winscope_trace_quick_settings_title">Winscope Trace</string>
+ <!-- [CHAR LIMIT=25] Title of developer tile to toggle sensors -->
+ <string name="sensors_off_quick_settings_title">Sensors Off</string>
+
<!-- [CHAR LIMIT=60] Title of work profile setting page -->
<string name="managed_profile_settings_title">Work profile settings</string>
<!-- [CHAR LIMIT=60] The preference title for enabling cross-profile remote contact search -->
<string name="managed_profile_contact_search_title">Contact search</string>
<!-- [CHAR LIMIT=NONE] The preference summary for enabling cross-profile remote contact search -->
<string name="managed_profile_contact_search_summary">Allow contact searches by your organization to identify callers and contacts</string>
- <!-- [CHAR LIMIT=60] The preference title for enabling cross profile calendar sync -->
+ <!-- [CHAR LIMIT=NONE] Settings label. This setting lets the user show their work events on their personal calendar. The adjective 'Cross-profile' is referring to the work and personal profiles a user has on their phone. -->
<string name="cross_profile_calendar_title">Cross-profile calendar</string>
- <!-- [CHAR LIMIT=NONE] The preference summary for enabling cross profile calendar sync -->
- <string name="cross_profile_calendar_summary">Show work events on personal calendar</string>
+ <!-- [CHAR LIMIT=NONE] Setting description. If the user turns on this setting, they can see their work events on their personal calendar. -->
+ <string name="cross_profile_calendar_summary">Show work events on your personal calendar</string>
+ <!-- [CHAR LIMIT=NONE] The preference summary when cross-profile calendar is restricted. -->
+ <string name="cross_profile_calendar_restricted_summary">Your organization doesn\u2019t allow personal apps to access your work calendar</string>
<!-- Time in hours -->
@@ -9849,8 +9956,6 @@
<!-- Preference and settings suggestion title text for display wake-up gesture [CHAR LIMIT=60]-->
<string name="ambient_display_wake_screen_title">Wake up display</string>
- <!-- Summary text for ambient display wake-up gesture [CHAR LIMIT=NONE]-->
- <string name="ambient_display_wake_screen_summary"></string>
<!-- Summary text for ambient display (phone) [CHAR LIMIT=NONE]-->
<string name="ambient_display_pickup_summary" product="default">To check time, notifications, and other info, pick up your phone.</string>
@@ -10543,20 +10648,20 @@
<string name="mobile_network_list_add_more">Add more</string>
<!-- Summary for an item in the page listing multiple mobile service subscriptions, indicating
that service is active and is tied to a physical SIM card [CHAR LIMIT=40] -->
- <string name="mobile_network_active_sim">Active SIM</string>
+ <string name="mobile_network_active_sim">Active / SIM</string>
<!-- Summary for an item in the page listing multiple mobile service subscriptions, indicating
that service is inactive and is tied to a physical SIM card [CHAR LIMIT=40] -->
- <string name="mobile_network_inactive_sim">Inactive SIM</string>
+ <string name="mobile_network_inactive_sim">Inactive / SIM</string>
<!-- Summary for an item in the page listing multiple mobile service subscriptions, indicating
that service is active and is tied to an eSIM profile [CHAR LIMIT=40] -->
- <string name="mobile_network_active_esim">Active eSIM</string>
+ <string name="mobile_network_active_esim">Active / Downloaded SIM</string>
<!-- Summary for an item in the page listing multiple mobile service subscriptions, indicating
that service is inactive and is tied to an eSIM profile [CHAR LIMIT=40] -->
- <string name="mobile_network_inactive_esim">Inactive eSIM</string>
+ <string name="mobile_network_inactive_esim">Inactive / Downloaded SIM</string>
<!-- Title of a dialog that lets a user modify the display name used for a mobile network
subscription in various places in the Settings app. The default name is typically just the
- carrier name, but especially in multi-SIM configurations users may want to use a different
- name. [CHAR LIMIT=40] -->
+ carrier name, but especially in multi-SIM configurations users may want to use a different
+ name. [CHAR LIMIT=40] -->
<string name="mobile_network_sim_name">SIM name</string>
<!-- Label on the confirmation button of a dialog that lets a user set the display name of a
mobile network subscription [CHAR LIMIT=20] -->
@@ -10568,6 +10673,35 @@
disabling/enabling a SIM. The SIM is disabled in this state. [CHAR LIMIT=40] -->
<string name="mobile_network_use_sim_off">Off</string>
+ <!-- Mobile network details page. Label for an option that lets the user delete an eSIM from
+ the device. [CHAR LIMIT=60] -->
+ <string name="mobile_network_erase_sim">Erase SIM</string>
+ <!-- Title of a confirmation dialog for deleting an eSIM from the device.
+ [CHAR LIMIT=60] -->
+ <string name="mobile_network_erase_sim_dialog_title">Erase this downloaded SIM?</string>
+ <!-- Body text of of a confirmation dialog for deleting an eSIM from the device.
+ [CHAR LIMIT=NONE] -->
+ <string name="mobile_network_erase_sim_dialog_body">
+ Erasing this SIM removes <xliff:g id="carrier" example="Verizon">%1$s</xliff:g> service
+ from this device.\n\nService for <xliff:g id="carrier" example="Verizon">%2$s</xliff:g>
+ won\u2019t be canceled.
+ </string>
+ <!-- OK button on a confirmation dialog for deleting an eSIM from the device.
+ [CHAR LIMIT=30] -->
+ <string name="mobile_network_erase_sim_dialog_ok">Erase</string>
+ <!-- Body text of the progress dialog we show while deleting an eSIM from the device.
+ [CHAR LIMIT=NONE] -->
+ <string name="mobile_network_erase_sim_dialog_progress">Erasing SIM\u2026</string>
+
+ <!-- Title of a dialog indicating that an error ocurred when deleting an eSIM from the device
+ [CHAR LIMIT=60] -->
+ <string name="mobile_network_erase_sim_error_dialog_title">Can\u2019t erase SIM</string>
+ <!-- Body of a dialog indicating that an error ocurred when deleting an eSIM from the device
+ [CHAR LIMIT=NONE] -->
+ <string name="mobile_network_erase_sim_error_dialog_body">
+ This SIM can\u2019t be erased due to an error.\n\nRestart your device and try again.
+ </string>
+
<!-- Title for preferred network type [CHAR LIMIT=NONE] -->
<string name="preferred_network_mode_title">Preferred network type</string>
<!-- Summary for preferred network type [CHAR LIMIT=NONE] -->
@@ -10690,6 +10824,10 @@
<string name="contextual_card_dismiss_keep">Keep</string>
<!-- String for contextual card dismissal [CHAR LIMIT=NONE] -->
<string name="contextual_card_dismiss_confirm_message">Remove this suggestion?</string>
+ <!-- String for snack bar message when users swipe to dismiss a contextual card [CHAR LIMIT=60]-->
+ <string name="contextual_card_removed_message">Suggestion removed</string>
+ <!-- Label for snack bar action allowing users to undo contextual card dismissal [CHAR LIMIT=30]-->
+ <string name="contextual_card_undo_dismissal_text">Undo</string>
<!-- Summary for low storage slice. [CHAR LIMIT=NONE] -->
<string name="low_storage_summary">Storage is low. <xliff:g id="percentage" example="54%">%1$s</xliff:g> used - <xliff:g id="free_space" example="32GB">%2$s</xliff:g> free</string>
@@ -10719,12 +10857,12 @@
<!-- Summary for the accessibility usage preference in the Privacy page. [CHAR LIMIT=NONE] -->
<plurals name="accessibility_usage_summary">
- <item quantity="one">1 service has full access to your device</item>
- <item quantity="other"><xliff:g id="service_count">%1$d</xliff:g> services have full access to your device</item>
+ <item quantity="one">1 app has full access to your device</item>
+ <item quantity="other"><xliff:g id="service_count">%1$d</xliff:g> apps have full access to your device</item>
</plurals>
<!-- Title for notification channel slice. [CHAR LIMIT=NONE] -->
- <string name="manage_app_notification">Manage <xliff:g id="app_name" example="Settings">%1$s</xliff:g> Notifications</string>
+ <string name="manage_app_notification">Manage <xliff:g id="app_name" example="Settings">%1$s</xliff:g> notifications</string>
<!-- Title for no suggested app in notification channel slice. [CHAR LIMIT=NONE] -->
<string name="no_suggested_app">No suggested application</string>
<!-- Summary for the channels count is equal or less than 3 in notification channel slice. [CHAR LIMIT=NONE] -->
@@ -10734,14 +10872,25 @@
</plurals>
<!-- Summary for the channels count is more than 3 in notification channel slice. [CHAR LIMIT=NONE] -->
<string name="notification_many_channel_count_summary"><xliff:g id="notification_channel_count" example="4">%1$d</xliff:g> notification channels. Tap to manage all.</string>
+ <!-- Summary for recently installed app in contextual notification channel slice. [CHAR LIMIT=NONE] -->
+ <string name="recently_installed_app">You recently installed this app.</string>
<!-- Title for the Switch output dialog (settings panel) with media related devices [CHAR LIMIT=50] -->
<string name="media_output_panel_title">Switch output</string>
<!-- Summary for represent which device is playing media [CHAR LIMIT=NONE] -->
<string name="media_output_panel_summary_of_playing_device">Currently playing on <xliff:g id="device_name" example="Bose headphone">%1$s</xliff:g></string>
+ <!-- Label for the title on wfc disclaimer fragment. [CHAR LIMIT=40] -->
+ <string name="wfc_disclaimer_title_text">Important information</string>
+
+ <!-- Label for the agree button on wfc disclaimer fragment. [CHAR LIMIT=30] -->
+ <string name="wfc_disclaimer_agree_button_text">CONTINUE</string>
+
+ <!-- Label for the disagree button on wfc disclaimer fragment. [CHAR LIMIT=30] -->
+ <string name="wfc_disclaimer_disagree_text">NO THANKS</string>
+
<!-- Message for forget passpoint dialog [CHAR LIMIT=none] -->
- <string name="forget_passpoint_dialog_message">Your subscription with this provider may be cancelled. Recurring subscriptions will not be cancelled. For more information, check with your provider.</string>
+ <string name="forget_passpoint_dialog_message">You may lose access to any remaining time or data. Check with your provider before removing.</string>
<!-- Keywords for Content Capture feature [CHAR_LIMIT=32] -->
<string name="keywords_content_capture">content capture</string>
@@ -10749,5 +10898,4 @@
<string name="content_capture">Content Capture</string>
<!-- Description of the 'Content Capture' feature toggle in the Settings -> Privacy screen [CHAR LIMIT=NONE]-->
<string name="content_capture_summary">Allow Android to save information seen on your screen or heard in video or audio content. Android makes helpful suggestions based on your device activity.</string>
-
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 92a4098..8fde9a0 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -520,4 +520,17 @@
<!-- Padding between content and the start icon is 8dp. -->
<item name="contentStartPadding">8dp</item>
</style>
+
+ <style name="DisclaimerPositiveButton" parent="@style/SudGlifButton.Primary">
+ <item name="android:layout_margin">16dp</item>
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:paddingEnd">8dp</item>
+ </style>
+
+ <style name="DisclaimerNegativeButton" parent="@style/SudGlifButton.Secondary">
+ <item name="android:layout_margin">16dp</item>
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:paddingEnd">8dp</item>
+ </style>
+
</resources>
diff --git a/res/xml/app_info_settings.xml b/res/xml/app_info_settings.xml
index 506686a..435a7ef 100644
--- a/res/xml/app_info_settings.xml
+++ b/res/xml/app_info_settings.xml
@@ -55,7 +55,7 @@
<Preference
android:key="storage_settings"
- android:title="@string/storage_settings"
+ android:title="@string/storage_settings_for_app"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppStoragePreferenceController" />
diff --git a/res/xml/app_notification_settings.xml b/res/xml/app_notification_settings.xml
index 54d6fe7..faad649 100644
--- a/res/xml/app_notification_settings.xml
+++ b/res/xml/app_notification_settings.xml
@@ -61,7 +61,7 @@
android:order="1001"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<com.android.settingslib.RestrictedSwitchPreference
- android:key="bubble"
+ android:key="bubble_pref"
android:title="@string/notification_bubbles_title"
android:order="1002"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
diff --git a/res/xml/channel_notification_settings.xml b/res/xml/channel_notification_settings.xml
index 94a2cdb..3158819 100644
--- a/res/xml/channel_notification_settings.xml
+++ b/res/xml/channel_notification_settings.xml
@@ -25,11 +25,6 @@
android:order="1"
android:layout="@layout/settings_entity_header" />
- <com.android.settingslib.widget.LayoutPreference
- android:key="block"
- android:order="2"
- android:layout="@layout/styled_switch_bar" />
-
<!-- Importance toggle -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="allow_sound"
@@ -38,10 +33,23 @@
android:summary="@string/allow_interruption_summary" />
<!-- Importance -->
- <com.android.settings.RestrictedListPreference
+ <com.android.settings.notification.ImportancePreference
android:key="importance"
- android:order="10"
- android:title="@string/notification_importance_title" />
+ android:order="4"
+ android:title="@string/notification_importance_title"
+ settings:allowDividerBelow="true"/>
+
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="min_importance"
+ android:order="5"
+ settings:allowDividerAbove="true"
+ android:title="@string/notification_importance_min_title"/>
+
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="high_importance"
+ android:order="6"
+ settings:allowDividerAbove="true"
+ android:title="@string/notification_importance_high_title"/>
<PreferenceCategory
android:key="channel_advanced"
@@ -87,7 +95,7 @@
settings:restrictedSwitchSummary="@string/enabled_by_admin"/>
<com.android.settingslib.RestrictedSwitchPreference
- android:key="bubble"
+ android:key="bubble_pref"
android:title="@string/notification_bubbles_title"
android:order="16"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
@@ -113,6 +121,7 @@
<com.android.settings.notification.NotificationFooterPreference
android:key="block_desc"
- android:order="110"/>
+ android:order="110"
+ settings:allowDividerAbove="false"/>
</PreferenceScreen>
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index 38fa060..e48ddc1 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -27,6 +27,18 @@
settings:controller="com.android.settings.notification.NotificationAssistantPreferenceController"/>
<SwitchPreference
+ android:key="asst_capability_prioritizer"
+ android:title="@string/asst_capability_prioritizer_title"
+ android:summary="@string/asst_capability_prioritizer_summary"
+ settings:controller="com.android.settings.notification.AssistantCapabilityPreferenceController" />
+
+ <SwitchPreference
+ android:key="asst_capabilities_actions_replies"
+ android:title="@string/asst_capabilities_actions_replies_title"
+ android:summary="@string/asst_capabilities_actions_replies_summary"
+ settings:controller="com.android.settings.notification.AssistantCapabilityPreferenceController" />
+
+ <SwitchPreference
android:key="hide_silent_icons"
android:title="@string/hide_silent_icons_title"
android:summary="@string/hide_silent_icons_summary"
@@ -61,6 +73,7 @@
<SwitchPreference
android:key="notification_bubbles"
android:title="@string/notification_bubbles_title"
+ android:summary="@string/notification_bubbles_summary"
settings:controller="com.android.settings.notification.BubbleNotificationPreferenceController"/>
<!-- Pulse notification light -->
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 032f622..a9af2d4 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -113,7 +113,8 @@
<Preference
android:key="quick_settings_tiles"
android:title="@string/quick_settings_developer_tiles"
- android:fragment="com.android.settings.development.qstile.DevelopmentTileConfigFragment" />
+ android:fragment="com.android.settings.development.qstile.DevelopmentTileConfigFragment"
+ settings:searchable="false" />
<!-- Configure trust agent behavior -->
<SwitchPreference
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index 0f4c1d3..91fe656 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -20,7 +20,7 @@
android:key="display_settings_screen"
android:title="@string/display_settings"
settings:keywords="@string/keywords_display"
- settings:initialExpandedChildrenCount="4">
+ settings:initialExpandedChildrenCount="5">
<com.android.settingslib.RestrictedPreference
android:key="brightness"
@@ -57,6 +57,15 @@
android:targetClass="@string/config_wallpaper_picker_class" />
</com.android.settingslib.RestrictedPreference>
+ <ListPreference
+ android:key="dark_ui_mode"
+ android:title="@string/dark_ui_mode"
+ android:dialogTitle="@string/dark_ui_mode_title"
+ android:entries="@array/dark_ui_mode_entries"
+ android:entryValues="@array/dark_ui_mode_values"
+ 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 -->
<com.android.settings.display.TimeoutListPreference
android:key="screen_timeout"
@@ -136,15 +145,6 @@
android:summary="@string/tap_to_wake_summary" />
<ListPreference
- android:key="dark_ui_mode"
- android:title="@string/dark_ui_mode"
- android:dialogTitle="@string/dark_ui_mode_title"
- android:entries="@array/dark_ui_mode_entries"
- android:entryValues="@array/dark_ui_mode_values"
- settings:keywords="@string/keywords_dark_ui_mode"
- settings:controller="com.android.settings.display.DarkUIPreferenceController" />
-
- <ListPreference
android:key="theme"
android:title="@string/device_theme"
android:summary="@string/summary_placeholder" />
diff --git a/res/xml/firmware_version.xml b/res/xml/firmware_version.xml
index 2914ef1..75336ce 100644
--- a/res/xml/firmware_version.xml
+++ b/res/xml/firmware_version.xml
@@ -68,6 +68,7 @@
android:title="@string/build_number"
android:summary="@string/summary_placeholder"
settings:enableCopying="true"
+ settings:allowDynamicSummaryInSlice="true"
settings:controller="com.android.settings.deviceinfo.firmwareversion.SimpleBuildNumberPreferenceController"/>
</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/gestures.xml b/res/xml/gestures.xml
index f705630..8515bd7 100644
--- a/res/xml/gestures.xml
+++ b/res/xml/gestures.xml
@@ -28,12 +28,6 @@
settings:controller="com.android.settings.gestures.AssistGestureSettingsPreferenceController" />
<Preference
- android:key="gesture_wake_screen_input_summary"
- android:title="@string/ambient_display_wake_screen_title"
- android:fragment="com.android.settings.gestures.WakeScreenGestureSettings"
- settings:controller="com.android.settings.gestures.WakeScreenGesturePreferenceController" />
-
- <Preference
android:key="gesture_swipe_down_fingerprint_input_summary"
android:title="@string/fingerprint_swipe_for_notifications_title"
android:fragment="com.android.settings.gestures.SwipeToNotificationSettings"
diff --git a/res/xml/mobile_network_list.xml b/res/xml/mobile_network_list.xml
index b72540f..c2baf46 100644
--- a/res/xml/mobile_network_list.xml
+++ b/res/xml/mobile_network_list.xml
@@ -16,11 +16,13 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="mobile_network_list_screen"
android:title="@string/network_settings_title">
<Preference
android:key="add_more"
+ settings:isPreferenceVisible="false"
android:title="@string/mobile_network_list_add_more"
android:icon="@drawable/ic_menu_add"
android:order="100" >
diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml
index 83bf5c4..bc498d2 100644
--- a/res/xml/my_device_info.xml
+++ b/res/xml/my_device_info.xml
@@ -115,6 +115,7 @@
android:title="@string/status_imei"
settings:keywords="@string/keywords_imei_info"
android:summary="@string/summary_placeholder"
+ settings:allowDynamicSummaryInSlice="true"
settings:controller="com.android.settings.deviceinfo.imei.ImeiInfoPreferenceController"/>
<!-- Android version -->
diff --git a/res/xml/network_and_internet_v2.xml b/res/xml/network_and_internet_v2.xml
index fd29c41..44c3121 100644
--- a/res/xml/network_and_internet_v2.xml
+++ b/res/xml/network_and_internet_v2.xml
@@ -22,6 +22,7 @@
<PreferenceCategory
android:key="multi_network_header"
+ android:title="@string/summary_placeholder"
settings:allowDividerBelow="true"
android:order="-40"
settings:controller="com.android.settings.network.MultiNetworkHeaderController"/>
@@ -79,7 +80,7 @@
settings:useAdminDisabledSummary="true" />
<com.android.settings.datausage.DataSaverPreference
- android:key="restrict_background"
+ android:key="restrict_background_parent_entry"
android:title="@string/data_saver_title"
android:icon="@drawable/ic_settings_data_usage"
android:order="10"
diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml
index 9be0b7d..94ead86 100644
--- a/res/xml/power_usage_summary.xml
+++ b/res/xml/power_usage_summary.xml
@@ -50,8 +50,8 @@
<SwitchPreference
android:key="battery_percentage"
- android:title="@string/battery_info"
- android:summary="@string/battery_info_description"
+ android:title="@string/battery_percentage"
+ android:summary="@string/battery_percentage_description"
settings:controller="com.android.settings.display.BatteryPercentagePreferenceController" />
diff --git a/res/xml/privacy_dashboard_settings.xml b/res/xml/privacy_dashboard_settings.xml
index d2d9e35..f39880c 100644
--- a/res/xml/privacy_dashboard_settings.xml
+++ b/res/xml/privacy_dashboard_settings.xml
@@ -63,12 +63,25 @@
settings:searchable="false"/>
<!-- Content Capture -->
+
+ <!-- NOTE: content capture has a different preference, depending whether or not the
+ ContentCaptureService implementations defines a custom settings activitiy on its manifest.
+ Hence, we show both here, but the controller itself will decide if it's available or not.
+ -->
+
<SwitchPreference
android:key="content_capture"
android:title="@string/content_capture"
android:summary="@string/content_capture_summary"
settings:controller="com.android.settings.privacy.EnableContentCapturePreferenceController"/>
+ <com.android.settings.widget.MasterSwitchPreference
+ android:key="content_capture_custom_settings"
+ android:title="@string/content_capture"
+ android:summary="@string/content_capture_summary"
+ settings:controller="com.android.settings.privacy.EnableContentCaptureWithServiceSettingsPreferenceController">
+ </com.android.settings.widget.MasterSwitchPreference>
+
<!-- Privacy Service -->
<PreferenceCategory
android:key="privacy_services"/>
diff --git a/res/xml/security_lockscreen_settings.xml b/res/xml/security_lockscreen_settings.xml
index 6833922..611d33f 100644
--- a/res/xml/security_lockscreen_settings.xml
+++ b/res/xml/security_lockscreen_settings.xml
@@ -60,12 +60,6 @@
settings:controller="com.android.settings.display.AmbientDisplayAlwaysOnPreferenceController" />
<Preference
- android:key="ambient_display_wake_screen"
- android:title="@string/ambient_display_wake_screen_title"
- android:fragment="com.android.settings.gestures.WakeScreenGestureSettings"
- settings:controller="com.android.settings.gestures.WakeScreenGesturePreferenceController" />
-
- <Preference
android:key="ambient_display_tap"
android:title="@string/ambient_display_tap_screen_title"
android:fragment="com.android.settings.gestures.TapScreenGestureSettings"
diff --git a/res/xml/single_choice_list_item_2.xml b/res/xml/single_choice_list_item_2.xml
new file mode 100644
index 0000000..ca80f44
--- /dev/null
+++ b/res/xml/single_choice_list_item_2.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/select_dialog_padding_start"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:orientation="horizontal"
+ android:descendantFocusability="blocksDescendants">
+
+ <RadioButton
+ android:id="@+id/radio"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:clickable="false" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginStart="@dimen/select_dialog_item_margin_start"
+ android:layout_gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@+id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/select_dialog_summary_padding_bottom"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
index 81a0453..57bee1f 100644
--- a/res/xml/sound_settings.xml
+++ b/res/xml/sound_settings.xml
@@ -41,7 +41,7 @@
settings:controller="com.android.settings.notification.MediaVolumePreferenceController"/>
<!-- Media output switcher -->
- <ListPreference
+ <Preference
android:key="media_output"
android:title="@string/media_output_title"
android:dialogTitle="@string/media_output_title"
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 05f4a81..9be01f9 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -53,7 +53,7 @@
android:key="zen_access"
android:title="@string/manage_zen_access_title"
android:fragment="com.android.settings.notification.ZenAccessSettings"
- settings:controller="com.android.settings.applications.specialaccess.ZenAccessController" />
+ settings:controller="com.android.settings.applications.specialaccess.zenaccess.ZenAccessController" />
<Preference
android:key="write_settings_apps"
diff --git a/res/xml/wake_screen_gesture_settings.xml b/res/xml/wake_screen_gesture_settings.xml
deleted file mode 100644
index 7bcb2e9..0000000
--- a/res/xml/wake_screen_gesture_settings.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:key="gesture_wake_screen_screen"
- android:title="@string/ambient_display_wake_screen_title">
-
- <com.android.settings.widget.VideoPreference
- android:key="gesture_wake_screen_video"
- app:animation="@raw/gesture_ambient_wake_screen"
- app:preview="@drawable/gesture_ambient_wake_screen" />
-
- <SwitchPreference
- android:key="gesture_wake_screen"
- android:title="@string/ambient_display_wake_screen_title"
- android:summary="@string/ambient_display_wake_screen_summary"
- app:keywords="@string/keywords_gesture"
- app:controller="com.android.settings.gestures.WakeScreenGesturePreferenceController"
- app:allowDividerAbove="true" />
-
-</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/wifi_calling_settings.xml b/res/xml/wifi_calling_settings.xml
index 0adb1e8..0276bdb 100644
--- a/res/xml/wifi_calling_settings.xml
+++ b/res/xml/wifi_calling_settings.xml
@@ -15,24 +15,27 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="wifi_calling_settings"
android:title="@string/wifi_calling_settings_title">
- <ListPreference
+ <com.android.settings.wifi.calling.ListWithEntrySummaryPreference
android:key="wifi_calling_mode"
android:title="@string/wifi_calling_mode_title"
android:summary="@string/wifi_calling_mode_title"
android:entries="@array/wifi_calling_mode_choices"
android:entryValues="@array/wifi_calling_mode_values"
- android:dialogTitle="@string/wifi_calling_mode_dialog_title" />
+ android:dialogTitle="@string/wifi_calling_mode_dialog_title"
+ settings:entrySummaries="@array/wifi_calling_mode_summaries" />
- <ListPreference
+ <com.android.settings.wifi.calling.ListWithEntrySummaryPreference
android:key="wifi_calling_roaming_mode"
android:title="@string/wifi_calling_roaming_mode_title"
android:summary="@string/wifi_calling_roaming_mode_summary"
android:entries="@array/wifi_calling_mode_choices_v2"
android:entryValues="@array/wifi_calling_mode_values"
- android:dialogTitle="@string/wifi_calling_roaming_mode_dialog_title" />
+ android:dialogTitle="@string/wifi_calling_roaming_mode_dialog_title"
+ settings:entrySummaries="@array/wifi_calling_mode_summaries" />
<Preference
android:key="emergency_address_key"
diff --git a/res/xml/wifi_network_details_fragment.xml b/res/xml/wifi_network_details_fragment.xml
index 7c542d8..8979efc 100644
--- a/res/xml/wifi_network_details_fragment.xml
+++ b/res/xml/wifi_network_details_fragment.xml
@@ -26,6 +26,11 @@
android:order="-10000"
settings:allowDividerBelow="true"/>
+ <com.android.settings.datausage.DataUsageSummaryPreference
+ android:key="status_header"
+ android:selectable="false"
+ settings:isPreferenceVisible="false"/>
+
<!-- Buttons -->
<com.android.settingslib.widget.ActionButtonsPreference
android:key="buttons"
diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml
index 6c5e3c4..81e60a2 100644
--- a/res/xml/wifi_tether_settings.xml
+++ b/res/xml/wifi_tether_settings.xml
@@ -23,7 +23,7 @@
settings:searchable="false"
settings:initialExpandedChildrenCount="3">
- <com.android.settings.widget.ValidatedEditTextPreference
+ <com.android.settings.wifi.tether.WifiTetherSsidPreference
android:key="wifi_tether_network_name"
android:title="@string/wifi_hotspot_name_title"
android:summary="@string/summary_placeholder"/>
diff --git a/res/xml/zen_access_permission_details.xml b/res/xml/zen_access_permission_details.xml
new file mode 100644
index 0000000..afa8d80
--- /dev/null
+++ b/res/xml/zen_access_permission_details.xml
@@ -0,0 +1,27 @@
+<?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"
+ android:key="zen_access_permission_detail_settings"
+ android:title="@string/manage_zen_access_title">
+
+ <SwitchPreference
+ android:key="zen_access_switch"
+ android:title="@string/zen_access_detail_switch"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 58bc58c..c78115d 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -326,8 +326,6 @@
if (showWipeEuicc()) {
if (showWipeEuiccCheckbox()) {
- TextView title = mContentView.findViewById(R.id.erase_esim_title);
- title.setText(R.string.erase_esim_storage);
mEsimStorageContainer.setVisibility(View.VISIBLE);
mEsimStorageContainer.setOnClickListener(new View.OnClickListener() {
@Override
diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java
index 0af12fd..6f6e6c4 100644
--- a/src/com/android/settings/RadioInfo.java
+++ b/src/com/android/settings/RadioInfo.java
@@ -457,6 +457,13 @@
imsWfcProvisionedSwitch = (Switch) findViewById(R.id.wfc_provisioned_switch);
eabProvisionedSwitch = (Switch) findViewById(R.id.eab_provisioned_switch);
+ if (!ImsManager.isImsSupportedOnDevice(phone.getContext())) {
+ imsVolteProvisionedSwitch.setVisibility(View.GONE);
+ imsVtProvisionedSwitch.setVisibility(View.GONE);
+ imsWfcProvisionedSwitch.setVisibility(View.GONE);
+ eabProvisionedSwitch.setVisibility(View.GONE);
+ }
+
cbrsDataSwitch = (Switch) findViewById(R.id.cbrs_data_switch);
cbrsDataSwitch.setVisibility(isCbrsSupported() ? View.VISIBLE : View.GONE);
@@ -631,8 +638,10 @@
R.string.radioInfo_menu_viewFDN).setOnMenuItemClickListener(mViewFDNCallback);
menu.add(1, MENU_ITEM_VIEW_SDN, 0,
R.string.radioInfo_menu_viewSDN).setOnMenuItemClickListener(mViewSDNCallback);
- menu.add(1, MENU_ITEM_GET_IMS_STATUS,
- 0, R.string.radioInfo_menu_getIMS).setOnMenuItemClickListener(mGetImsStatus);
+ if (ImsManager.isImsSupportedOnDevice(phone.getContext())) {
+ menu.add(1, MENU_ITEM_GET_IMS_STATUS,
+ 0, R.string.radioInfo_menu_getIMS).setOnMenuItemClickListener(mGetImsStatus);
+ }
menu.add(1, MENU_ITEM_TOGGLE_DATA,
0, R.string.radio_info_data_connection_disable).setOnMenuItemClickListener(mToggleData);
return true;
@@ -1384,6 +1393,9 @@
}
private void updateImsProvisionedState() {
+ if (!ImsManager.isImsSupportedOnDevice(phone.getContext())) {
+ return;
+ }
log("updateImsProvisionedState isImsVolteProvisioned()=" + isImsVolteProvisioned());
//delightful hack to prevent on-checked-changed calls from
//actually forcing the ims provisioning to its transient/current value.
@@ -1559,7 +1571,8 @@
}
private static boolean isDsdsSupported() {
- return TelephonyManager.getDefault().isMultisimSupported();
+ return (TelephonyManager.getDefault().isMultiSimSupported()
+ == TelephonyManager.MULTISIM_ALLOWED);
}
private static boolean isDsdsEnabled() {
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index bd45f9b..5d7dd99 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -213,8 +213,6 @@
mInitiateButton.setOnClickListener(mInitiateListener);
if (showEuiccSettings(getContext())) {
mEsimContainer.setVisibility(View.VISIBLE);
- TextView title = mContentView.findViewById(R.id.erase_esim_title);
- title.setText(R.string.reset_esim_title);
mEsimContainer.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index 7c21b55..72e1919 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -157,20 +157,9 @@
SubscriptionManager.getPhoneId(mSubId)).factoryReset();
restoreDefaultApn(context);
esimFactoryReset(context, context.getPackageName());
- // There has been issues when Sms raw table somehow stores orphan
- // fragments. They lead to garbled message when new fragments come
- // in and combied with those stale ones. In case this happens again,
- // user can reset all network settings which will clean up this table.
- cleanUpSmsRawTable(context);
}
};
- private void cleanUpSmsRawTable(Context context) {
- ContentResolver resolver = context.getContentResolver();
- Uri uri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw/permanentDelete");
- resolver.delete(uri, null, null);
- }
-
@VisibleForTesting
void esimFactoryReset(Context context, String packageName) {
if (mEraseEsim) {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index fac4253..7595657 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -77,6 +77,7 @@
public static class FactoryResetActivity extends SettingsActivity { /* empty */ }
public static class RunningServicesActivity extends SettingsActivity { /* empty */ }
public static class BatterySaverSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class BatterySaverScheduleSettingsActivity extends SettingsActivity { /* empty */ }
public static class AccountSyncSettingsActivity extends SettingsActivity { /* empty */ }
public static class AccountSyncSettingsInAddAccountActivity extends SettingsActivity { /* empty */ }
public static class CryptKeeperSettingsActivity extends SettingsActivity { /* empty */ }
@@ -96,6 +97,7 @@
public static class PictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
public static class AppPictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class ZenAccessDetailSettingsActivity extends SettingsActivity {}
public static class ConditionProviderSettingsActivity extends SettingsActivity { /* empty */ }
public static class UsbSettingsActivity extends SettingsActivity { /* empty */ }
public static class UsbDetailsActivity extends SettingsActivity { /* empty */ }
@@ -159,6 +161,7 @@
public static class WebViewAppPickerActivity extends SettingsActivity { /* empty */ }
public static class AdvancedConnectedDeviceActivity extends SettingsActivity { /* empty */ }
public static class BluetoothDeviceDetailActivity extends SettingsActivity { /* empty */ }
+ public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
// Top level categories for new IA
public static class NetworkDashboardActivity extends SettingsActivity {}
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 541ca3a..001e65b 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -50,6 +50,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.settings.Settings.WifiSettingsActivity;
import com.android.settings.applications.manageapplications.ManageApplications;
+import com.android.settings.backup.BackupSettingsHelper;
+import com.android.settings.backup.UserBackupSettingsActivity;
import com.android.settings.core.OnActivityResultListener;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.core.SubSettingLauncher;
@@ -630,6 +632,12 @@
showDev, isAdmin)
|| somethingChanged;
+ // Enable/disable backup settings depending on whether backup is activated for the user.
+ boolean isBackupActive = new BackupSettingsHelper(this).isBackupServiceActive();
+ somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
+ UserBackupSettingsActivity.class.getName()), isBackupActive, isAdmin)
+ || somethingChanged;
+
somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
Settings.WifiDisplaySettingsActivity.class.getName()),
WifiDisplaySettings.isAvailable(this), isAdmin)
diff --git a/src/com/android/settings/UserCredentialsSettings.java b/src/com/android/settings/UserCredentialsSettings.java
index c30e51d..d322819 100644
--- a/src/com/android/settings/UserCredentialsSettings.java
+++ b/src/com/android/settings/UserCredentialsSettings.java
@@ -154,11 +154,15 @@
dialog.dismiss();
}
};
- if (item.isSystem()) {
- // TODO: a safe means of clearing wifi certificates. Configs refer to aliases
- // directly so deleting certs will break dependent access points.
- builder.setNegativeButton(R.string.trusted_credentials_remove_label, listener);
- }
+ // TODO: b/127865361
+ // a safe means of clearing wifi certificates. Configs refer to aliases
+ // directly so deleting certs will break dependent access points.
+ // However, Wi-Fi used to remove this certificate from storage if the network
+ // was removed, regardless if it is used in more than one network.
+ // It has been decided to allow removing certificates from this menu, as we
+ // assume that the user who manually adds certificates must have a way to
+ // manually remove them.
+ builder.setNegativeButton(R.string.trusted_credentials_remove_label, listener);
}
return builder.create();
}
@@ -172,7 +176,8 @@
* Deletes all certificates and keys under a given alias.
*
* If the {@link Credential} is for a system alias, all active grants to the alias will be
- * removed using {@link KeyChain}.
+ * removed using {@link KeyChain}. If the {@link Credential} is for Wi-Fi alias, all
+ * credentials and keys will be removed using {@link KeyStore}.
*/
private class RemoveCredentialsTask extends AsyncTask<Credential, Void, Credential[]> {
private Context context;
@@ -188,14 +193,32 @@
for (final Credential credential : credentials) {
if (credential.isSystem()) {
removeGrantsAndDelete(credential);
- continue;
+ } else {
+ deleteWifiCredential(credential);
}
- throw new UnsupportedOperationException(
- "Not implemented for wifi certificates. This should not be reachable.");
}
return credentials;
}
+ private void deleteWifiCredential(final Credential credential) {
+ final KeyStore keyStore = KeyStore.getInstance();
+ final EnumSet<Credential.Type> storedTypes = credential.getStoredTypes();
+
+ // Remove all Wi-Fi credentials
+ if (storedTypes.contains(Credential.Type.USER_KEY)) {
+ keyStore.delete(Credentials.USER_PRIVATE_KEY + credential.getAlias(),
+ Process.WIFI_UID);
+ }
+ if (storedTypes.contains(Credential.Type.USER_CERTIFICATE)) {
+ keyStore.delete(Credentials.USER_CERTIFICATE + credential.getAlias(),
+ Process.WIFI_UID);
+ }
+ if (storedTypes.contains(Credential.Type.CA_CERTIFICATE)) {
+ keyStore.delete(Credentials.CA_CERTIFICATE + credential.getAlias(),
+ Process.WIFI_UID);
+ }
+ }
+
private void removeGrantsAndDelete(final Credential credential) {
final KeyChainConnection conn;
try {
@@ -488,5 +511,11 @@
public boolean isSystem() {
return UserHandle.getAppId(uid) == Process.SYSTEM_UID;
}
+
+ public String getAlias() { return alias; }
+
+ public EnumSet<Type> getStoredTypes() {
+ return storedTypes;
+ }
}
}
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index a93fb76..36230d1 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -22,6 +22,8 @@
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
import android.annotation.Nullable;
+import android.app.ActionBar;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
@@ -92,6 +94,7 @@
import androidx.annotation.StringRes;
import androidx.core.graphics.drawable.IconCompat;
import androidx.fragment.app.Fragment;
+import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -101,6 +104,7 @@
import com.android.settings.core.FeatureFlags;
import com.android.settings.development.featureflags.FeatureFlagPersistent;
import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settingslib.widget.ActionBarShadowController;
import java.net.InetAddress;
import java.util.Iterator;
@@ -1023,4 +1027,28 @@
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
return !(am.isLowRamDevice() && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q));
}
+
+ /**
+ * Adds a shadow appear/disappear animation to action bar scroll.
+ *
+ * <p/>
+ * This method must be called after {@link Fragment#onCreate(Bundle)}.
+ */
+ public static void setActionBarShadowAnimation(Activity activity, Lifecycle lifecycle,
+ View scrollView) {
+ if (activity == null) {
+ Log.w(TAG, "No activity, cannot style actionbar.");
+ return;
+ }
+ final ActionBar actionBar = activity.getActionBar();
+ if (actionBar == null) {
+ Log.w(TAG, "No actionbar, cannot style actionbar.");
+ return;
+ }
+ actionBar.setElevation(0);
+
+ if (lifecycle != null && scrollView != null) {
+ ActionBarShadowController.attachToView(activity, lifecycle, scrollView);
+ }
+ }
}
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index 1881ca7..641d8ca 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -31,6 +31,9 @@
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle.Event;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -40,9 +43,6 @@
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.Iterator;
import java.util.List;
@@ -53,7 +53,7 @@
* Controller that shows and updates the bluetooth device name
*/
public class AccessibilityHearingAidPreferenceController extends BasePreferenceController
- implements LifecycleObserver, OnResume, OnPause {
+ implements LifecycleObserver {
private static final String TAG = "AccessibilityHearingAidPreferenceController";
private Preference mHearingAidPreference;
@@ -104,7 +104,7 @@
return mHearingAidProfileSupported ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
- @Override
+ @OnLifecycleEvent(Event.ON_RESUME)
public void onResume() {
if (mHearingAidProfileSupported) {
IntentFilter filter = new IntentFilter();
@@ -114,7 +114,7 @@
}
}
- @Override
+ @OnLifecycleEvent(Event.ON_PAUSE)
public void onPause() {
if (mHearingAidProfileSupported) {
mContext.unregisterReceiver(mHearingAidChangedReceiver);
diff --git a/src/com/android/settings/accounts/AvatarViewMixin.java b/src/com/android/settings/accounts/AvatarViewMixin.java
index 3ce8c0a..9e762c7 100644
--- a/src/com/android/settings/accounts/AvatarViewMixin.java
+++ b/src/com/android/settings/accounts/AvatarViewMixin.java
@@ -17,6 +17,7 @@
package com.android.settings.accounts;
import android.accounts.Account;
+import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
@@ -64,11 +65,13 @@
private final Context mContext;
private final ImageView mAvatarView;
private final MutableLiveData<Bitmap> mAvatarImage;
+ private final ActivityManager mActivityManager;
private String mAccountName;
public AvatarViewMixin(SettingsHomepageActivity activity, ImageView avatarView) {
mContext = activity.getApplicationContext();
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
mAvatarView = avatarView;
mAvatarView.setOnClickListener(v -> {
Intent intent;
@@ -114,7 +117,11 @@
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
if (!mContext.getResources().getBoolean(R.bool.config_show_avatar_in_homepage)) {
- Log.d(TAG, "Feature disabled. Skipping");
+ Log.d(TAG, "Feature disabled by config. Skipping");
+ return;
+ }
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Feature disabled on low ram device. Skipping");
return;
}
if (hasAccount()) {
diff --git a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
index 7aaf80d..c32a33d 100644
--- a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
+++ b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
@@ -21,6 +21,7 @@
import android.provider.SearchIndexableResource;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.EmergencyBroadcastPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -85,6 +86,10 @@
super.onResume();
mIsFirstLaunch = false;
+
+ if (mRecentAppsPreferenceController.isAvailable()) {
+ Utils.setActionBarShadowAnimation(getActivity(), getSettingsLifecycle(), getListView());
+ }
}
@Override
diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java
index c0d18c6..be86dd5 100644
--- a/src/com/android/settings/applications/RecentAppsPreferenceController.java
+++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.icu.text.RelativeDateTimeFormatter;
import android.os.PowerManager;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -224,7 +225,8 @@
.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info))
.setTitle(appEntry.label)
.setSummary(StringUtil.formatRelativeTime(mContext,
- System.currentTimeMillis() - stat.getLastTimeUsed(), false))
+ System.currentTimeMillis() - stat.getLastTimeUsed(), false,
+ RelativeDateTimeFormatter.Style.SHORT))
.setOnClickListener(v ->
AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
R.string.application_info_label, pkgName, appEntry.info.uid,
diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
index c661890..e118ed6 100644
--- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
@@ -559,15 +559,15 @@
if (mHomePackages.contains(mAppEntry.info.packageName)
|| isSystemPackage(mActivity.getResources(), mPm, mPackageInfo)) {
// Disable button for core system applications.
- mButtonsPref.setButton2Text(R.string.uninstall_text)
+ mButtonsPref.setButton2Text(R.string.disable_text)
.setButton2Icon(R.drawable.ic_settings_delete);
} else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
- mButtonsPref.setButton2Text(R.string.uninstall_text)
+ mButtonsPref.setButton2Text(R.string.disable_text)
.setButton2Icon(R.drawable.ic_settings_delete);
disableable = !mApplicationFeatureProvider.getKeepEnabledPackages()
.contains(mAppEntry.info.packageName);
} else {
- mButtonsPref.setButton2Text(R.string.install_text)
+ mButtonsPref.setButton2Text(R.string.enable_text)
.setButton2Icon(R.drawable.ic_settings_install);
disableable = true;
}
diff --git a/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java b/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java
index d0b26a5..0f90c69 100644
--- a/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java
+++ b/src/com/android/settings/applications/appinfo/DrawOverlayDetails.java
@@ -15,22 +15,13 @@
*/
package com.android.settings.applications.appinfo;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
-import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.Bundle;
-import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
@@ -55,16 +46,11 @@
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
private static final String LOG_TAG = "DrawOverlayDetails";
- private static final int[] APP_OPS_OP_CODE = {
- AppOpsManager.OP_SYSTEM_ALERT_WINDOW
- };
-
// Use a bridge to get the overlay details but don't initialize it to connect with all state.
// TODO: Break out this functionality into its own class.
private AppStateOverlayBridge mOverlayBridge;
private AppOpsManager mAppOpsManager;
private SwitchPreference mSwitchPref;
- private Intent mSettingsIntent;
private OverlayState mOverlayState;
@Override
@@ -82,18 +68,15 @@
// find preferences
addPreferencesFromResource(R.xml.draw_overlay_permissions_details);
- mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
+ mSwitchPref = findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
// install event listeners
mSwitchPref.setOnPreferenceChangeListener(this);
-
- mSettingsIntent = new Intent(Intent.ACTION_MAIN)
- .setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
}
// Override here so we don't have an empty screen
@Override
- public View onCreateView (LayoutInflater inflater,
+ public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// if we don't have a package info, show a page saying this is unsupported
@@ -104,21 +87,6 @@
}
@Override
- public void onResume() {
- super.onResume();
- getActivity().getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- Window window = getActivity().getWindow();
- WindowManager.LayoutParams attrs = window.getAttributes();
- attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
- window.setAttributes(attrs);
- }
-
- @Override
public void onDestroy() {
super.onDestroy();
mOverlayBridge.release();
@@ -164,7 +132,9 @@
@Override
protected boolean refreshUi() {
- if (mPackageInfo == null) return true;
+ if (mPackageInfo == null) {
+ return true;
+ }
mOverlayState = mOverlayBridge.getOverlayInfo(mPackageName,
mPackageInfo.applicationInfo.uid);
@@ -174,9 +144,6 @@
// you cannot ask a user to grant you a permission you did not have!
mSwitchPref.setEnabled(mOverlayState.permissionDeclared && mOverlayState.controlEnabled);
- ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
- PackageManager.GET_META_DATA, mUserId);
-
return true;
}
diff --git a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
index e46f294..bd08236 100644
--- a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
+++ b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
@@ -19,7 +19,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -41,10 +40,6 @@
private final TextView mAppName;
private final ImageView mAppIcon;
- private final boolean mKeepStableHeight;
-
- @VisibleForTesting
- View mSummaryContainer;
@VisibleForTesting
final TextView mSummary;
@VisibleForTesting
@@ -54,20 +49,18 @@
@VisibleForTesting
final Switch mSwitch;
- ApplicationViewHolder(View itemView, boolean keepStableHeight) {
+ ApplicationViewHolder(View itemView) {
super(itemView);
mAppName = itemView.findViewById(android.R.id.title);
mAppIcon = itemView.findViewById(android.R.id.icon);
- mSummaryContainer = itemView.findViewById(R.id.summary_container);
mSummary = itemView.findViewById(android.R.id.summary);
mDisabled = itemView.findViewById(R.id.appendix);
- mKeepStableHeight = keepStableHeight;
mSwitch = itemView.findViewById(R.id.switchWidget);
mWidgetContainer = itemView.findViewById(android.R.id.widget_frame);
}
static View newView(ViewGroup parent) {
- return newView(parent, false);
+ return newView(parent, false /* twoTarget */);
}
static View newView(ViewGroup parent, boolean twoTarget) {
@@ -76,13 +69,13 @@
final ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame);
if (twoTarget) {
if (widgetFrame != null) {
- LayoutInflater.from(parent.getContext())
- .inflate(R.layout.preference_widget_master_switch, widgetFrame, true);
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.preference_widget_master_switch, widgetFrame, true);
- View divider = LayoutInflater.from(parent.getContext()).inflate(
- R.layout.preference_two_target_divider, view, false);
- // second to last, before widget frame
- view.addView(divider, view.getChildCount() - 1);
+ View divider = LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.preference_two_target_divider, view, false);
+ // second to last, before widget frame
+ view.addView(divider, view.getChildCount() - 1);
}
} else if (widgetFrame != null) {
widgetFrame.setVisibility(View.GONE);
@@ -92,12 +85,10 @@
void setSummary(CharSequence summary) {
mSummary.setText(summary);
- updateSummaryContainer();
}
void setSummary(@StringRes int summary) {
mSummary.setText(summary);
- updateSummaryContainer();
}
void setEnabled(boolean isEnabled) {
@@ -133,17 +124,6 @@
} else {
mDisabled.setVisibility(View.GONE);
}
- updateSummaryContainer();
- }
-
- void updateSummaryContainer() {
- if (mKeepStableHeight) {
- mSummaryContainer.setVisibility(View.VISIBLE);
- return;
- }
- final boolean hasContent =
- !TextUtils.isEmpty(mDisabled.getText()) || !TextUtils.isEmpty(mSummary.getText());
- mSummaryContainer.setVisibility(hasContent ? View.VISIBLE : View.GONE);
}
void updateSizeText(AppEntry entry, CharSequence invalidSizeStr, int whichSize) {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 9586be0..9839934 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -33,7 +33,6 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.app.usage.IUsageStatsManager;
import android.content.Context;
@@ -50,22 +49,18 @@
import android.util.ArraySet;
import android.util.IconDrawableFactory;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Filter;
import android.widget.FrameLayout;
import android.widget.SearchView;
import android.widget.Spinner;
-import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -1059,14 +1054,13 @@
@Override
public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view;
+ final View view;
if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
view = ApplicationViewHolder.newView(parent, true /* twoTarget */);
} else {
view = ApplicationViewHolder.newView(parent, false /* twoTarget */);
}
- return new ApplicationViewHolder(view,
- shouldUseStableItemHeight(mManageApplications.mListType));
+ return new ApplicationViewHolder(view);
}
@Override
@@ -1153,11 +1147,6 @@
mSearchFilter.filter(query);
}
- @VisibleForTesting
- static boolean shouldUseStableItemHeight(int listType) {
- return true;
- }
-
private static boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
if (info1 == null || info2 == null) {
return false;
diff --git a/src/com/android/settings/applications/managedomainurls/DomainAppPreference.java b/src/com/android/settings/applications/managedomainurls/DomainAppPreference.java
index 94d2818..bc7b855 100644
--- a/src/com/android/settings/applications/managedomainurls/DomainAppPreference.java
+++ b/src/com/android/settings/applications/managedomainurls/DomainAppPreference.java
@@ -47,12 +47,6 @@
setState();
}
- @Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
- super.onBindViewHolder(holder);
- holder.itemView.findViewById(R.id.appendix).setVisibility(View.GONE);
- }
-
public void reuse() {
setState();
notifyChanged();
diff --git a/src/com/android/settings/applications/specialaccess/ZenAccessController.java b/src/com/android/settings/applications/specialaccess/ZenAccessController.java
deleted file mode 100644
index 5ae2bd3..0000000
--- a/src/com/android/settings/applications/specialaccess/ZenAccessController.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.specialaccess;
-
-import android.app.ActivityManager;
-import android.content.Context;
-
-import com.android.settings.core.BasePreferenceController;
-
-public class ZenAccessController extends BasePreferenceController {
-
- private final ActivityManager mActivityManager;
-
- public ZenAccessController(Context context, String preferenceKey) {
- super(context, preferenceKey);
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return !mActivityManager.isLowRamDevice()
- ? AVAILABLE_UNSEARCHABLE
- : UNSUPPORTED_ON_DEVICE;
- }
-}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
new file mode 100644
index 0000000..fc85f7d
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
@@ -0,0 +1,76 @@
+/*
+ * 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.applications.specialaccess.zenaccess;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Warning dialog when revoking zen access warning that zen rule instances will be deleted.
+ */
+public class FriendlyWarningDialogFragment extends InstrumentedDialogFragment {
+ static final String KEY_PKG = "p";
+ static final String KEY_LABEL = "l";
+
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_ZEN_ACCESS_REVOKE;
+ }
+
+ public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
+ Bundle args = new Bundle();
+ args.putString(KEY_PKG, pkg);
+ args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
+ setArguments(args);
+ return this;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Bundle args = getArguments();
+ final String pkg = args.getString(KEY_PKG);
+ final String label = args.getString(KEY_LABEL);
+
+ final String title = getResources().getString(
+ R.string.zen_access_revoke_warning_dialog_title, label);
+ final String summary = getResources()
+ .getString(R.string.zen_access_revoke_warning_dialog_summary);
+ return new AlertDialog.Builder(getContext())
+ .setMessage(summary)
+ .setTitle(title)
+ .setCancelable(true)
+ .setPositiveButton(R.string.okay,
+ (dialog, id) -> {
+ ZenAccessController.deleteRules(getContext(), pkg);
+ ZenAccessController.setAccess(getContext(), pkg, false);
+ })
+ .setNegativeButton(R.string.cancel,
+ (dialog, id) -> {
+ // pass
+ })
+ .create();
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/OWNERS b/src/com/android/settings/applications/specialaccess/zenaccess/OWNERS
new file mode 100644
index 0000000..9b5f41e
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/OWNERS
@@ -0,0 +1,2 @@
+beverlyt@google.com
+juliacr@google.com
\ No newline at end of file
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
new file mode 100644
index 0000000..69318f8
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
@@ -0,0 +1,73 @@
+/*
+ * 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.applications.specialaccess.zenaccess;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.notification.ZenAccessSettings;
+
+/**
+ * Warning dialog when allowing zen access warning about the privileges being granted.
+ */
+public class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
+ static final String KEY_PKG = "p";
+ static final String KEY_LABEL = "l";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_ZEN_ACCESS_GRANT;
+ }
+
+ public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
+ Bundle args = new Bundle();
+ args.putString(KEY_PKG, pkg);
+ args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
+ setArguments(args);
+ return this;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Bundle args = getArguments();
+ final String pkg = args.getString(KEY_PKG);
+ final String label = args.getString(KEY_LABEL);
+
+ final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
+ label);
+ final String summary = getResources()
+ .getString(R.string.zen_access_warning_dialog_summary);
+ return new AlertDialog.Builder(getContext())
+ .setMessage(summary)
+ .setTitle(title)
+ .setCancelable(true)
+ .setPositiveButton(R.string.allow,
+ (dialog, id) -> ZenAccessController.setAccess(getContext(), pkg, true))
+ .setNegativeButton(R.string.deny,
+ (dialog, id) -> {
+ // pass
+ })
+ .create();
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
new file mode 100644
index 0000000..946599b
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.specialaccess.zenaccess;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.NotificationManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.ParceledListSlice;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.List;
+import java.util.Set;
+
+public class ZenAccessController extends BasePreferenceController {
+
+ private static final String TAG = "ZenAccessController";
+
+ private final ActivityManager mActivityManager;
+
+ public ZenAccessController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return isSupported(mActivityManager)
+ ? AVAILABLE_UNSEARCHABLE
+ : UNSUPPORTED_ON_DEVICE;
+ }
+
+ public static boolean isSupported(ActivityManager activityManager) {
+ return !activityManager.isLowRamDevice();
+ }
+
+ public static Set<String> getPackagesRequestingNotificationPolicyAccess() {
+ final ArraySet<String> requestingPackages = new ArraySet<>();
+ try {
+ final String[] PERM = {
+ android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
+ };
+ final ParceledListSlice list = AppGlobals.getPackageManager()
+ .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
+ ActivityManager.getCurrentUser());
+ final List<PackageInfo> pkgs = list.getList();
+ if (pkgs != null) {
+ for (PackageInfo info : pkgs) {
+ requestingPackages.add(info.packageName);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot reach packagemanager", e);
+ }
+ return requestingPackages;
+ }
+
+ public static Set<String> getAutoApprovedPackages(Context context) {
+ final Set<String> autoApproved = new ArraySet<>();
+ autoApproved.addAll(context.getSystemService(NotificationManager.class)
+ .getEnabledNotificationListenerPackages());
+ return autoApproved;
+ }
+
+ public static boolean hasAccess(Context context, String pkg) {
+ return context.getSystemService(
+ NotificationManager.class).isNotificationPolicyAccessGrantedForPackage(pkg);
+ }
+
+ public static void setAccess(final Context context, final String pkg, final boolean access) {
+ logSpecialPermissionChange(access, pkg, context);
+ AsyncTask.execute(() -> {
+ final NotificationManager mgr = context.getSystemService(NotificationManager.class);
+ mgr.setNotificationPolicyAccessGranted(pkg, access);
+ });
+ }
+
+ public static void deleteRules(final Context context, final String pkg) {
+ AsyncTask.execute(() -> {
+ final NotificationManager mgr = context.getSystemService(NotificationManager.class);
+ mgr.removeAutomaticZenRules(pkg);
+ });
+ }
+
+ @VisibleForTesting
+ static void logSpecialPermissionChange(boolean enable, String packageName, Context context) {
+ int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_DND_ALLOW
+ : SettingsEnums.APP_SPECIAL_PERMISSION_DND_DENY;
+ FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
+ logCategory, packageName);
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
new file mode 100644
index 0000000..a18e7d6
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
@@ -0,0 +1,100 @@
+/*
+ * 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.applications.specialaccess.zenaccess;
+
+import android.app.ActivityManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoWithHeader;
+
+import java.util.Set;
+
+public class ZenAccessDetails extends AppInfoWithHeader implements
+ ZenAccessSettingObserverMixin.Listener {
+
+ private static final String SWITCH_PREF_KEY = "zen_access_switch";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.ZEN_ACCESS_DETAIL;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.zen_access_permission_details);
+ getSettingsLifecycle().addObserver(
+ new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
+ }
+
+ @Override
+ protected boolean refreshUi() {
+ final Context context = getContext();
+ if (!ZenAccessController.isSupported(context.getSystemService(ActivityManager.class))) {
+ return false;
+ }
+ // If this app didn't declare this permission in their manifest, don't bother showing UI.
+ final Set<String> needAccessApps =
+ ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
+ if (!needAccessApps.contains(mPackageName)) {
+ return false;
+ }
+ updatePreference(context, findPreference(SWITCH_PREF_KEY));
+ return true;
+ }
+
+ @Override
+ protected AlertDialog createDialog(int id, int errorCode) {
+ return null;
+ }
+
+ public void updatePreference(Context context, SwitchPreference preference) {
+ final CharSequence label = mPackageInfo.applicationInfo.loadLabel(mPm);
+ final Set<String> autoApproved = ZenAccessController.getAutoApprovedPackages(context);
+ if (autoApproved.contains(mPackageName)) {
+ //Auto approved, user cannot do anything. Hard code summary and disable preference.
+ preference.setEnabled(false);
+ preference.setSummary(getString(R.string.zen_access_disabled_package_warning));
+ return;
+ }
+ preference.setChecked(ZenAccessController.hasAccess(context, mPackageName));
+ preference.setOnPreferenceChangeListener((p, newValue) -> {
+ final boolean access = (Boolean) newValue;
+ if (access) {
+ new ScaryWarningDialogFragment()
+ .setPkgInfo(mPackageName, label)
+ .show(getFragmentManager(), "dialog");
+ } else {
+ new FriendlyWarningDialogFragment()
+ .setPkgInfo(mPackageName, label)
+ .show(getFragmentManager(), "dialog");
+ }
+ return false;
+ });
+ }
+
+ @Override
+ public void onZenAccessPolicyChanged() {
+ refreshUi();
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
new file mode 100644
index 0000000..30507ef
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
@@ -0,0 +1,77 @@
+/*
+ * 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.applications.specialaccess.zenaccess;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+public class ZenAccessSettingObserverMixin extends ContentObserver implements LifecycleObserver,
+ OnStart, OnStop {
+
+ public interface Listener {
+ void onZenAccessPolicyChanged();
+ }
+
+ private final Context mContext;
+ private final Listener mListener;
+
+ public ZenAccessSettingObserverMixin(Context context, Listener listener) {
+ super(new Handler(Looper.getMainLooper()));
+ mContext = context;
+ mListener = listener;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mListener != null) {
+ mListener.onZenAccessPolicyChanged();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
+ return;
+ }
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES),
+ false /* notifyForDescendants */,
+ this /* observer */);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS),
+ false /* notifyForDescendants */,
+ this /* observer */);
+ }
+
+ @Override
+ public void onStop() {
+ if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
+ return;
+ }
+ mContext.getContentResolver().unregisterContentObserver(this /* observer */);
+ }
+}
diff --git a/src/com/android/settings/backup/BackupSettingsHelper.java b/src/com/android/settings/backup/BackupSettingsHelper.java
index fa77154..1d3455b 100644
--- a/src/com/android/settings/backup/BackupSettingsHelper.java
+++ b/src/com/android/settings/backup/BackupSettingsHelper.java
@@ -25,6 +25,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -91,9 +92,9 @@
*
* @return Label for the backup settings item.
*/
- public String getLabelForBackupSettings() {
- String label = getLabelFromBackupTransport();
- if (label == null || label.isEmpty()) {
+ public CharSequence getLabelForBackupSettings() {
+ CharSequence label = getLabelFromBackupTransport();
+ if (TextUtils.isEmpty(label)) {
label = mContext.getString(R.string.privacy_settings_title);
}
return label;
@@ -209,7 +210,7 @@
}
/** Checks if backup service is enabled for this user. */
- private boolean isBackupServiceActive() {
+ public boolean isBackupServiceActive() {
boolean backupOkay;
try {
backupOkay = mBackupManager.isBackupServiceActive(UserHandle.myUserId());
@@ -222,10 +223,11 @@
}
@VisibleForTesting
- String getLabelFromBackupTransport() {
+ CharSequence getLabelFromBackupTransport() {
try {
- String label =
- mBackupManager.getDataManagementLabel(mBackupManager.getCurrentTransport());
+ CharSequence label =
+ mBackupManager.getDataManagementLabelForUser(
+ UserHandle.myUserId(), mBackupManager.getCurrentTransport());
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Received the backup settings label from backup transport: " + label);
}
diff --git a/src/com/android/settings/backup/BackupSettingsPreferenceController.java b/src/com/android/settings/backup/BackupSettingsPreferenceController.java
index 2a657ea..4e0e3b4 100644
--- a/src/com/android/settings/backup/BackupSettingsPreferenceController.java
+++ b/src/com/android/settings/backup/BackupSettingsPreferenceController.java
@@ -31,7 +31,7 @@
private static final String BACKUP_SETTINGS = "backup_settings";
private static final String MANUFACTURER_SETTINGS = "manufacturer_backup";
private Intent mBackupSettingsIntent;
- private String mBackupSettingsTitle;
+ private CharSequence mBackupSettingsTitle;
private String mBackupSettingsSummary;
private Intent mManufacturerIntent;
private String mManufacturerLabel;
diff --git a/src/com/android/settings/backup/DataManagementPreferenceController.java b/src/com/android/settings/backup/DataManagementPreferenceController.java
index d19b866..34d51ac 100644
--- a/src/com/android/settings/backup/DataManagementPreferenceController.java
+++ b/src/com/android/settings/backup/DataManagementPreferenceController.java
@@ -48,7 +48,7 @@
return;
}
preference.setIntent(mPSCD.getManageIntent());
- final String manageLabel = mPSCD.getManageLabel();
+ final CharSequence manageLabel = mPSCD.getManageLabel();
if (manageLabel != null) {
preference.setTitle(manageLabel);
}
diff --git a/src/com/android/settings/backup/OWNERS b/src/com/android/settings/backup/OWNERS
index c026a35..a7b55fd 100644
--- a/src/com/android/settings/backup/OWNERS
+++ b/src/com/android/settings/backup/OWNERS
@@ -1,6 +1,13 @@
-# Default reviewers for this and subdirectories.
+# Use this reviewer by default.
+br-framework-team+reviews@google.com
+
+# People who can approve changes for submission.
+anniemeng@google.com
+nathch@google.com
+rthakohov@google.com
+
+# Others (in case above are not available).
bryanmawhinney@google.com
-cprins@google.com
jorlow@google.com
philippov@google.com
stefanot@google.com
\ No newline at end of file
diff --git a/src/com/android/settings/backup/PrivacySettingsConfigData.java b/src/com/android/settings/backup/PrivacySettingsConfigData.java
index ffe05b8..8bea11b 100644
--- a/src/com/android/settings/backup/PrivacySettingsConfigData.java
+++ b/src/com/android/settings/backup/PrivacySettingsConfigData.java
@@ -27,7 +27,7 @@
private Intent mConfigIntent;
private String mConfigSummary;
private Intent mManageIntent;
- private String mManageLabel;
+ private CharSequence mManageLabel;
private PrivacySettingsConfigData() {
mBackupEnabled = false;
@@ -85,11 +85,11 @@
mManageIntent = manageIntent;
}
- public String getManageLabel() {
+ public CharSequence getManageLabel() {
return mManageLabel;
}
- public void setManageLabel(final String manageLabel) {
+ public void setManageLabel(final CharSequence manageLabel) {
mManageLabel = manageLabel;
}
}
diff --git a/src/com/android/settings/backup/PrivacySettingsUtils.java b/src/com/android/settings/backup/PrivacySettingsUtils.java
index f8f21dd..bb1108c 100644
--- a/src/com/android/settings/backup/PrivacySettingsUtils.java
+++ b/src/com/android/settings/backup/PrivacySettingsUtils.java
@@ -97,7 +97,8 @@
data.setConfigSummary(backupManager.getDestinationString(transport));
data.setManageIntent(validatedActivityIntent(context,
backupManager.getDataManagementIntent(transport), "management"));
- data.setManageLabel(backupManager.getDataManagementLabel(transport));
+ data.setManageLabel(
+ backupManager.getDataManagementLabelForUser(UserHandle.myUserId(), transport));
data.setBackupGray(false);
} catch (RemoteException e) {
// leave it 'false' and disable the UI; there's no backup manager
diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
index 097ffe2..81b70b5 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -140,7 +140,7 @@
if (!mHasPassword) {
// No password registered, launch into enrollment wizard.
launchChooseLock();
- } else {
+ } else if (!mLaunchedConfirmLock || mToken == null) {
launchConfirmLock(getConfirmLockTitleResId(), getChallenge());
}
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityToggle.java b/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityToggle.java
index 49f4f7f..dffc67d 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityToggle.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityToggle.java
@@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
@@ -58,9 +59,22 @@
a.recycle();
}
mSwitch = findViewById(R.id.toggle);
+ mSwitch.setChecked(false);
}
public boolean isChecked() {
return mSwitch.isChecked();
}
+
+ public void setChecked(boolean checked) {
+ mSwitch.setChecked(checked);
+ }
+
+ public void setListener(CompoundButton.OnCheckedChangeListener listener) {
+ mSwitch.setOnCheckedChangeListener(listener);
+ }
+
+ public Switch getSwitch() {
+ return mSwitch;
+ }
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index 5f2b675..d923e55 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -25,7 +25,7 @@
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
-import android.widget.LinearLayout;
+import android.widget.CompoundButton;
import android.widget.TextView;
import com.android.settings.R;
@@ -38,6 +38,7 @@
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.span.LinkSpan;
+import com.google.android.setupdesign.view.IllustrationVideoView;
public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
@@ -46,18 +47,44 @@
private FaceManager mFaceManager;
private FaceEnrollAccessibilityToggle mSwitchDiversity;
+ private IllustrationVideoView mIllustrationNormal;
+ private View mIllustrationAccessibility;
+
+ private CompoundButton.OnCheckedChangeListener mSwitchDiversityListener =
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ mIllustrationNormal.stop();
+ mIllustrationNormal.setVisibility(View.INVISIBLE);
+ mIllustrationAccessibility.setVisibility(View.VISIBLE);
+ } else {
+ mIllustrationNormal.setVisibility(View.VISIBLE);
+ mIllustrationNormal.start();
+ mIllustrationAccessibility.setVisibility(View.INVISIBLE);
+ }
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
mFaceManager = Utils.getFaceManagerOrNull(this);
- final LinearLayout accessibilityLayout = findViewById(R.id.accessibility_layout);
final Button accessibilityButton = findViewById(R.id.accessibility_button);
+ final View footerView = findViewById(R.id.footer_layout);
accessibilityButton.setOnClickListener(view -> {
- accessibilityButton.setVisibility(View.INVISIBLE);
- accessibilityLayout.setVisibility(View.VISIBLE);
+ mSwitchDiversity.setChecked(true);
+ accessibilityButton.setVisibility(View.GONE);
+ mSwitchDiversity.setVisibility(View.VISIBLE);
+ footerView.setVisibility(View.GONE);
});
mSwitchDiversity = findViewById(R.id.toggle_diversity);
+ mSwitchDiversity.setListener(mSwitchDiversityListener);
+
+ mIllustrationNormal = findViewById(R.id.illustration_normal);
+ mIllustrationAccessibility = findViewById(R.id.illustration_accessibility);
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
@@ -91,6 +118,13 @@
}
@Override
+ protected void onResume() {
+ super.onResume();
+ mSwitchDiversityListener.onCheckedChanged(mSwitchDiversity.getSwitch(),
+ mSwitchDiversity.isChecked());
+ }
+
+ @Override
protected boolean isDisabledByAdmin() {
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
this, DevicePolicyManager.KEYGUARD_DISABLE_FACE, mUserId) != null;
@@ -113,7 +147,7 @@
@Override
protected int getDescriptionResDisabledByAdmin() {
- return R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled;
+ return R.string.security_settings_face_enroll_introduction_message_unlock_disabled;
}
@Override
@@ -177,7 +211,7 @@
} else {
intent.setClass(this, FaceEnrollEnrolling.class);
}
- intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, mSwitchDiversity.isChecked());
+ intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked());
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
return intent;
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java
index ea5a303..9651eec 100644
--- a/src/com/android/settings/biometrics/face/FaceSettings.java
+++ b/src/com/android/settings/biometrics/face/FaceSettings.java
@@ -31,6 +31,7 @@
import android.util.Log;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
@@ -56,6 +57,7 @@
private int mUserId;
private byte[] mToken;
private FaceSettingsAttentionPreferenceController mAttentionController;
+ private FaceSettingsRemoveButtonPreferenceController mRemoveController;
private final FaceSettingsRemoveButtonPreferenceController.Listener mRemovalListener = () -> {
if (getActivity() != null) {
@@ -166,8 +168,9 @@
if (controller instanceof FaceSettingsAttentionPreferenceController) {
mAttentionController = (FaceSettingsAttentionPreferenceController) controller;
} else if (controller instanceof FaceSettingsRemoveButtonPreferenceController) {
- ((FaceSettingsRemoveButtonPreferenceController) controller)
- .setListener(mRemovalListener);
+ mRemoveController = (FaceSettingsRemoveButtonPreferenceController) controller;
+ mRemoveController.setListener(mRemovalListener);
+ mRemoveController.setActivity((SettingsActivity) getActivity());
}
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
index baab391..5174482 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
@@ -16,9 +16,14 @@
package com.android.settings.biometrics.face;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.DialogInterface;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
+import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
@@ -28,7 +33,9 @@
import androidx.preference.Preference;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List;
@@ -43,6 +50,33 @@
private static final String TAG = "FaceSettings/Remove";
private static final String KEY = "security_settings_face_delete_faces_container";
+ public static class ConfirmRemoveDialog extends InstrumentedDialogFragment {
+
+ private DialogInterface.OnClickListener mOnClickListener;
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_FACE_REMOVE;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ builder.setTitle(R.string.security_settings_face_settings_remove_dialog_title)
+ .setMessage(R.string.security_settings_face_settings_remove_dialog_details)
+ .setPositiveButton(R.string.okay, mOnClickListener)
+ .setNegativeButton(R.string.cancel, mOnClickListener);
+ AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+
+ public void setOnClickListener(DialogInterface.OnClickListener listener) {
+ mOnClickListener = listener;
+ }
+ }
+
interface Listener {
void onRemoved();
}
@@ -50,6 +84,7 @@
private Button mButton;
private List<Face> mFaces;
private Listener mListener;
+ private SettingsActivity mActivity;
private final Context mContext;
private final int mUserId;
@@ -77,6 +112,27 @@
}
};
+ private final DialogInterface.OnClickListener mOnClickListener
+ = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (mFaces.isEmpty()) {
+ Log.e(TAG, "No faces");
+ return;
+ }
+ if (mFaces.size() > 1) {
+ Log.e(TAG, "Multiple enrollments: " + mFaces.size());
+ }
+
+ // Remove the first/only face
+ mFaceManager.remove(mFaces.get(0), mUserId, mRemovalCallback);
+ } else {
+ mButton.setEnabled(true);
+ }
+ }
+ };
+
public FaceSettingsRemoveButtonPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mContext = context;
@@ -115,20 +171,17 @@
public void onClick(View v) {
if (v == mButton) {
mButton.setEnabled(false);
- if (mFaces.isEmpty()) {
- Log.e(TAG, "No faces");
- return;
- }
- if (mFaces.size() > 1) {
- Log.e(TAG, "Multiple enrollments: " + mFaces.size());
- }
-
- // Remove the first/only face
- mFaceManager.remove(mFaces.get(0), mUserId, mRemovalCallback);
+ ConfirmRemoveDialog dialog = new ConfirmRemoveDialog();
+ dialog.setOnClickListener(mOnClickListener);
+ dialog.show(mActivity.getSupportFragmentManager(), ConfirmRemoveDialog.class.getName());
}
}
public void setListener(Listener listener) {
mListener = listener;
}
+
+ public void setActivity(SettingsActivity activity) {
+ mActivity = activity;
+ }
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
index fd805b8..3860352 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
@@ -26,7 +26,6 @@
import com.android.settings.R;
import com.android.settings.widget.EntityHeaderController;
-import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -66,10 +65,8 @@
}
protected void setHeaderProperties() {
- final Pair<Drawable, String> pair = BluetoothUtils
- .getBtClassDrawableWithDescription(mContext, mCachedDevice,
- mContext.getResources().getFraction(R.fraction.bt_battery_scale_fraction, 1,
- 1));
+ final Pair<Drawable, String> pair = Utils.getBtRainbowDrawableWithDescription(mContext,
+ mCachedDevice);
String summaryText = mCachedDevice.getConnectionSummary();
// If both the hearing aids are connected, two device status should be shown.
// If Second Summary is unavailable, to set it to null.
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index d4c0ed0..8d727eb 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -38,7 +38,6 @@
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.GearPreference;
-import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
@@ -130,8 +129,8 @@
// Null check is done at the framework
setSummary(mCachedDevice.getConnectionSummary());
- final Pair<Drawable, String> pair = BluetoothUtils
- .getBtClassDrawableWithDescription(getContext(), mCachedDevice);
+ final Pair<Drawable, String> pair = Utils
+ .getBtRainbowDrawableWithDescription(getContext(), mCachedDevice);
if (pair.first != null) {
setIcon(pair.first);
contentDescription = pair.second;
@@ -246,5 +245,4 @@
R.string.bluetooth_pairing_error_message);
}
}
-
}
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index ff4a98f..e02ddc4 100755
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -21,8 +21,12 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.util.Log;
+import android.util.Pair;
import android.widget.Toast;
import androidx.annotation.VisibleForTesting;
@@ -30,8 +34,10 @@
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.AdaptiveIcon;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
@@ -179,4 +185,25 @@
return META_INT_ERROR;
}
}
+
+ /**
+ * Get colorful bluetooth icon with description
+ */
+ public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice) {
+ final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
+ context, cachedDevice);
+ final Resources resources = context.getResources();
+ final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
+ final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
+
+ // get color index based on mac address
+ final int index = Math.abs(cachedDevice.getAddress().hashCode()) % iconBgColors.length;
+ pair.first.setColorFilter(iconFgColors[index], PorterDuff.Mode.SRC_ATOP);
+ final Drawable adaptiveIcon = new AdaptiveIcon(context, pair.first);
+ ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
+
+ return new Pair<>(adaptiveIcon, pair.second);
+ }
+
}
diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java
index 6c2da07..fbb2ae3 100644
--- a/src/com/android/settings/core/FeatureFlags.java
+++ b/src/com/android/settings/core/FeatureFlags.java
@@ -26,4 +26,7 @@
public static final String NETWORK_INTERNET_V2 = "settings_network_and_internet_v2";
public static final String SLICE_INJECTION = "settings_slice_injection";
public static final String MAINLINE_MODULE = "settings_mainline_module";
+ public static final String WIFI_DETAILS_SAVED_SCREEN = "settings_wifi_details_saved_screen";
+ public static final String WIFI_DETAILS_DATAUSAGE_HEADER =
+ "settings_wifi_details_datausage_header";
}
diff --git a/src/com/android/settings/core/HideNonSystemOverlayMixin.java b/src/com/android/settings/core/HideNonSystemOverlayMixin.java
new file mode 100644
index 0000000..59cef3b
--- /dev/null
+++ b/src/com/android/settings/core/HideNonSystemOverlayMixin.java
@@ -0,0 +1,64 @@
+/*
+ * 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.core;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.app.Activity;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+
+/**
+ * A mixin that adds window flag to prevent non-system overlays showing on top of Settings
+ * activities.
+ */
+public class HideNonSystemOverlayMixin implements LifecycleObserver {
+
+ private final Activity mActivity;
+
+ public HideNonSystemOverlayMixin(Activity activity) {
+ mActivity = activity;
+ }
+
+ @OnLifecycleEvent(ON_START)
+ public void onStart() {
+ if (mActivity == null) {
+ return;
+ }
+ mActivity.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ android.util.EventLog.writeEvent(0x534e4554, "120484087", -1, "");
+ }
+
+
+ @OnLifecycleEvent(ON_STOP)
+ public void onStop() {
+ if (mActivity == null) {
+ return;
+ }
+ final Window window = mActivity.getWindow();
+ final WindowManager.LayoutParams attrs = window.getAttributes();
+ attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+ window.setAttributes(attrs);
+ }
+}
diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java
index 294e754..cd13654 100644
--- a/src/com/android/settings/core/SettingsBaseActivity.java
+++ b/src/com/android/settings/core/SettingsBaseActivity.java
@@ -32,7 +32,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
-import android.view.WindowManager.LayoutParams;
import android.widget.Toolbar;
import androidx.fragment.app.FragmentActivity;
@@ -59,8 +58,8 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
final long startTime = System.currentTimeMillis();
+ getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 147d0be..10eec4e 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -50,6 +50,7 @@
import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetails;
import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureSettings;
import com.android.settings.applications.specialaccess.vrlistener.VrListenerSettings;
+import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails;
import com.android.settings.backup.PrivacySettings;
import com.android.settings.backup.ToggleBackupSettingFragment;
import com.android.settings.backup.UserBackupSettingsActivity;
@@ -75,6 +76,7 @@
import com.android.settings.enterprise.EnterprisePrivacySettings;
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings;
import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
import com.android.settings.gestures.AssistGestureSettings;
import com.android.settings.gestures.DoubleTapPowerSettings;
@@ -135,6 +137,7 @@
import com.android.settings.wifi.WifiAPITest;
import com.android.settings.wifi.WifiInfo;
import com.android.settings.wifi.WifiSettings;
+import com.android.settings.wifi.calling.WifiCallingDisclaimerFragment;
import com.android.settings.wifi.calling.WifiCallingSettings;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
@@ -208,6 +211,7 @@
UserSettings.class.getName(),
NotificationAccessSettings.class.getName(),
ZenAccessSettings.class.getName(),
+ ZenAccessDetails.class.getName(),
ZenModeAutomationSettings.class.getName(),
PrintSettingsFragment.class.getName(),
PrintJobSettingsFragment.class.getName(),
@@ -260,6 +264,7 @@
ConnectedDeviceDashboardFragment.class.getName(),
UsbDetailsFragment.class.getName(),
AppAndNotificationDashboardFragment.class.getName(),
+ WifiCallingDisclaimerFragment.class.getName(),
AccountDashboardFragment.class.getName(),
EnterprisePrivacySettings.class.getName(),
WebViewAppPicker.class.getName(),
@@ -268,6 +273,7 @@
DataUsageList.class.getName(),
ToggleBackupSettingFragment.class.getName(),
PreviouslyConnectedDeviceDashboardFragment.class.getName(),
+ BatterySaverScheduleSettings.class.getName()
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index 6348b91..744cbd2 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -46,7 +46,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.AdaptiveHomepageIcon;
+import com.android.settings.widget.AdaptiveIcon;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
@@ -197,8 +197,8 @@
Drawable iconDrawable = tileIcon.loadDrawable(preference.getContext());
if (forceRoundedIcon
&& !TextUtils.equals(mContext.getPackageName(), tile.getPackageName())) {
- iconDrawable = new AdaptiveHomepageIcon(mContext, iconDrawable);
- ((AdaptiveHomepageIcon) iconDrawable).setBackgroundColor(mContext, tile);
+ iconDrawable = new AdaptiveIcon(mContext, iconDrawable);
+ ((AdaptiveIcon) iconDrawable).setBackgroundColor(mContext, tile);
}
preference.setIcon(iconDrawable);
} else if (tile.getMetaData() != null
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreference.java b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
index 481538d..93df2f1 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreference.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
@@ -69,6 +69,8 @@
private boolean mDefaultTextColorSet;
private int mDefaultTextColor;
private int mNumPlans;
+ /** The specified un-initialized value for cycle time */
+ private final long CYCLE_TIME_UNINITIAL_VALUE = 0;
/** The ending time of the billing cycle in milliseconds since epoch. */
private long mCycleEndTimeMs;
/** The time of the last update in standard milliseconds since the epoch */
@@ -94,6 +96,7 @@
/** WiFi only mode */
private boolean mWifiMode;
private String mUsagePeriod;
+ private boolean mSingleWifi; // Shows only one specified WiFi network usage
public DataUsageSummaryPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -142,9 +145,10 @@
notifyChanged();
}
- void setWifiMode(boolean isWifiMode, String usagePeriod) {
+ void setWifiMode(boolean isWifiMode, String usagePeriod, boolean isSingleWifi) {
mWifiMode = isWifiMode;
mUsagePeriod = usagePeriod;
+ mSingleWifi = isSingleWifi;
notifyChanged();
}
@@ -171,7 +175,16 @@
Button launchButton = (Button) holder.findViewById(R.id.launch_mdp_app_button);
TextView limitInfo = (TextView) holder.findViewById(R.id.data_limits);
- if (mWifiMode) {
+ if (mWifiMode && mSingleWifi) {
+ updateCycleTimeText(holder);
+
+ usageTitle.setVisibility(View.GONE);
+ launchButton.setVisibility(View.GONE);
+ carrierInfo.setVisibility(View.GONE);
+
+ limitInfo.setVisibility(TextUtils.isEmpty(mLimitInfoText) ? View.GONE : View.VISIBLE);
+ limitInfo.setText(mLimitInfoText);
+ } else if (mWifiMode) {
usageTitle.setText(R.string.data_usage_wifi_title);
usageTitle.setVisibility(View.VISIBLE);
TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
@@ -265,6 +278,13 @@
private void updateCycleTimeText(PreferenceViewHolder holder) {
TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
+ // Takes zero as a special case which value is never set.
+ if (mCycleEndTimeMs == CYCLE_TIME_UNINITIAL_VALUE) {
+ cycleTime.setVisibility(View.GONE);
+ return;
+ }
+
+ cycleTime.setVisibility(View.VISIBLE);
long millisLeft = mCycleEndTimeMs - System.currentTimeMillis();
if (millisLeft <= 0) {
cycleTime.setText(getContext().getString(R.string.billing_cycle_none_left));
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
index 353c5ee..a06bb77 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -65,10 +65,10 @@
private final EntityHeaderController mEntityHeaderController;
private final Lifecycle mLifecycle;
private final PreferenceFragmentCompat mFragment;
- private final DataUsageController mDataUsageController;
- private final DataUsageInfoController mDataInfoController;
+ protected final DataUsageController mDataUsageController;
+ protected final DataUsageInfoController mDataInfoController;
private final NetworkTemplate mDefaultTemplate;
- private final NetworkPolicyEditor mPolicyEditor;
+ protected final NetworkPolicyEditor mPolicyEditor;
private final int mDataUsageTemplate;
private final boolean mHasMobileData;
private final SubscriptionManager mSubscriptionManager;
@@ -200,11 +200,13 @@
if (DataUsageUtils.hasSim(mActivity)) {
info = mDataUsageController.getDataUsageInfo(mDefaultTemplate);
mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate));
- summaryPreference.setWifiMode(/* isWifiMode */ false, /* usagePeriod */ null);
+ summaryPreference.setWifiMode(/* isWifiMode */ false,
+ /* usagePeriod */ null, /* isSingleWifi */ false);
} else {
info = mDataUsageController.getDataUsageInfo(
NetworkTemplate.buildTemplateWifiWildcard());
- summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */ info.period);
+ summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */
+ info.period, /* isSingleWifi */ false);
summaryPreference.setLimitInfo(null);
summaryPreference.setUsageNumbers(info.usageLevel,
/* dataPlanSize */ -1L,
diff --git a/src/com/android/settings/datausage/WifiDataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/WifiDataUsageSummaryPreferenceController.java
new file mode 100644
index 0000000..8b6d10a
--- /dev/null
+++ b/src/com/android/settings/datausage/WifiDataUsageSummaryPreferenceController.java
@@ -0,0 +1,72 @@
+/*
+ * 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.datausage;
+
+import android.app.Activity;
+import android.net.NetworkTemplate;
+import android.telephony.SubscriptionManager;
+import android.text.format.Formatter;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.net.DataUsageController;
+
+/**
+ * The controller displays a data usage chart for the specified Wi-Fi network.
+ */
+public class WifiDataUsageSummaryPreferenceController extends DataUsageSummaryPreferenceController {
+ final String mNetworkId;
+
+ public WifiDataUsageSummaryPreferenceController(Activity activity,
+ Lifecycle lifecycle, PreferenceFragmentCompat fragment, CharSequence networkId) {
+ super(activity, lifecycle, fragment, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ if (networkId == null) {
+ mNetworkId = null;
+ } else {
+ mNetworkId = String.valueOf(networkId);
+ }
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (preference == null) {
+ return;
+ }
+
+ final DataUsageSummaryPreference mPreference = (DataUsageSummaryPreference) preference;
+ // TODO(b/126299427): Currently gets data usage of whole Wi-Fi networks, but should get
+ // specified one.
+ final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(mNetworkId);
+ final DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
+ template);
+ mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(template));
+
+ mPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */
+ info.period, /* isSingleWifi */ true);
+ mPreference.setChartEnabled(true);
+ // Treats Wi-Fi network as unlimited network, which has same usage level and limited level.
+ mPreference.setUsageNumbers(info.usageLevel, info.usageLevel, /* hasMobileData */ false);
+
+ // TODO(b/126142293): Passpoint Wi-Fi should have limit of data usage and time remaining
+ mPreference.setProgress(100);
+ mPreference.setLabels(Formatter.formatFileSize(mContext, /* sizeBytes */ 0),
+ DataUsageUtils.formatDataUsage(mContext, info.usageLevel));
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
index ff980b2..66735c8 100644
--- a/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
+++ b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
@@ -77,9 +77,10 @@
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- switch(viewType) {
+ switch (viewType) {
case TYPE_HEADER: {
- final View view = inflater.inflate(R.layout.preference_category_material,
+ final View view = inflater.inflate(
+ R.layout.time_zone_search_header,
parent, false);
return new HeaderViewHolder(view);
}
@@ -136,7 +137,8 @@
return mShowHeader && position == 0;
}
- public @NonNull ArrayFilter getFilter() {
+ @NonNull
+ public ArrayFilter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
@@ -153,14 +155,18 @@
public interface AdapterItem {
CharSequence getTitle();
+
CharSequence getSummary();
+
String getIconText();
+
String getCurrentTime();
/**
* @return unique non-negative number
*/
long getItemId();
+
String[] getSearchKeys();
}
diff --git a/src/com/android/settings/development/qstile/DevelopmentTiles.java b/src/com/android/settings/development/qstile/DevelopmentTiles.java
index 5edbc70..bb791ab 100644
--- a/src/com/android/settings/development/qstile/DevelopmentTiles.java
+++ b/src/com/android/settings/development/qstile/DevelopmentTiles.java
@@ -16,9 +16,12 @@
package com.android.settings.development.qstile;
+import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
+import android.app.KeyguardManager;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
@@ -38,6 +41,8 @@
import com.android.internal.app.LocalePicker;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.development.SystemPropPoker;
@@ -273,4 +278,45 @@
}
}
}
-}
\ No newline at end of file
+
+ /**
+ * Tile to toggle sensors off to control camera, mic, and sensors managed by the SensorManager.
+ */
+ public static class SensorsOff extends DevelopmentTiles {
+ private Context mContext;
+ private SensorPrivacyManager mSensorPrivacyManager;
+ private KeyguardManager mKeyguardManager;
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+ private boolean mIsEnabled;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mContext = getApplicationContext();
+ mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService(
+ Context.SENSOR_PRIVACY_SERVICE);
+ mIsEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled();
+ mMetricsFeatureProvider = FeatureFactory.getFactory(
+ mContext).getMetricsFeatureProvider();
+ mKeyguardManager = (KeyguardManager) mContext.getSystemService(
+ Context.KEYGUARD_SERVICE);
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ @Override
+ public void setIsEnabled(boolean isEnabled) {
+ // Don't allow sensors to be reenabled from the lock screen.
+ if (mIsEnabled && mKeyguardManager.isKeyguardLocked()) {
+ return;
+ }
+ mMetricsFeatureProvider.action(getApplicationContext(), SettingsEnums.QS_SENSOR_PRIVACY,
+ isEnabled);
+ mIsEnabled = isEnabled;
+ mSensorPrivacyManager.setSensorPrivacy(isEnabled);
+ }
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
index f13af94..855a988 100644
--- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
@@ -16,12 +16,8 @@
package com.android.settings.deviceinfo;
-import static android.content.Context.CLIPBOARD_SERVICE;
-
import android.app.Activity;
import android.app.settings.SettingsEnums;
-import android.content.ClipData;
-import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -79,11 +75,7 @@
@Override
public CharSequence getSummary() {
- try {
- return BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY);
- } catch (Exception e) {
- return mContext.getText(R.string.device_info_default);
- }
+ return BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY);
}
@Override
@@ -99,29 +91,7 @@
@Override
public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public boolean isSliceable() {
- return true;
- }
-
- @Override
- public boolean isCopyableSlice() {
- return true;
- }
-
- @Override
- public void copy() {
- final ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(
- CLIPBOARD_SERVICE);
- final ClipData clip = ClipData.newPlainText("text", getSummary());
- clipboard.setPrimaryClip(clip);
-
- final String toast = mContext.getString(R.string.copyable_slice_toast,
- mContext.getText(R.string.build_number));
- Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
+ return AVAILABLE_UNSEARCHABLE;
}
@Override
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java
index c7d4459..36f1a43 100644
--- a/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java
@@ -16,10 +16,16 @@
package com.android.settings.deviceinfo.firmwareversion;
+import static android.content.Context.CLIPBOARD_SERVICE;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.text.BidiFormatter;
+import android.widget.Toast;
+import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
public class SimpleBuildNumberPreferenceController extends BasePreferenceController {
@@ -38,4 +44,26 @@
public CharSequence getSummary() {
return BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY);
}
+
+ @Override
+ public boolean isSliceable() {
+ return true;
+ }
+
+ @Override
+ public boolean isCopyableSlice() {
+ return true;
+ }
+
+ @Override
+ public void copy() {
+ final ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(
+ CLIPBOARD_SERVICE);
+ final ClipData clip = ClipData.newPlainText("text", getSummary());
+ clipboard.setPrimaryClip(clip);
+
+ final String toast = mContext.getString(R.string.copyable_slice_toast,
+ mContext.getText(R.string.build_number));
+ Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
+ }
}
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index d95320f..a014c29 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -174,7 +174,8 @@
return;
}
- mTelephonyManager.listen(mPhoneStateListener,
+ mTelephonyManager.createForSubscriptionId(mSubscriptionInfo.getSubscriptionId())
+ .listen(mPhoneStateListener,
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
| PhoneStateListener.LISTEN_SERVICE_STATE);
@@ -197,8 +198,8 @@
return;
}
- mTelephonyManager.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_NONE);
+ mTelephonyManager.createForSubscriptionId(mSubscriptionInfo.getSubscriptionId())
+ .listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
if (mShowLatestAreaInfo) {
mContext.unregisterReceiver(mAreaInfoReceiver);
@@ -432,8 +433,7 @@
@VisibleForTesting
PhoneStateListener getPhoneStateListener() {
- return new PhoneStateListener(
- mSubscriptionInfo.getSubscriptionId()) {
+ return new PhoneStateListener() {
@Override
public void onDataConnectionStateChanged(int state) {
updateDataState(state);
diff --git a/src/com/android/settings/fuelgauge/BatterySaverController.java b/src/com/android/settings/fuelgauge/BatterySaverController.java
index c4ed88b..acb5e32 100644
--- a/src/com/android/settings/fuelgauge/BatterySaverController.java
+++ b/src/com/android/settings/fuelgauge/BatterySaverController.java
@@ -90,10 +90,10 @@
final int percent = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
final int mode = Settings.Global.getInt(resolver,
- Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
if (isPowerSaveOn) {
return mContext.getString(R.string.battery_saver_on_summary);
- } else if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+ } else if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE) {
if (percent != 0) {
return mContext.getString(R.string.battery_saver_off_scheduled_summary,
Utils.formatPercentage(percent));
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
index 2cc73d1..0b129ef 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java
@@ -61,9 +61,9 @@
@Override
public CharSequence getSummary() {
final ContentResolver resolver = mContext.getContentResolver();
- final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
- if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+ final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
+ if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE) {
final int threshold =
Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
if (threshold <= 0) {
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
index d49eb0a..796df46 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java
@@ -29,7 +29,7 @@
*
* Will call the appropriate power manager APIs and modify the correct settings to enable
* users to control their automatic battery saver toggling preferences.
- * See {@link Settings.Global#AUTOMATIC_POWER_SAVER_MODE} for more details.
+ * See {@link Settings.Global#AUTOMATIC_POWER_SAVE_MODE} for more details.
*/
public class BatterySaverScheduleRadioButtonsController {
@@ -48,12 +48,12 @@
public String getDefaultKey() {
final ContentResolver resolver = mContext.getContentResolver();
- // Note: this can also be obtained via PowerManager.getPowerSaveMode()
- final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ // Note: this can also be obtained via PowerManager.getPowerSaveModeTrigger()
+ final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
// if mode is "dynamic" we are in routine mode, percentage with non-zero threshold is
// percentage mode, otherwise it is no schedule mode
- if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+ if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE) {
final int threshold =
Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
if (threshold <= 0) {
@@ -68,18 +68,18 @@
final ContentResolver resolver = mContext.getContentResolver();
switch(key) {
case KEY_NO_SCHEDULE:
- Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
break;
case KEY_PERCENTAGE:
- Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
break;
case KEY_ROUTINE:
- Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_DYNAMIC);
+ Settings.Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC);
break;
default:
throw new IllegalStateException(
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
index 6253947..b704fde 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
@@ -39,7 +39,7 @@
*
* Will call the appropriate power manager APIs and modify the correct settings to enable
* users to control their automatic battery saver toggling preferences.
- * See {@link Settings.Global#AUTOMATIC_POWER_SAVER_MODE} for more details.
+ * See {@link Settings.Global#AUTOMATIC_POWER_SAVE_MODE} for more details.
*/
public class BatterySaverScheduleSeekBarController implements
OnPreferenceChangeListener {
@@ -76,12 +76,12 @@
public void updateSeekBar() {
final ContentResolver resolver = mContext.getContentResolver();
- // Note: this can also be obtained via PowerManager.getPowerSaveMode()
- final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ // Note: this can also be obtained via PowerManager.getPowerSaveModeTrigger()
+ final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
// if mode is "dynamic" we are in routine mode, percentage with non-zero threshold is
// percentage mode, otherwise it is no schedule mode
- if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
+ if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE) {
final int threshold =
Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
if (threshold <= 0) {
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceController.java
index b14fec9..7a1f7f5 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceController.java
@@ -19,7 +19,7 @@
@Override
public void updateState(Preference preference) {
- int setting = Settings.System.getInt(mContext.getContentResolver(),
+ int setting = Settings.Global.getInt(mContext.getContentResolver(),
LOW_POWER_STICKY_AUTO_DISABLE_ENABLED, 1);
((SwitchPreference) preference).setChecked(setting == 0);
@@ -28,7 +28,7 @@
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean keepActive = (Boolean) newValue;
- Settings.System.putInt(mContext.getContentResolver(),
+ Settings.Global.putInt(mContext.getContentResolver(),
LOW_POWER_STICKY_AUTO_DISABLE_ENABLED,
keepActive ? 0 : 1);
return true;
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java b/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java
index f75588c..bb69999 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java
@@ -59,7 +59,7 @@
mDatabaseHelper = AnomalyDatabaseHelper.getInstance(context);
}
- public static BatteryDatabaseManager getInstance(Context context) {
+ public static synchronized BatteryDatabaseManager getInstance(Context context) {
if (sSingleton == null) {
sSingleton = new BatteryDatabaseManager(context);
}
@@ -84,15 +84,15 @@
public synchronized boolean insertAnomaly(int uid, String packageName, int type,
int anomalyState,
long timestampMs) {
- try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
- ContentValues values = new ContentValues();
- values.put(UID, uid);
- values.put(PACKAGE_NAME, packageName);
- values.put(ANOMALY_TYPE, type);
- values.put(ANOMALY_STATE, anomalyState);
- values.put(TIME_STAMP_MS, timestampMs);
- return db.insertWithOnConflict(TABLE_ANOMALY, null, values, CONFLICT_IGNORE) != -1;
- }
+ final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(UID, uid);
+ values.put(PACKAGE_NAME, packageName);
+ values.put(ANOMALY_TYPE, type);
+ values.put(ANOMALY_STATE, anomalyState);
+ values.put(TIME_STAMP_MS, timestampMs);
+
+ return db.insertWithOnConflict(TABLE_ANOMALY, null, values, CONFLICT_IGNORE) != -1;
}
/**
@@ -100,43 +100,41 @@
*/
public synchronized List<AppInfo> queryAllAnomalies(long timestampMsAfter, int state) {
final List<AppInfo> appInfos = new ArrayList<>();
- try (SQLiteDatabase db = mDatabaseHelper.getReadableDatabase()) {
- final String[] projection = {PACKAGE_NAME, ANOMALY_TYPE, UID};
- final String orderBy = AnomalyDatabaseHelper.AnomalyColumns.TIME_STAMP_MS + " DESC";
- final Map<Integer, AppInfo.Builder> mAppInfoBuilders = new ArrayMap<>();
- final String selection = TIME_STAMP_MS + " > ? AND " + ANOMALY_STATE + " = ? ";
- final String[] selectionArgs = new String[]{String.valueOf(timestampMsAfter),
- String.valueOf(state)};
+ final SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
+ final String[] projection = {PACKAGE_NAME, ANOMALY_TYPE, UID};
+ final String orderBy = AnomalyDatabaseHelper.AnomalyColumns.TIME_STAMP_MS + " DESC";
+ final Map<Integer, AppInfo.Builder> mAppInfoBuilders = new ArrayMap<>();
+ final String selection = TIME_STAMP_MS + " > ? AND " + ANOMALY_STATE + " = ? ";
+ final String[] selectionArgs = new String[]{String.valueOf(timestampMsAfter),
+ String.valueOf(state)};
- try (Cursor cursor = db.query(TABLE_ANOMALY, projection, selection, selectionArgs,
- null /* groupBy */, null /* having */, orderBy)) {
- while (cursor.moveToNext()) {
- final int uid = cursor.getInt(cursor.getColumnIndex(UID));
- if (!mAppInfoBuilders.containsKey(uid)) {
- final AppInfo.Builder builder = new AppInfo.Builder()
- .setUid(uid)
- .setPackageName(
- cursor.getString(cursor.getColumnIndex(PACKAGE_NAME)));
- mAppInfoBuilders.put(uid, builder);
- }
- mAppInfoBuilders.get(uid).addAnomalyType(
- cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE)));
+ try (Cursor cursor = db.query(TABLE_ANOMALY, projection, selection, selectionArgs,
+ null /* groupBy */, null /* having */, orderBy)) {
+ while (cursor.moveToNext()) {
+ final int uid = cursor.getInt(cursor.getColumnIndex(UID));
+ if (!mAppInfoBuilders.containsKey(uid)) {
+ final AppInfo.Builder builder = new AppInfo.Builder()
+ .setUid(uid)
+ .setPackageName(
+ cursor.getString(cursor.getColumnIndex(PACKAGE_NAME)));
+ mAppInfoBuilders.put(uid, builder);
}
+ mAppInfoBuilders.get(uid).addAnomalyType(
+ cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE)));
}
+ }
- for (Integer uid : mAppInfoBuilders.keySet()) {
- appInfos.add(mAppInfoBuilders.get(uid).build());
- }
+ for (Integer uid : mAppInfoBuilders.keySet()) {
+ appInfos.add(mAppInfoBuilders.get(uid).build());
}
return appInfos;
}
public synchronized void deleteAllAnomaliesBeforeTimeStamp(long timestampMs) {
- try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
- db.delete(TABLE_ANOMALY, TIME_STAMP_MS + " < ?",
- new String[]{String.valueOf(timestampMs)});
- }
+ final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+ db.delete(TABLE_ANOMALY, TIME_STAMP_MS + " < ?",
+ new String[]{String.valueOf(timestampMs)});
}
/**
@@ -152,12 +150,12 @@
for (int i = 0; i < size; i++) {
whereArgs[i] = appInfos.get(i).packageName;
}
- try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
- final ContentValues values = new ContentValues();
- values.put(ANOMALY_STATE, state);
- db.update(TABLE_ANOMALY, values, PACKAGE_NAME + " IN (" + TextUtils.join(",",
- Collections.nCopies(appInfos.size(), "?")) + ")", whereArgs);
- }
+
+ final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.put(ANOMALY_STATE, state);
+ db.update(TABLE_ANOMALY, values, PACKAGE_NAME + " IN (" + TextUtils.join(",",
+ Collections.nCopies(appInfos.size(), "?")) + ")", whereArgs);
}
}
@@ -170,21 +168,20 @@
public synchronized SparseLongArray queryActionTime(
@AnomalyDatabaseHelper.ActionType int type) {
final SparseLongArray timeStamps = new SparseLongArray();
- try (SQLiteDatabase db = mDatabaseHelper.getReadableDatabase()) {
- final String[] projection = {ActionColumns.UID, ActionColumns.TIME_STAMP_MS};
- final String selection = ActionColumns.ACTION_TYPE + " = ? ";
- final String[] selectionArgs = new String[]{String.valueOf(type)};
+ final SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
+ final String[] projection = {ActionColumns.UID, ActionColumns.TIME_STAMP_MS};
+ final String selection = ActionColumns.ACTION_TYPE + " = ? ";
+ final String[] selectionArgs = new String[]{String.valueOf(type)};
- try (Cursor cursor = db.query(TABLE_ACTION, projection, selection, selectionArgs,
- null /* groupBy */, null /* having */, null /* orderBy */)) {
- final int uidIndex = cursor.getColumnIndex(ActionColumns.UID);
- final int timestampIndex = cursor.getColumnIndex(ActionColumns.TIME_STAMP_MS);
+ try (Cursor cursor = db.query(TABLE_ACTION, projection, selection, selectionArgs,
+ null /* groupBy */, null /* having */, null /* orderBy */)) {
+ final int uidIndex = cursor.getColumnIndex(ActionColumns.UID);
+ final int timestampIndex = cursor.getColumnIndex(ActionColumns.TIME_STAMP_MS);
- while (cursor.moveToNext()) {
- final int uid = cursor.getInt(uidIndex);
- final long timeStamp = cursor.getLong(timestampIndex);
- timeStamps.append(uid, timeStamp);
- }
+ while (cursor.moveToNext()) {
+ final int uid = cursor.getInt(uidIndex);
+ final long timeStamp = cursor.getLong(timestampIndex);
+ timeStamps.append(uid, timeStamp);
}
}
@@ -196,14 +193,14 @@
*/
public synchronized boolean insertAction(@AnomalyDatabaseHelper.ActionType int type,
int uid, String packageName, long timestampMs) {
- try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
- final ContentValues values = new ContentValues();
- values.put(ActionColumns.UID, uid);
- values.put(ActionColumns.PACKAGE_NAME, packageName);
- values.put(ActionColumns.ACTION_TYPE, type);
- values.put(ActionColumns.TIME_STAMP_MS, timestampMs);
- return db.insertWithOnConflict(TABLE_ACTION, null, values, CONFLICT_REPLACE) != -1;
- }
+ final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.put(ActionColumns.UID, uid);
+ values.put(ActionColumns.PACKAGE_NAME, packageName);
+ values.put(ActionColumns.ACTION_TYPE, type);
+ values.put(ActionColumns.TIME_STAMP_MS, timestampMs);
+
+ return db.insertWithOnConflict(TABLE_ACTION, null, values, CONFLICT_REPLACE) != -1;
}
/**
@@ -211,14 +208,13 @@
*/
public synchronized boolean deleteAction(@AnomalyDatabaseHelper.ActionType int type,
int uid, String packageName) {
- try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
- final String where =
- ActionColumns.ACTION_TYPE + " = ? AND " + ActionColumns.UID + " = ? AND "
- + ActionColumns.PACKAGE_NAME + " = ? ";
- final String[] whereArgs = new String[]{String.valueOf(type), String.valueOf(uid),
- String.valueOf(packageName)};
+ SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+ final String where =
+ ActionColumns.ACTION_TYPE + " = ? AND " + ActionColumns.UID + " = ? AND "
+ + ActionColumns.PACKAGE_NAME + " = ? ";
+ final String[] whereArgs = new String[]{String.valueOf(type), String.valueOf(uid),
+ String.valueOf(packageName)};
- return db.delete(TABLE_ACTION, where, whereArgs) != 0;
- }
+ return db.delete(TABLE_ACTION, where, whereArgs) != 0;
}
}
diff --git a/src/com/android/settings/gestures/SystemNavigationPreferenceController.java b/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
index a11754e..e1be9d4 100644
--- a/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
+++ b/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
@@ -51,13 +51,15 @@
private static final String ENABLE_ASSISTANT_GESTURE = "ENABLE_ASSISTANT_GESTURE";
private static final String PROTOTYPE_ENABLED = "prototype_enabled";
- private static final int EDGE_SENSITIVITY_WIDTH = 32;
+ private static final int EDGE_SENSITIVITY_WIDTH = 48;
private static final String EDGE_SENSITIVITY_KEY = "quickstepcontroller_edge_width_sensitivity";
private static final String GESTURES_MATCH_MAP_OFF = "000000";
private static final String GESTURES_MATCH_MAP_ON = "071133";
private static final String GESTURES_MATCH_MAP_KEY = "quickstepcontroller_gesture_match_map";
+ private static final String OVERLAY_NAVBAR_KEY =
+ "com.android.internal.experiment.navbar.default";
private static final String OVERLAY_NAVBAR_TYPE_INSET =
"com.android.internal.experiment.navbar.type.inset";
private static final String OVERLAY_NAVBAR_TYPE_FLOATING =
@@ -174,6 +176,7 @@
.asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE));
if (overlayManager != null) {
try {
+ overlayManager.setEnabled(OVERLAY_NAVBAR_KEY, true, USER_SYSTEM);
overlayManager.setEnabled(OVERLAY_NAVBAR_TYPE_FLOATING, false, USER_SYSTEM);
overlayManager.setEnabled(OVERLAY_NAVBAR_TYPE_INSET, enable, USER_SYSTEM);
} catch (RemoteException e) {
diff --git a/src/com/android/settings/gestures/WakeScreenGesturePreferenceController.java b/src/com/android/settings/gestures/WakeScreenGesturePreferenceController.java
deleted file mode 100644
index e9d03d7..0000000
--- a/src/com/android/settings/gestures/WakeScreenGesturePreferenceController.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.gestures;
-
-import static android.provider.Settings.Secure.DOZE_WAKE_SCREEN_GESTURE;
-
-import android.annotation.UserIdInt;
-import android.content.Context;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.aware.AwareFeatureProvider;
-import com.android.settings.overlay.FeatureFactory;
-
-public class WakeScreenGesturePreferenceController extends GesturePreferenceController {
-
- private static final int ON = 1;
- private static final int OFF = 0;
-
- private static final String PREF_KEY_VIDEO = "gesture_wake_screen_video";
-
- private final AwareFeatureProvider mFeatureProvider;
- private AmbientDisplayConfiguration mAmbientConfig;
- @UserIdInt
- private final int mUserId;
-
- public WakeScreenGesturePreferenceController(Context context, String key) {
- super(context, key);
- mUserId = UserHandle.myUserId();
- mFeatureProvider = FeatureFactory.getFactory(context).getAwareFeatureProvider();
- }
-
- @Override
- public int getAvailabilityStatus() {
- if (!getAmbientConfig().wakeScreenGestureAvailable()
- || !mFeatureProvider.isSupported(mContext)) {
- return UNSUPPORTED_ON_DEVICE;
- }
-
- if (!mFeatureProvider.isEnabled(mContext)) {
- return CONDITIONALLY_UNAVAILABLE;
- }
-
- return getAmbientConfig().alwaysOnEnabled(mUserId)
- ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
- }
-
- @Override
- protected boolean canHandleClicks() {
- return getAmbientConfig().alwaysOnEnabled(mUserId);
- }
-
- @Override
- public boolean isSliceable() {
- return TextUtils.equals(getPreferenceKey(), "gesture_wake_screen");
- }
-
- @Override
- protected String getVideoPrefKey() {
- return PREF_KEY_VIDEO;
- }
-
- @Override
- public boolean isChecked() {
- return getAmbientConfig().wakeScreenGestureEnabled(mUserId);
- }
-
- @Override
- public boolean setChecked(boolean isChecked) {
- return Settings.Secure.putInt(mContext.getContentResolver(), DOZE_WAKE_SCREEN_GESTURE,
- isChecked ? ON : OFF);
- }
-
- private AmbientDisplayConfiguration getAmbientConfig() {
- if (mAmbientConfig == null) {
- mAmbientConfig = new AmbientDisplayConfiguration(mContext);
- }
-
- return mAmbientConfig;
- }
-
- @VisibleForTesting
- public void setConfig(AmbientDisplayConfiguration config) {
- mAmbientConfig = config;
- }
-}
diff --git a/src/com/android/settings/gestures/WakeScreenGestureSettings.java b/src/com/android/settings/gestures/WakeScreenGestureSettings.java
deleted file mode 100644
index b8a782a..0000000
--- a/src/com/android/settings/gestures/WakeScreenGestureSettings.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.gestures;
-
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.provider.SearchIndexableResource;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.SearchIndexable;
-
-import java.util.Arrays;
-import java.util.List;
-
-@SearchIndexable
-public class WakeScreenGestureSettings extends DashboardFragment {
-
- private static final String TAG = "WakeScreenGestureSettings";
-
- public static final String PREF_KEY_SUGGESTION_COMPLETE =
- "pref_wake_screen_gesture_suggestion_complete";
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.SETTINGS_GESTURE_WAKE_SCREEN;
- }
-
- @Override
- protected String getLogTag() {
- return TAG;
- }
-
- @Override
- protected int getPreferenceScreenResId() {
- return R.xml.wake_screen_gesture_settings;
- }
-
- public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider() {
- @Override
- public List<SearchIndexableResource> getXmlResourcesToIndex(
- Context context, boolean enabled) {
- final SearchIndexableResource sir = new SearchIndexableResource(context);
- sir.xmlResId = R.xml.wake_screen_gesture_settings;
- return Arrays.asList(sir);
- }
- };
-
-}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 9e869a9..9b83f88 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -17,6 +17,7 @@
package com.android.settings.homepage;
import android.animation.LayoutTransition;
+import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.view.View;
@@ -53,7 +54,10 @@
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(this, avatarView);
getLifecycle().addObserver(avatarViewMixin);
- showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
+ if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
+ // Only allow contextual feature on high ram devices.
+ showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
+ }
showFragment(new TopLevelSettings(), R.id.main_content);
((FrameLayout) findViewById(R.id.main_content))
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
index 43e90e8..ce7777a 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
@@ -139,7 +139,7 @@
final String action = mContext.getString(R.string.config_settingsintelligence_log_action);
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
index 13564b5..32f505e 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -17,8 +17,8 @@
package com.android.settings.homepage.contextualcards;
import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI;
+import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI;
import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
-import static com.android.settings.slices.CustomSliceRegistry.NOTIFICATION_CHANNEL_SLICE_URI;
import android.content.Context;
import android.database.ContentObserver;
@@ -67,7 +67,8 @@
@VisibleForTesting
Uri mNotifyUri;
- private Context mContext;
+
+ private final Context mContext;
ContextualCardLoader(Context context) {
super(context);
@@ -186,7 +187,6 @@
// Collect future and eligible cards
for (Future<ContextualCard> cardFuture : eligibleCards) {
try {
- //TODO(b/124492762): Log latency and timeout occurrence.
final ContextualCard card = cardFuture.get(ELIGIBILITY_CHECKER_TIMEOUT_MS,
TimeUnit.MILLISECONDS);
if (card != null) {
@@ -208,7 +208,7 @@
private boolean isLargeCard(ContextualCard card) {
return card.getSliceUri().equals(CONTEXTUAL_WIFI_SLICE_URI)
|| card.getSliceUri().equals(BLUETOOTH_DEVICES_SLICE_URI)
- || card.getSliceUri().equals(NOTIFICATION_CHANNEL_SLICE_URI);
+ || card.getSliceUri().equals(CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI);
}
public interface CardContentLoaderListener {
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index c829015..92108f0 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
@@ -22,6 +22,7 @@
import static java.util.stream.Collectors.groupingBy;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings;
@@ -38,9 +39,12 @@
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.ArrayList;
import java.util.List;
@@ -96,7 +100,6 @@
mLifecycleObservers = new ArrayList<>();
mControllerRendererPool = new ControllerRendererPool();
mLifecycle.addObserver(this);
-
if (savedInstanceState == null) {
mIsFirstLaunch = true;
mSavedCards = null;
@@ -109,13 +112,13 @@
}
}
- void loadContextualCards(ContextualCardsFragment fragment) {
+ void loadContextualCards(LoaderManager loaderManager) {
mStartTime = System.currentTimeMillis();
final CardContentLoaderCallbacks cardContentLoaderCallbacks =
new CardContentLoaderCallbacks(mContext);
cardContentLoaderCallbacks.setListener(this);
// Use the cached data when navigating back to the first page and upon screen rotation.
- LoaderManager.getInstance(fragment).initLoader(CARD_CONTENT_LOADER_ID, null /* bundle */,
+ loaderManager.initLoader(CARD_CONTENT_LOADER_ID, null /* bundle */,
cardContentLoaderCallbacks);
}
@@ -195,8 +198,8 @@
@Override
public void onFinishCardLoading(List<ContextualCard> cards) {
final long loadTime = System.currentTimeMillis() - mStartTime;
- //TODO(b/123668403): remove the log here once we do the change with FutureTask
Log.d(TAG, "Total loading time = " + loadTime);
+
final List<ContextualCard> cardsToKeep = getCardsToKeep(cards);
//navigate back to the homepage, screen rotate or after card dismissal
@@ -206,15 +209,25 @@
return;
}
- //only log homepage display upon a fresh launch
+ final MetricsFeatureProvider metricsFeatureProvider =
+ FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
final long timeoutLimit = getCardLoaderTimeout(mContext);
if (loadTime <= timeoutLimit) {
onContextualCardUpdated(cards.stream()
.collect(groupingBy(ContextualCard::getCardType)));
+ } else {
+ // log timeout occurrence
+ metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
+ SettingsEnums.ACTION_CONTEXTUAL_CARD_LOAD_TIMEOUT,
+ SettingsEnums.SETTINGS_HOMEPAGE,
+ null /* key */, (int) loadTime /* value */);
}
+ //only log homepage display upon a fresh launch
final long totalTime = System.currentTimeMillis() - mStartTime;
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext)
.logHomepageDisplay(totalTime);
+ metricsFeatureProvider.action(mContext,
+ SettingsEnums.ACTION_CONTEXTUAL_HOME_SHOW, (int) totalTime);
mIsFirstLaunch = false;
}
@@ -228,6 +241,21 @@
outState.putStringArrayList(KEY_CONTEXTUAL_CARDS, cards);
}
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ // Duplicate a list to avoid java.util.ConcurrentModificationException.
+ final List<ContextualCard> cards = new ArrayList<>(mContextualCards);
+ for (ContextualCard card : cards) {
+ final ContextualCardController controller = mControllerRendererPool
+ .getController(mContext, card.getCardType());
+ if (hasWindowFocus && controller instanceof OnStart) {
+ ((OnStart) controller).onStart();
+ }
+ if (!hasWindowFocus && controller instanceof OnStop) {
+ ((OnStop) controller).onStop();
+ }
+ }
+ }
+
public ControllerRendererPool getControllerRendererPool() {
return mControllerRendererPool;
}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
index e598e4c..bd25866 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
@@ -19,22 +19,25 @@
import static com.android.settings.homepage.contextualcards.ContextualCardsAdapter.SPAN_COUNT;
import android.app.settings.SettingsEnums;
+import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.loader.app.LoaderManager;
import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
+import com.android.settings.overlay.FeatureFactory;
-public class ContextualCardsFragment extends InstrumentedFragment {
+public class ContextualCardsFragment extends InstrumentedFragment implements
+ FocusRecyclerView.FocusListener {
private static final String TAG = "ContextualCardsFragment";
- private RecyclerView mCardsContainer;
+ private FocusRecyclerView mCardsContainer;
private GridLayoutManager mLayoutManager;
private ContextualCardsAdapter mContextualCardsAdapter;
private ContextualCardManager mContextualCardManager;
@@ -42,14 +45,19 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle(),
+ final Context context = getContext();
+ if (savedInstanceState == null) {
+ FeatureFactory.getFactory(context).getSlicesFeatureProvider().newUiSession();
+ }
+ mContextualCardManager = new ContextualCardManager(context, getSettingsLifecycle(),
savedInstanceState);
+
}
@Override
public void onStart() {
super.onStart();
- mContextualCardManager.loadContextualCards(this);
+ mContextualCardManager.loadContextualCards(LoaderManager.getInstance(this));
}
@Override
@@ -64,11 +72,17 @@
this /* lifecycleOwner */, mContextualCardManager);
mCardsContainer.setAdapter(mContextualCardsAdapter);
mContextualCardManager.setListener(mContextualCardsAdapter);
+ mCardsContainer.setListener(this);
return rootView;
}
@Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ mContextualCardManager.onWindowFocusChanged(hasWindowFocus);
+ }
+
+ @Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_HOMEPAGE;
}
diff --git a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
index 5423ce3..811aaa2 100644
--- a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
+++ b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
@@ -18,6 +18,7 @@
import static android.app.slice.Slice.HINT_ERROR;
+import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
@@ -27,6 +28,9 @@
import androidx.slice.Slice;
import androidx.slice.SliceViewManager;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -46,11 +50,39 @@
@Override
public ContextualCard call() throws Exception {
- return isCardEligibleToDisplay(mCard) ? mCard : null;
+ final long startTime = System.currentTimeMillis();
+ final MetricsFeatureProvider metricsFeatureProvider =
+ FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
+ ContextualCard result;
+
+ if (isCardEligibleToDisplay(mCard)) {
+ metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
+ SettingsEnums.ACTION_CONTEXTUAL_CARD_ELIGIBILITY,
+ SettingsEnums.SETTINGS_HOMEPAGE,
+ mCard.getTextSliceUri() /* key */, 1 /* true */);
+ result = mCard;
+ } else {
+ metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
+ SettingsEnums.ACTION_CONTEXTUAL_CARD_ELIGIBILITY,
+ SettingsEnums.SETTINGS_HOMEPAGE,
+ mCard.getTextSliceUri() /* key */, 0 /* false */);
+ result = null;
+ }
+ // Log individual card loading time
+ metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
+ SettingsEnums.ACTION_CONTEXTUAL_CARD_LOAD,
+ SettingsEnums.SETTINGS_HOMEPAGE,
+ mCard.getTextSliceUri() /* key */,
+ (int) (System.currentTimeMillis() - startTime) /* value */);
+
+ return result;
}
@VisibleForTesting
boolean isCardEligibleToDisplay(ContextualCard card) {
+ if (card.getRankingScore() < 0) {
+ return false;
+ }
if (card.isCustomCard()) {
return true;
}
diff --git a/src/com/android/settings/homepage/contextualcards/FocusRecyclerView.java b/src/com/android/settings/homepage/contextualcards/FocusRecyclerView.java
new file mode 100644
index 0000000..a2ec9af
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/FocusRecyclerView.java
@@ -0,0 +1,36 @@
+package com.android.settings.homepage.contextualcards;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class FocusRecyclerView extends RecyclerView {
+
+ private FocusListener mListener;
+
+ public FocusRecyclerView(Context context) {
+ super(context);
+ }
+
+ public FocusRecyclerView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (mListener != null) {
+ mListener.onWindowFocusChanged(hasWindowFocus);
+ }
+ }
+
+ public void setListener(FocusListener listener) {
+ mListener = listener;
+ }
+
+ public interface FocusListener {
+ void onWindowFocusChanged(boolean hasWindowFocus);
+ }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
index 7bbe430..86fee03 100644
--- a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
@@ -56,10 +56,12 @@
.setCardName(CustomSliceRegistry.BATTERY_FIX_SLICE_URI.toString())
.setCardCategory(ContextualCard.Category.IMPORTANT)
.build();
+ final String contextualNotificationChannelSliceUri =
+ CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI.toString();
final ContextualCard notificationChannelCard =
ContextualCard.newBuilder()
- .setSliceUri(CustomSliceRegistry.NOTIFICATION_CHANNEL_SLICE_URI.toString())
- .setCardName(CustomSliceRegistry.NOTIFICATION_CHANNEL_SLICE_URI.toString())
+ .setSliceUri(contextualNotificationChannelSliceUri)
+ .setCardName(contextualNotificationChannelSliceUri)
.setCardCategory(ContextualCard.Category.POSSIBLE)
.build();
final ContextualCardList cards = ContextualCardList.newBuilder()
@@ -72,4 +74,4 @@
return cards;
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
index c741b98..66f6c81 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java
@@ -162,6 +162,7 @@
mCardControllers.add(new RingerVibrateConditionController(mAppContext, this /* manager */));
mCardControllers.add(new RingerMutedConditionController(mAppContext, this /* manager */));
mCardControllers.add(new WorkModeConditionController(mAppContext, this /* manager */));
+ mCardControllers.add(new GrayscaleConditionController(mAppContext, this /* manager */));
}
/**
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionController.java b/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionController.java
new file mode 100644
index 0000000..664707d
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionController.java
@@ -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.homepage.contextualcards.conditional;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.ColorDisplayManager;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.homepage.contextualcards.ContextualCard;
+
+import java.net.URISyntaxException;
+import java.util.Objects;
+
+public class GrayscaleConditionController implements ConditionalCardController {
+ static final int ID = Objects.hash("GrayscaleConditionController");
+
+ private static final String TAG = "GrayscaleCondition";
+
+ private final Context mAppContext;
+ private final ConditionManager mConditionManager;
+ private final ColorDisplayManager mColorDisplayManager;
+
+ private Intent mIntent;
+
+ public GrayscaleConditionController(Context appContext, ConditionManager conditionManager) {
+ mAppContext = appContext;
+ mConditionManager = conditionManager;
+ mColorDisplayManager = mAppContext.getSystemService(ColorDisplayManager.class);
+ }
+
+ @Override
+ public long getId() {
+ return ID;
+ }
+
+ @Override
+ public boolean isDisplayable() {
+ try {
+ mIntent = Intent.parseUri(
+ mAppContext.getString(R.string.config_grayscale_settings_intent),
+ Intent.URI_INTENT_SCHEME);
+ } catch (URISyntaxException e) {
+ Log.w(TAG, "Failure parsing grayscale settings intent, skipping", e);
+ return false;
+ }
+ return mColorDisplayManager.isSaturationActivated();
+ }
+
+ @Override
+ public void onPrimaryClick(Context context) {
+ mAppContext.startActivity(mIntent);
+ }
+
+ @Override
+ public void onActionClick() {
+ // Turn off grayscale
+ mColorDisplayManager.setSaturationLevel(100 /* staturationLevel */);
+ mConditionManager.onConditionChanged();
+ }
+
+ @Override
+ public ContextualCard buildContextualCard() {
+ return new ConditionalContextualCard.Builder()
+ .setConditionId(ID)
+ .setMetricsConstant(SettingsEnums.SETTINGS_CONDITION_GRAYSCALE_MODE)
+ .setActionText(mAppContext.getText(R.string.condition_turn_off))
+ .setName(mAppContext.getPackageName() + "/" + mAppContext.getText(
+ R.string.condition_grayscale_title))
+ .setTitleText(mAppContext.getText(R.string.condition_grayscale_title).toString())
+ .setSummaryText(
+ mAppContext.getText(R.string.condition_grayscale_summary).toString())
+ .setIconDrawable(mAppContext.getDrawable(R.drawable.ic_gray_scale_24dp))
+ .setViewType(ConditionContextualCardRenderer.VIEW_TYPE_HALF_WIDTH)
+ .build();
+ }
+
+ @Override
+ public void startMonitoringStateChange() {
+
+ }
+
+ @Override
+ public void stopMonitoringStateChange() {
+
+ }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSlice.java b/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSlice.java
deleted file mode 100644
index 6c7f930..0000000
--- a/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSlice.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.deviceinfo;
-
-import android.app.PendingIntent;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.PowerManager;
-
-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.SubSettings;
-import com.android.settings.Utils;
-import com.android.settings.fuelgauge.BatteryInfo;
-import com.android.settings.fuelgauge.PowerUsageSummary;
-import com.android.settings.slices.CustomSliceRegistry;
-import com.android.settings.slices.CustomSliceable;
-import com.android.settings.slices.SliceBuilderUtils;
-
-/**
- * Utility class to build a Battery Slice, and handle all associated actions.
- */
-public class BatteryInfoSlice implements CustomSliceable {
- private static final String TAG = "BatteryInfoSlice";
-
- private final Context mContext;
-
- private BatteryInfo mBatteryInfo;
- private boolean mIsBatteryInfoLoading;
-
- public BatteryInfoSlice(Context context) {
- mContext = context;
- }
-
- @Override
- public Slice getSlice() {
- if (mBatteryInfo == null) {
- mIsBatteryInfoLoading = true;
- loadBatteryInfo();
- }
- final IconCompat icon = IconCompat.createWithResource(mContext,
- R.drawable.ic_settings_battery);
- final CharSequence title = mContext.getText(R.string.power_usage_summary_title);
- final SliceAction primarySliceAction = SliceAction.createDeeplink(getPrimaryAction(), icon,
- ListBuilder.ICON_IMAGE, title);
- final Slice slice = new ListBuilder(mContext, CustomSliceRegistry.BATTERY_INFO_SLICE_URI,
- ListBuilder.INFINITY)
- .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
- .setHeader(new ListBuilder.HeaderBuilder().setTitle(title))
- .addRow(new ListBuilder.RowBuilder()
- .setTitle(getBatteryPercentString(), mIsBatteryInfoLoading)
- .setSubtitle(getSummary(), mIsBatteryInfoLoading)
- .setPrimaryAction(primarySliceAction))
- .build();
- mBatteryInfo = null;
- mIsBatteryInfoLoading = false;
- return slice;
- }
-
- @Override
- public Uri getUri() {
- return CustomSliceRegistry.BATTERY_INFO_SLICE_URI;
- }
-
- @Override
- public void onNotifyChange(Intent intent) {
-
- }
-
- @Override
- public Intent getIntent() {
- final String screenTitle = mContext.getText(R.string.power_usage_summary_title).toString();
- return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
- PowerUsageSummary.class.getName(), "" /* key */, screenTitle,
- SettingsEnums.SLICE)
- .setClassName(mContext.getPackageName(), SubSettings.class.getName())
- .setData(CustomSliceRegistry.BATTERY_INFO_SLICE_URI);
- }
-
- @Override
- public IntentFilter getIntentFilter() {
- final IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
- intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
- intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
- intentFilter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
- return intentFilter;
- }
-
- @VisibleForTesting
- void loadBatteryInfo() {
- BatteryInfo.getBatteryInfo(mContext, info -> {
- mBatteryInfo = info;
- mContext.getContentResolver().notifyChange(getUri(), null);
- }, true);
- }
-
- @VisibleForTesting
- CharSequence getBatteryPercentString() {
- return mBatteryInfo == null ? null : mBatteryInfo.batteryPercentString;
- }
-
- @VisibleForTesting
- CharSequence getSummary() {
- if (mBatteryInfo == null) {
- return null;
- }
- return mBatteryInfo.remainingLabel == null ? mBatteryInfo.statusLabel
- : mBatteryInfo.remainingLabel;
- }
-
- private PendingIntent getPrimaryAction() {
- final Intent intent = getIntent();
- return PendingIntent.getActivity(mContext, 0 /* requestCode */,
- intent, 0 /* flags */);
- }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
index 99fe219..a760eff 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
@@ -46,7 +46,6 @@
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBroadcastReceiver;
import com.android.settings.slices.SliceBuilderUtils;
-import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -211,8 +210,9 @@
@VisibleForTesting
IconCompat getBluetoothDeviceIcon(CachedBluetoothDevice device) {
- final Pair<Drawable, String> pair = BluetoothUtils
- .getBtClassDrawableWithDescription(mContext, device);
+ final Pair<Drawable, String> pair =
+ com.android.settings.bluetooth.Utils.getBtRainbowDrawableWithDescription(mContext,
+ device);
final Drawable drawable = pair.first;
// Use default bluetooth icon if can't get icon.
@@ -221,14 +221,6 @@
com.android.internal.R.drawable.ic_settings_bluetooth);
}
- // Tint icon: Accent color for connected state; Disable color for busy state.
- @ColorInt int color = Utils.getColorAccentDefaultColor(mContext);
- if (device.isBusy()) {
- color = Utils.getDisabled(mContext,
- Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal));
- }
- drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
-
return Utils.createIconWithDrawable(drawable);
}
diff --git a/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSlice.java b/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSlice.java
new file mode 100644
index 0000000..2025a06
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSlice.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.content.Context;
+import android.net.Uri;
+
+import com.android.settings.R;
+import com.android.settings.slices.CustomSliceRegistry;
+
+public class ContextualNotificationChannelSlice extends NotificationChannelSlice {
+
+ public ContextualNotificationChannelSlice(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Uri getUri() {
+ return CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI;
+ }
+
+ @Override
+ protected CharSequence getSubTitle(String packageName, int uid) {
+ return mContext.getText(R.string.recently_installed_app);
+ }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java
index ae8b80a..c319dca 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java
@@ -128,7 +128,7 @@
return leftChannel.getId().compareTo(rightChannel.getId());
};
- private final Context mContext;
+ protected final Context mContext;
@VisibleForTesting
NotificationBackend mNotificationBackend;
private String mPackageName;
@@ -413,7 +413,7 @@
return maxSentCountPackage;
}
- private CharSequence getSubTitle(String packageName, int uid) {
+ protected CharSequence getSubTitle(String packageName, int uid) {
final int channelCount = mNotificationBackend.getChannelCount(packageName, uid);
if (channelCount > DEFAULT_EXPANDED_ROW_COUNT) {
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java b/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java
index ee63536..ef0a67d 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java
@@ -16,7 +16,6 @@
package com.android.settings.homepage.contextualcards.slices;
-import android.animation.LayoutTransition;
import android.content.Context;
import android.view.View;
@@ -95,7 +94,6 @@
public SliceViewHolder(View view) {
super(view);
sliceView = view.findViewById(R.id.slice_view);
- sliceView.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
}
}
diff --git a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
index 44d600e..e4c64f7 100644
--- a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
@@ -74,6 +74,7 @@
mController = AppEntitiesHeaderController.newInstance(mContext, view)
.setHeaderTitleRes(R.string.location_category_recent_location_access)
.setHeaderDetailsRes(R.string.location_recent_location_access_view_details)
+ .setHeaderEmptyRes(R.string.location_no_recent_accesses)
.setHeaderDetailsClickListener((View v) -> {
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
@@ -100,14 +101,20 @@
.setIcon(access.icon)
.setTitle(access.label)
.setSummary(access.contentDescription)
+ .setOnClickListener((v) -> {
+ final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
+ intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, access.packageName);
+ intent.putExtra(Intent.EXTRA_USER, access.userHandle);
+ mContext.startActivity(intent);
+ })
.build();
mController.setAppEntity(i, appEntityInfo);
}
for (; i < MAXIMUM_APP_COUNT; i++) {
mController.removeAppEntity(i);
}
- } else {
- // If there's no item to display, add a "No recent apps" item.
}
mController.apply();
}
diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
index 7416018..d1e55e4 100644
--- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java
+++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
@@ -70,7 +70,7 @@
@Override
public void close() {
-
+ mLocalMediaManager = null;
}
@Override
diff --git a/src/com/android/settings/media/MediaOutputIndicatorSlice.java b/src/com/android/settings/media/MediaOutputIndicatorSlice.java
new file mode 100644
index 0000000..eb0c81f
--- /dev/null
+++ b/src/com/android/settings/media/MediaOutputIndicatorSlice.java
@@ -0,0 +1,104 @@
+/*
+ * 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.media;
+
+import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
+
+import android.annotation.ColorInt;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+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.slices.CustomSliceable;
+import com.android.settings.slices.SliceBackgroundWorker;
+import com.android.settingslib.media.MediaOutputSliceConstants;
+
+public class MediaOutputIndicatorSlice implements CustomSliceable {
+
+ private Context mContext;
+ @VisibleForTesting
+ MediaOutputIndicatorWorker mWorker;
+
+ public MediaOutputIndicatorSlice(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public Slice getSlice() {
+ if (!getWorker().isVisible()) {
+ return null;
+ }
+ final IconCompat icon = IconCompat.createWithResource(mContext,
+ com.android.internal.R.drawable.ic_settings_bluetooth);
+ final CharSequence title = mContext.getText(R.string.media_output_title);
+ final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext,
+ 0 /* requestCode */, getMediaOutputSliceIntent(), 0 /* flags */);
+ final SliceAction primarySliceAction = SliceAction.createDeeplink(
+ primaryActionIntent, icon, ListBuilder.ICON_IMAGE, title);
+ @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
+
+ final ListBuilder listBuilder = new ListBuilder(mContext,
+ MEDIA_OUTPUT_INDICATOR_SLICE_URI,
+ ListBuilder.INFINITY)
+ .setAccentColor(color)
+ .addRow(new ListBuilder.RowBuilder()
+ .setTitle(title)
+ .setSubtitle(getWorker().findActiveDeviceName())
+ .setPrimaryAction(primarySliceAction));
+ return listBuilder.build();
+ }
+
+ private MediaOutputIndicatorWorker getWorker() {
+ if (mWorker == null) {
+ mWorker = (MediaOutputIndicatorWorker) SliceBackgroundWorker.getInstance(getUri());
+ }
+ return mWorker;
+ }
+
+ private Intent getMediaOutputSliceIntent() {
+ final Intent intent = new Intent()
+ .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return intent;
+ }
+
+ @Override
+ public Uri getUri() {
+ return MEDIA_OUTPUT_INDICATOR_SLICE_URI;
+ }
+
+ @Override
+ public Intent getIntent() {
+ // This Slice reflects active media device information and launch MediaOutputSlice. It does
+ // not contain its owned Slice data
+ return null;
+ }
+
+ @Override
+ public Class getBackgroundWorkerClass() {
+ return MediaOutputIndicatorWorker.class;
+ }
+}
diff --git a/src/com/android/settings/media/MediaOutputIndicatorWorker.java b/src/com/android/settings/media/MediaOutputIndicatorWorker.java
new file mode 100644
index 0000000..adee055
--- /dev/null
+++ b/src/com/android/settings/media/MediaOutputIndicatorWorker.java
@@ -0,0 +1,161 @@
+/*
+ * 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.media;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.settings.R;
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.slices.SliceBackgroundWorker;
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Listener for background change from {@code BluetoothCallback} to update media output indicator.
+ */
+public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements BluetoothCallback {
+
+ private static final String TAG = "MediaOutputIndicatorWorker";
+
+ private LocalBluetoothManager mLocalBluetoothManager;
+ private LocalBluetoothProfileManager mProfileManager;
+
+ public MediaOutputIndicatorWorker(Context context, Uri uri) {
+ super(context, uri);
+ }
+
+ @Override
+ protected void onSlicePinned() {
+ LocalBluetoothManager mLocalBluetoothManager = Utils.getLocalBtManager(getContext());
+ if (mLocalBluetoothManager == null) {
+ Log.e(TAG, "Bluetooth is not supported on this device");
+ return;
+ }
+ mProfileManager = mLocalBluetoothManager.getProfileManager();
+ mLocalBluetoothManager.getEventManager().registerCallback(this);
+ }
+
+ @Override
+ protected void onSliceUnpinned() {
+ if (mLocalBluetoothManager == null) {
+ Log.e(TAG, "Bluetooth is not supported on this device");
+ return;
+ }
+ mLocalBluetoothManager.getEventManager().unregisterCallback(this);
+ }
+
+ @Override
+ public void close() throws IOException {
+ mLocalBluetoothManager = null;
+ mProfileManager = null;
+ }
+
+ @Override
+ public void onBluetoothStateChanged(int bluetoothState) {
+ // To handle the case that Bluetooth on and no connected devices
+ notifySliceChange();
+ }
+
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ if (bluetoothProfile == BluetoothProfile.A2DP) {
+ notifySliceChange();
+ }
+ }
+
+ /**
+ * To decide Slice's visibility.
+ *
+ * @return true if device is connected or previously connected, false for other cases.
+ */
+ public boolean isVisible() {
+ return !CollectionUtils.isEmpty(getConnectableA2dpDevices())
+ || !CollectionUtils.isEmpty(getConnectableHearingAidDevices());
+ }
+
+ private List<BluetoothDevice> getConnectableA2dpDevices() {
+ // get A2dp devices on all states
+ // (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile == null) {
+ return new ArrayList<>();
+ }
+ return a2dpProfile.getConnectableDevices();
+ }
+
+ private List<BluetoothDevice> getConnectableHearingAidDevices() {
+ // get hearing aid profile devices on all states
+ // (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ if (hapProfile == null) {
+ return new ArrayList<>();
+ }
+
+ return hapProfile.getConnectableDevices();
+ }
+
+ /**
+ * Get active devices name.
+ *
+ * @return active Bluetooth device alias, or default summary if no active device.
+ */
+ public CharSequence findActiveDeviceName() {
+ // Return Hearing Aid device name if it is active
+ BluetoothDevice activeDevice = findActiveHearingAidDevice();
+ if (activeDevice != null) {
+ return activeDevice.getAliasName();
+ }
+ // Return A2DP device name if it is active
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null) {
+ activeDevice = a2dpProfile.getActiveDevice();
+ if (activeDevice != null) {
+ return activeDevice.getAliasName();
+ }
+ }
+ // No active device, return default summary
+ return getContext().getText(R.string.media_output_default_summary);
+ }
+
+ private BluetoothDevice findActiveHearingAidDevice() {
+ final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+ if (hearingAidProfile == null) {
+ return null;
+ }
+
+ final List<BluetoothDevice> activeDevices = hearingAidProfile.getActiveDevices();
+ for (BluetoothDevice btDevice : activeDevices) {
+ if (btDevice != null) {
+ return btDevice;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java
index d52b441..e76bc88 100644
--- a/src/com/android/settings/media/MediaOutputSlice.java
+++ b/src/com/android/settings/media/MediaOutputSlice.java
@@ -20,13 +20,11 @@
import android.annotation.ColorInt;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.UserHandle;
-import android.util.IconDrawableFactory;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -58,37 +56,53 @@
private MediaDeviceUpdateWorker mWorker;
private String mPackageName;
- private IconDrawableFactory mIconDrawableFactory;
public MediaOutputSlice(Context context) {
mContext = context;
mPackageName = getUri().getQueryParameter(MEDIA_PACKAGE_NAME);
- mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
}
@VisibleForTesting
- void init(String packageName, MediaDeviceUpdateWorker worker, IconDrawableFactory factory) {
+ void init(String packageName, MediaDeviceUpdateWorker worker) {
mPackageName = packageName;
mWorker = worker;
- mIconDrawableFactory = factory;
}
@Override
public Slice getSlice() {
- final PackageManager pm = mContext.getPackageManager();
+ final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (!adapter.isEnabled()) {
+ Log.d(TAG, "getSlice() Bluetooth is off");
+ return null;
+ }
+
+ if (getWorker() == null) {
+ Log.d(TAG, "getSlice() Can not get worker through uri!");
+ return null;
+ }
final List<MediaDevice> devices = getMediaDevices();
- final CharSequence title = Utils.getApplicationLabel(mContext, mPackageName);
- final CharSequence summary =
- mContext.getString(R.string.media_output_panel_summary_of_playing_device,
- getConnectedDeviceName());
-
- final Drawable drawable =
- Utils.getBadgedIcon(mIconDrawableFactory, pm, mPackageName, UserHandle.myUserId());
- final IconCompat icon = Utils.createIconWithDrawable(drawable);
-
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
- final SliceAction primarySliceAction = SliceAction.createDeeplink(getPrimaryAction(), icon,
+
+ final MediaDevice connectedDevice = getWorker().getCurrentConnectedMediaDevice();
+ final ListBuilder listBuilder = buildActiveDeviceHeader(color, connectedDevice);
+
+ for (MediaDevice device : devices) {
+ if (!TextUtils.equals(connectedDevice.getId(), device.getId())) {
+ listBuilder.addRow(getMediaDeviceRow(device));
+ }
+ }
+
+ return listBuilder.build();
+ }
+
+ private ListBuilder buildActiveDeviceHeader(@ColorInt int color, MediaDevice device) {
+ final String title = device.getName();
+ final IconCompat icon = IconCompat.createWithResource(mContext, device.getIcon());
+
+ final PendingIntent broadcastAction =
+ getBroadcastIntent(mContext, device.getId(), device.hashCode());
+ final SliceAction primarySliceAction = SliceAction.createDeeplink(broadcastAction, icon,
ListBuilder.ICON_IMAGE, title);
final ListBuilder listBuilder = new ListBuilder(mContext, MEDIA_OUTPUT_SLICE_URI,
@@ -97,41 +111,27 @@
.addRow(new ListBuilder.RowBuilder()
.setTitleItem(icon, ListBuilder.ICON_IMAGE)
.setTitle(title)
- .setSubtitle(summary)
+ .setSubtitle(device.getSummary())
.setPrimaryAction(primarySliceAction));
- for (MediaDevice device : devices) {
- listBuilder.addRow(getMediaDeviceRow(device));
- }
-
- return listBuilder.build();
+ return listBuilder;
}
private MediaDeviceUpdateWorker getWorker() {
if (mWorker == null) {
mWorker = (MediaDeviceUpdateWorker) SliceBackgroundWorker.getInstance(getUri());
- mWorker.setPackageName(mPackageName);
+ if (mWorker != null) {
+ mWorker.setPackageName(mPackageName);
+ }
}
return mWorker;
}
private List<MediaDevice> getMediaDevices() {
- List<MediaDevice> devices = getWorker().getMediaDevices();
+ final List<MediaDevice> devices = getWorker().getMediaDevices();
return devices;
}
- private String getConnectedDeviceName() {
- final MediaDevice device = getWorker().getCurrentConnectedMediaDevice();
- return device != null ? device.getName() : "";
- }
-
- private PendingIntent getPrimaryAction() {
- final PackageManager pm = mContext.getPackageManager();
- final Intent launchIntent = pm.getLaunchIntentForPackage(mPackageName);
- final Intent intent = launchIntent;
- return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
- }
-
private ListBuilder.RowBuilder getMediaDeviceRow(MediaDevice device) {
final String title = device.getName();
final PendingIntent broadcastAction =
@@ -141,7 +141,8 @@
.setTitleItem(deviceIcon, ListBuilder.ICON_IMAGE)
.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon,
ListBuilder.ICON_IMAGE, title))
- .setTitle(title);
+ .setTitle(title)
+ .setSubtitle(device.getSummary());
return rowBuilder;
}
diff --git a/src/com/android/settings/network/MobileNetworkListController.java b/src/com/android/settings/network/MobileNetworkListController.java
index 7de6cdd..79715e3 100644
--- a/src/com/android/settings/network/MobileNetworkListController.java
+++ b/src/com/android/settings/network/MobileNetworkListController.java
@@ -24,14 +24,10 @@
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.euicc.EuiccManager;
import android.util.ArrayMap;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-import androidx.lifecycle.OnLifecycleEvent;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.network.telephony.MobileNetworkActivity;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -39,6 +35,12 @@
import java.util.List;
import java.util.Map;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
/**
* This populates the entries on a page which lists all available mobile subscriptions. Each entry
* has the name of the subscription with some subtext giving additional detail, and clicking on the
@@ -48,6 +50,9 @@
LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
private static final String TAG = "MobileNetworkListCtlr";
+ @VisibleForTesting
+ static final String KEY_ADD_MORE = "add_more";
+
private SubscriptionManager mSubscriptionManager;
private SubscriptionsChangeListener mChangeListener;
private PreferenceScreen mPreferenceScreen;
@@ -76,6 +81,8 @@
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceScreen = screen;
+ final EuiccManager euiccManager = mContext.getSystemService(EuiccManager.class);
+ mPreferenceScreen.findPreference(KEY_ADD_MORE).setVisible(euiccManager.isEnabled());
update();
}
@@ -93,7 +100,7 @@
final List<SubscriptionInfo> subscriptions = SubscriptionUtil.getAvailableSubscriptions(
mSubscriptionManager);
for (SubscriptionInfo info : subscriptions) {
- int subId = info.getSubscriptionId();
+ final int subId = info.getSubscriptionId();
Preference pref = existingPreferences.remove(subId);
if (pref == null) {
pref = new Preference(mPreferenceScreen.getContext());
diff --git a/src/com/android/settings/network/MobileNetworkSummaryController.java b/src/com/android/settings/network/MobileNetworkSummaryController.java
index a1fef4c..56735ab 100644
--- a/src/com/android/settings/network/MobileNetworkSummaryController.java
+++ b/src/com/android/settings/network/MobileNetworkSummaryController.java
@@ -16,8 +16,6 @@
package com.android.settings.network;
-import static android.telephony.TelephonyManager.MultiSimVariants.UNKNOWN;
-
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
@@ -25,7 +23,6 @@
import android.content.Intent;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import com.android.settings.R;
@@ -52,7 +49,6 @@
private SubscriptionManager mSubscriptionManager;
private SubscriptionsChangeListener mChangeListener;
- private TelephonyManager mTelephonyMgr;
private EuiccManager mEuiccManager;
private AddPreference mPreference;
@@ -74,7 +70,6 @@
public MobileNetworkSummaryController(Context context, Lifecycle lifecycle) {
super(context);
mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
- mTelephonyMgr = mContext.getSystemService(TelephonyManager.class);
mEuiccManager = mContext.getSystemService(EuiccManager.class);
if (lifecycle != null) {
mChangeListener = new SubscriptionsChangeListener(context, this);
@@ -124,48 +119,43 @@
mContext.startActivity(intent);
}
- private boolean shouldShowAddButton() {
- // The add button should only show up if the device is in multi-sim mode and the eSIM
- // manager is enabled.
- return mTelephonyMgr.getMultiSimConfiguration() != UNKNOWN && mEuiccManager.isEnabled();
- }
-
private void update() {
if (mPreference == null) {
return;
}
- final boolean showAddButton = shouldShowAddButton();
refreshSummary(mPreference);
- if (!showAddButton) {
- mPreference.setOnAddClickListener(null);
- } else {
- mPreference.setAddWidgetEnabled(!mChangeListener.isAirplaneModeOn());
- mPreference.setOnAddClickListener(p -> {
- startAddSimFlow();
- });
- }
- final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(
- mSubscriptionManager);
mPreference.setOnPreferenceClickListener(null);
+ mPreference.setOnAddClickListener(null);
mPreference.setFragment(null);
mPreference.setEnabled(!mChangeListener.isAirplaneModeOn());
+
+ final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(
+ mSubscriptionManager);
+
if (subs.isEmpty()) {
- if (showAddButton) {
- mPreference.setEnabled(false);
- } else if (mEuiccManager.isEnabled()) {
+ if (mEuiccManager.isEnabled()) {
mPreference.setOnPreferenceClickListener((Preference pref) -> {
startAddSimFlow();
return true;
});
}
- } else if (subs.size() == 1) {
- mPreference.setOnPreferenceClickListener((Preference pref) -> {
- final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
- mContext.startActivity(intent);
- return true;
- });
} else {
- mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName());
+ // We have one or more existing subscriptions, so we want the plus button if eSIM is
+ // supported.
+ if (mEuiccManager.isEnabled()) {
+ mPreference.setAddWidgetEnabled(!mChangeListener.isAirplaneModeOn());
+ mPreference.setOnAddClickListener(p -> startAddSimFlow());
+ }
+
+ if (subs.size() == 1) {
+ mPreference.setOnPreferenceClickListener((Preference pref) -> {
+ final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
+ mContext.startActivity(intent);
+ return true;
+ });
+ } else {
+ mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName());
+ }
}
}
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 7672c2e..8c686a5 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -168,7 +168,12 @@
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
- sir.xmlResId = R.xml.network_and_internet;
+ if (FeatureFlagPersistent.isEnabled(context,
+ FeatureFlags.NETWORK_INTERNET_V2)) {
+ sir.xmlResId = R.xml.network_and_internet_v2;
+ } else {
+ sir.xmlResId = R.xml.network_and_internet;
+ }
return Arrays.asList(sir);
}
diff --git a/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java
index 567e52e..1136736 100644
--- a/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java
+++ b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java
@@ -22,6 +22,7 @@
import android.text.TextUtils;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
@@ -45,7 +46,7 @@
@Override
public int getAvailabilityStatus() {
- return AVAILABLE_UNSEARCHABLE;
+ return Utils.isDemoUser(mContext) ? UNSUPPORTED_ON_DEVICE : AVAILABLE_UNSEARCHABLE;
}
@Override
diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
index 088bfbf..47db875 100644
--- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java
+++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
@@ -27,7 +27,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
-import android.telephony.NetworkRegistrationState;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -304,7 +304,7 @@
* Config the connected network operator preference when the page was created. When user get
* into this page, the device might or might not have data connection.
* - If the device has data:
- * 1. use {@code ServiceState#getNetworkRegistrationStates()} to get the currently
+ * 1. use {@code ServiceState#getNetworkRegistrationInfoList()} to get the currently
* registered cellIdentity, wrap it into a CellInfo;
* 2. set the signal strength level as strong;
* 3. use {@link TelephonyManager#getNetworkOperatorName()} to get the title of the
@@ -317,8 +317,9 @@
if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
// Try to get the network registration states
ServiceState ss = mTelephonyManager.getServiceState();
- List<NetworkRegistrationState> networkList =
- ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN);
+ List<NetworkRegistrationInfo> networkList =
+ ss.getNetworkRegistrationInfoListForTransportType(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
if (networkList == null || networkList.size() == 0) {
// Remove the connected network operators category
mConnectedPreferenceCategory.setVisible(false);
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index 9f5ece2..3ccca00 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -16,8 +16,6 @@
package com.android.settings.notification;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.settings.SettingsEnums;
@@ -26,8 +24,6 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
-import android.view.Window;
-import android.view.WindowManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -88,8 +84,6 @@
public void onResume() {
super.onResume();
- getActivity().getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
Log.w(TAG, "Missing package or uid or packageinfo");
finish();
@@ -124,15 +118,6 @@
}
@Override
- public void onPause() {
- super.onPause();
- final Window window = getActivity().getWindow();
- final WindowManager.LayoutParams attrs = window.getAttributes();
- attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
- window.setAttributes(attrs);
- }
-
- @Override
protected String getLogTag() {
return TAG;
}
@@ -152,6 +137,10 @@
context, mImportanceListener, mBackend));
mControllers.add(new ImportancePreferenceController(
context, mImportanceListener, mBackend));
+ mControllers.add(new MinImportancePreferenceController(
+ context, mImportanceListener, mBackend));
+ mControllers.add(new HighImportancePreferenceController(
+ context, mImportanceListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,
mImportanceListener, mBackend));
mControllers.add(new LightsPreferenceController(context, mBackend));
diff --git a/src/com/android/settings/notification/AssistantCapabilityPreferenceController.java b/src/com/android/settings/notification/AssistantCapabilityPreferenceController.java
new file mode 100644
index 0000000..be90a04
--- /dev/null
+++ b/src/com/android/settings/notification/AssistantCapabilityPreferenceController.java
@@ -0,0 +1,75 @@
+/*
+ * 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.notification;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.service.notification.Adjustment;
+
+import com.android.settings.core.TogglePreferenceController;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.util.List;
+
+public class AssistantCapabilityPreferenceController extends TogglePreferenceController {
+
+ static final String PRIORITIZER_KEY = "asst_capability_prioritizer";
+ static final String SMART_KEY = "asst_capabilities_actions_replies";
+ private NotificationBackend mBackend;
+
+ public AssistantCapabilityPreferenceController(Context context, String key) {
+ super(context, key);
+ mBackend = new NotificationBackend();
+ }
+
+ @VisibleForTesting
+ void setBackend(NotificationBackend backend) {
+ mBackend = backend;
+ }
+
+ @Override
+ public boolean isChecked() {
+ List<String> capabilities = mBackend.getAssistantCapabilities(mContext.getPackageName());
+ if (PRIORITIZER_KEY.equals(getPreferenceKey())) {
+ return capabilities.contains(Adjustment.KEY_IMPORTANCE);
+ } else if (SMART_KEY.equals(getPreferenceKey())) {
+ return capabilities.contains(Adjustment.KEY_CONTEXTUAL_ACTIONS)
+ && capabilities.contains(Adjustment.KEY_TEXT_REPLIES);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (PRIORITIZER_KEY.equals(getPreferenceKey())) {
+ mBackend.allowAssistantCapability(Adjustment.KEY_IMPORTANCE, isChecked);
+ } else if (SMART_KEY.equals(getPreferenceKey())) {
+ mBackend.allowAssistantCapability(Adjustment.KEY_CONTEXTUAL_ACTIONS, isChecked);
+ mBackend.allowAssistantCapability(Adjustment.KEY_TEXT_REPLIES, isChecked);
+ }
+ return true;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mBackend.getAllowedNotificationAssistant() != null
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+}
+
+
diff --git a/src/com/android/settings/notification/BubblePreferenceController.java b/src/com/android/settings/notification/BubblePreferenceController.java
index e61de4b..5b3be44 100644
--- a/src/com/android/settings/notification/BubblePreferenceController.java
+++ b/src/com/android/settings/notification/BubblePreferenceController.java
@@ -30,7 +30,7 @@
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String TAG = "BubblePrefContr";
- private static final String KEY = "bubble";
+ private static final String KEY = "bubble_pref";
private static final int SYSTEM_WIDE_ON = 1;
private static final int SYSTEM_WIDE_OFF = 0;
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index f92e529..850fde2 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -94,9 +94,12 @@
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
mControllers.add(new HeaderPreferenceController(context, this));
- mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
mControllers.add(new ImportancePreferenceController(
context, mImportanceListener, mBackend));
+ mControllers.add(new MinImportancePreferenceController(
+ context, mImportanceListener, mBackend));
+ mControllers.add(new HighImportancePreferenceController(
+ context, mImportanceListener, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,
diff --git a/src/com/android/settings/notification/HighImportancePreferenceController.java b/src/com/android/settings/notification/HighImportancePreferenceController.java
new file mode 100644
index 0000000..fe843fd
--- /dev/null
+++ b/src/com/android/settings/notification/HighImportancePreferenceController.java
@@ -0,0 +1,84 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import androidx.preference.Preference;
+
+public class HighImportancePreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_IMPORTANCE = "high_importance";
+ private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+ public HighImportancePreferenceController(Context context,
+ NotificationSettingsBase.ImportanceListener importanceListener,
+ NotificationBackend backend) {
+ super(context, backend);
+ mImportanceListener = importanceListener;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IMPORTANCE;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mChannel == null) {
+ return false;
+ }
+ if (isDefaultChannel()) {
+ return false;
+ }
+ return mChannel.getImportance() >= IMPORTANCE_DEFAULT;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mAppRow!= null && mChannel != null) {
+ preference.setEnabled(mAdmin == null && isChannelConfigurable());
+
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setChecked(mChannel.getImportance() >= IMPORTANCE_HIGH);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ final boolean checked = (boolean) newValue;
+
+ mChannel.setImportance(checked ? IMPORTANCE_HIGH : IMPORTANCE_DEFAULT);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ saveChannel();
+ mImportanceListener.onImportanceChanged();
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/settings/notification/ImportancePreference.java b/src/com/android/settings/notification/ImportancePreference.java
new file mode 100644
index 0000000..b8f3e45
--- /dev/null
+++ b/src/com/android/settings/notification/ImportancePreference.java
@@ -0,0 +1,172 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+
+import com.android.settingslib.R;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+public class ImportancePreference extends Preference {
+
+ boolean mIsBlockable = true;
+ boolean mIsConfigurable = true;
+ int mImportance;
+ ImageButton blockButton;
+ ImageButton silenceButton;
+ ImageButton alertButton;
+ ArrayMap<ImageButton, Integer> mImageButtons = new ArrayMap<>();
+ Context mContext;
+
+ public ImportancePreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context);
+ }
+
+ public ImportancePreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ public ImportancePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public ImportancePreference(Context context) {
+ super(context);
+ init(context);
+ }
+
+ private void init(Context context) {
+ mContext = context;
+ setLayoutResource(R.layout.notif_importance_preference);
+ }
+
+ public void setImportance(int importance) {
+ mImportance = importance;
+ }
+
+ public void setBlockable(boolean blockable) {
+ mIsBlockable = blockable;
+ }
+
+ public void setConfigurable(boolean configurable) {
+ mIsConfigurable = configurable;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ View blockView = holder.itemView.findViewById(R.id.block);
+ View alertView = holder.itemView.findViewById(R.id.alert);
+ View silenceView = holder.itemView.findViewById(R.id.silence);
+ if (!mIsBlockable) {
+ blockView.setVisibility(View.GONE);
+ if (mImportance == IMPORTANCE_NONE) {
+ mImportance = IMPORTANCE_LOW;
+ callChangeListener(IMPORTANCE_LOW);
+ }
+
+ }
+ blockButton = blockView.findViewById(R.id.block_icon);
+ silenceButton = silenceView.findViewById(R.id.silence_icon);
+ alertButton = alertView.findViewById(R.id.alert_icon);
+ mImageButtons.put(blockButton, mContext.getColor(R.color.notification_block_color));
+ mImageButtons.put(silenceButton, mContext.getColor(R.color.notification_silence_color));
+ mImageButtons.put(alertButton, mContext.getColor(R.color.notification_alert_color));
+
+ switch (mImportance) {
+ case IMPORTANCE_NONE:
+ colorizeImageButton(blockButton.getId());
+ if (!mIsConfigurable) {
+ alertView.setVisibility(View.GONE);
+ silenceView.setVisibility(View.GONE);
+ }
+ break;
+ case IMPORTANCE_MIN:
+ case IMPORTANCE_LOW:
+ colorizeImageButton(silenceButton.getId());
+ if (!mIsConfigurable) {
+ alertView.setVisibility(View.GONE);
+ blockView.setVisibility(View.GONE);
+ }
+ break;
+ case IMPORTANCE_HIGH:
+ default:
+ colorizeImageButton(alertButton.getId());
+ if (!mIsConfigurable) {
+ blockView.setVisibility(View.GONE);
+ silenceView.setVisibility(View.GONE);
+ }
+ break;
+ }
+
+ blockButton.setOnClickListener(v -> {
+ callChangeListener(IMPORTANCE_NONE);
+ colorizeImageButton(blockButton.getId());
+ });
+ silenceButton.setOnClickListener(v -> {
+ callChangeListener(IMPORTANCE_LOW);
+ colorizeImageButton(silenceButton.getId());
+ });
+ alertButton.setOnClickListener(v -> {
+ callChangeListener(IMPORTANCE_DEFAULT);
+ colorizeImageButton(alertButton.getId());
+ });
+ }
+
+ private void colorizeImageButton(int buttonId) {
+ if (mImageButtons != null) {
+ for (int i = 0; i < mImageButtons.size(); i++) {
+ final ImageButton imageButton = mImageButtons.keyAt(i);
+ final int color = mImageButtons.valueAt(i);
+ if (imageButton != null) {
+ LayerDrawable drawable = (LayerDrawable) imageButton.getDrawable();
+ Drawable foreground = drawable.findDrawableByLayerId(R.id.fore);
+ GradientDrawable background =
+ (GradientDrawable) drawable.findDrawableByLayerId(R.id.back);
+ if (buttonId == imageButton.getId()) {
+ foreground.setTint(Color.WHITE);
+ background.setColor(color);
+ } else {
+ foreground.setTint(color);
+ background.setColor(Color.TRANSPARENT);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/ImportancePreferenceController.java b/src/com/android/settings/notification/ImportancePreferenceController.java
index 4c20a46..0955571 100644
--- a/src/com/android/settings/notification/ImportancePreferenceController.java
+++ b/src/com/android/settings/notification/ImportancePreferenceController.java
@@ -18,21 +18,15 @@
import static android.app.NotificationChannel.USER_LOCKED_SOUND;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.content.Context;
import android.media.RingtoneManager;
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.RestrictedListPreference;
import com.android.settings.core.PreferenceControllerMixin;
+import androidx.preference.Preference;
+
public class ImportancePreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
@@ -53,44 +47,33 @@
@Override
public boolean isAvailable() {
- if (!super.isAvailable()) {
+ if (mAppRow == null) {
return false;
}
if (mChannel == null) {
return false;
}
- return !isDefaultChannel();
+ if (isDefaultChannel()) {
+ return false;
+ }
+ return true;
}
@Override
public void updateState(Preference preference) {
if (mAppRow!= null && mChannel != null) {
preference.setEnabled(mAdmin == null && isChannelConfigurable());
- preference.setSummary(getImportanceSummary(mChannel));
-
- int importances = IMPORTANCE_HIGH - IMPORTANCE_MIN + 1;
- CharSequence[] entries = new CharSequence[importances];
- CharSequence[] values = new CharSequence[importances];
-
- int index = 0;
- for (int i = IMPORTANCE_HIGH; i >= IMPORTANCE_MIN; i--) {
- NotificationChannel channel = new NotificationChannel("", "", i);
- entries[index] = getImportanceSummary(channel);
- values[index] = String.valueOf(i);
- index++;
- }
-
- RestrictedListPreference pref = (RestrictedListPreference) preference;
- pref.setEntries(entries);
- pref.setEntryValues(values);
- pref.setValue(String.valueOf(mChannel.getImportance()));
+ ImportancePreference pref = (ImportancePreference) preference;
+ pref.setBlockable(isChannelBlockable());
+ pref.setConfigurable(isChannelConfigurable());
+ pref.setImportance(mChannel.getImportance());
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mChannel != null) {
- final int importance = Integer.parseInt((String) newValue);
+ final int importance = (Integer) newValue;
// If you are moving from an importance level without sound to one with sound,
// but the sound you had selected was "Silence",
@@ -111,39 +94,4 @@
}
return true;
}
-
- protected String getImportanceSummary(NotificationChannel channel) {
- String summary = "";
- int importance = channel.getImportance();
- switch (importance) {
- case IMPORTANCE_UNSPECIFIED:
- summary = mContext.getString(R.string.notification_importance_unspecified);
- break;
- case NotificationManager.IMPORTANCE_MIN:
- summary = mContext.getString(R.string.notification_importance_min);
- break;
- case NotificationManager.IMPORTANCE_LOW:
- summary = mContext.getString(R.string.notification_importance_low);
- break;
- case NotificationManager.IMPORTANCE_DEFAULT:
- if (SoundPreferenceController.hasValidSound(channel)) {
- summary = mContext.getString(R.string.notification_importance_default);
- } else {
- summary = mContext.getString(R.string.notification_importance_low);
- }
- break;
- case NotificationManager.IMPORTANCE_HIGH:
- case NotificationManager.IMPORTANCE_MAX:
- if (SoundPreferenceController.hasValidSound(channel)) {
- summary = mContext.getString(R.string.notification_importance_high);
- } else {
- summary = mContext.getString(R.string.notification_importance_high_silent);
- }
- break;
- default:
- return "";
- }
-
- return summary;
- }
}
diff --git a/src/com/android/settings/notification/MinImportancePreferenceController.java b/src/com/android/settings/notification/MinImportancePreferenceController.java
new file mode 100644
index 0000000..771ac60
--- /dev/null
+++ b/src/com/android/settings/notification/MinImportancePreferenceController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import androidx.preference.Preference;
+
+public class MinImportancePreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_IMPORTANCE = "min_importance";
+ private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+ public MinImportancePreferenceController(Context context,
+ NotificationSettingsBase.ImportanceListener importanceListener,
+ NotificationBackend backend) {
+ super(context, backend);
+ mImportanceListener = importanceListener;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IMPORTANCE;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mChannel == null) {
+ return false;
+ }
+ if (isDefaultChannel()) {
+ return false;
+ }
+ return mChannel.getImportance() <= IMPORTANCE_LOW;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mAppRow!= null && mChannel != null) {
+ preference.setEnabled(mAdmin == null && isChannelConfigurable());
+
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setChecked(mChannel.getImportance() == IMPORTANCE_MIN);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ final boolean checked = (boolean) newValue;
+
+ mChannel.setImportance(checked ? IMPORTANCE_MIN : IMPORTANCE_LOW);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ saveChannel();
+ mImportanceListener.onImportanceChanged();
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index ba07438..4227050 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -339,6 +339,27 @@
}
}
+ public void allowAssistantCapability(String capability, boolean allowed) {
+ try {
+ if (allowed) {
+ sINM.allowAssistantCapability(capability);
+ } else {
+ sINM.disallowAssistantCapability(capability);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ }
+
+ public List<String> getAssistantCapabilities(String pkg) {
+ try {
+ return sINM.getAllowedAssistantCapabilities(pkg);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ return new ArrayList<>();
+ }
+
protected void recordAggregatedUsageEvents(Context context, AppRow appRow) {
long now = System.currentTimeMillis();
long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
diff --git a/src/com/android/settings/notification/NotificationPreferenceController.java b/src/com/android/settings/notification/NotificationPreferenceController.java
index 22f07be..3f535fb 100644
--- a/src/com/android/settings/notification/NotificationPreferenceController.java
+++ b/src/com/android/settings/notification/NotificationPreferenceController.java
@@ -111,6 +111,9 @@
}
protected boolean isChannelConfigurable() {
+ if (mAppRow != null && mAppRow.lockedImportance) {
+ return false;
+ }
if (mChannel != null && mAppRow != null) {
return !Objects.equals(mChannel.getId(), mAppRow.lockedChannelId);
}
diff --git a/src/com/android/settings/notification/RemoteVolumePreferenceController.java b/src/com/android/settings/notification/RemoteVolumePreferenceController.java
index 0ad307e..37a493e 100644
--- a/src/com/android/settings/notification/RemoteVolumePreferenceController.java
+++ b/src/com/android/settings/notification/RemoteVolumePreferenceController.java
@@ -35,6 +35,7 @@
import java.io.IOException;
import java.util.List;
+import java.util.Objects;
public class RemoteVolumePreferenceController extends
VolumeSeekBarPreferenceController {
@@ -58,14 +59,14 @@
if (mActiveToken == null) {
updateToken(token);
}
- if (mActiveToken == token) {
+ if (Objects.equals(mActiveToken, token)) {
updatePreference(mPreference, mActiveToken, pi);
}
}
@Override
public void onRemoteRemoved(MediaSession.Token t) {
- if (mActiveToken == t) {
+ if (Objects.equals(mActiveToken, t)) {
updateToken(null);
if (mPreference != null) {
mPreference.setVisible(false);
@@ -75,7 +76,7 @@
@Override
public void onRemoteVolumeChanged(MediaSession.Token token, int flags) {
- if (mActiveToken == token) {
+ if (Objects.equals(mActiveToken, token)) {
final MediaController.PlaybackInfo pi = mMediaController.getPlaybackInfo();
if (pi != null) {
setSliderPosition(pi.getCurrentVolume());
@@ -116,13 +117,13 @@
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
super.onResume();
- //TODO(b/126199571): register callback once b/126890783 is fixed
+ mMediaSessions.init();
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
super.onPause();
- //TODO(b/126199571): unregister callback once b/126890783 is fixed
+ mMediaSessions.destroy();
}
@Override
@@ -189,8 +190,7 @@
@Override
public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() {
- //TODO(b/126199571): return RemoteVolumeSliceWorker once b/126890783 is fixed
- return null;
+ return RemoteVolumeSliceWorker.class;
}
private void updatePreference(VolumeSeekBarPreference seekBarPreference,
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index b26b921..eec0fb8 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -38,7 +38,6 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.sound.HandsFreeProfileOutputPreferenceController;
-import com.android.settings.sound.MediaOutputPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settings.widget.UpdatableListPreferenceDialogFragment;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -77,7 +76,6 @@
private RingtonePreference mRequestPreference;
private UpdatableListPreferenceDialogFragment mDialogFragment;
- private String mMediaOutputControllerKey;
private String mHfpOutputControllerKey;
@Override
@@ -132,8 +130,6 @@
final int metricsCategory;
if (mHfpOutputControllerKey.equals(preference.getKey())) {
metricsCategory = SettingsEnums.DIALOG_SWITCH_HFP_DEVICES;
- } else if (mMediaOutputControllerKey.equals(preference.getKey())) {
- metricsCategory = SettingsEnums.DIALOG_SWITCH_A2DP_DEVICES;
} else {
metricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN;
}
@@ -186,9 +182,6 @@
volumeControllers.add(use(CallVolumePreferenceController.class));
volumeControllers.add(use(RemoteVolumePreferenceController.class));
- use(MediaOutputPreferenceController.class).setCallback(listPreference ->
- onPreferenceDataChanged(listPreference));
- mMediaOutputControllerKey = use(MediaOutputPreferenceController.class).getPreferenceKey();
use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference ->
onPreferenceDataChanged(listPreference));
mHfpOutputControllerKey =
diff --git a/src/com/android/settings/notification/ZenAccessSettings.java b/src/com/android/settings/notification/ZenAccessSettings.java
index d057c75..fca8255 100644
--- a/src/com/android/settings/notification/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/ZenAccessSettings.java
@@ -18,56 +18,40 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.Dialog;
import android.app.NotificationManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
import android.provider.SearchIndexableResource;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.Log;
import android.view.View;
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog;
-import androidx.preference.Preference;
-import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
import com.android.settings.R;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.specialaccess.zenaccess.ZenAccessController;
+import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails;
+import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
-import com.android.settings.widget.AppSwitchPreference;
import com.android.settings.widget.EmptyTextSettings;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.apppreference.AppPreference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
@SearchIndexable
-public class ZenAccessSettings extends EmptyTextSettings {
+public class ZenAccessSettings extends EmptyTextSettings implements
+ ZenAccessSettingObserverMixin.Listener {
private final String TAG = "ZenAccessSettings";
- private final SettingObserver mObserver = new SettingObserver();
private Context mContext;
private PackageManager mPkgMan;
private NotificationManager mNoMan;
@@ -84,6 +68,8 @@
mContext = getActivity();
mPkgMan = mContext.getPackageManager();
mNoMan = mContext.getSystemService(NotificationManager.class);
+ getSettingsLifecycle().addObserver(
+ new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
}
@Override
@@ -102,30 +88,22 @@
super.onResume();
if (!ActivityManager.isLowRamDeviceStatic()) {
reloadList();
- getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), false,
- mObserver);
- getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.ENABLED_NOTIFICATION_LISTENERS), false,
- mObserver);
} else {
setEmptyText(R.string.disabled_low_ram_device);
}
}
@Override
- public void onPause() {
- super.onPause();
- if (!ActivityManager.isLowRamDeviceStatic()) {
- getContentResolver().unregisterContentObserver(mObserver);
- }
+ public void onZenAccessPolicyChanged() {
+ reloadList();
}
private void reloadList() {
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
final ArrayList<ApplicationInfo> apps = new ArrayList<>();
- final ArraySet<String> requesting = getPackagesRequestingNotificationPolicyAccess();
+ final Set<String> requesting =
+ ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
if (!requesting.isEmpty()) {
final List<ApplicationInfo> installed = mPkgMan.getInstalledApplications(0);
if (installed != null) {
@@ -143,204 +121,42 @@
for (ApplicationInfo app : apps) {
final String pkg = app.packageName;
final CharSequence label = app.loadLabel(mPkgMan);
- final SwitchPreference pref = new AppSwitchPreference(getPrefContext());
+ final AppPreference pref = new AppPreference(getPrefContext());
pref.setKey(pkg);
- pref.setPersistent(false);
pref.setIcon(app.loadIcon(mPkgMan));
pref.setTitle(label);
- pref.setChecked(hasAccess(pkg));
if (autoApproved.contains(pkg)) {
+ //Auto approved, user cannot do anything. Hard code summary and disable preference.
pref.setEnabled(false);
pref.setSummary(getString(R.string.zen_access_disabled_package_warning));
+ } else {
+ // Not auto approved, update summary according to notification backend.
+ pref.setSummary(getPreferenceSummary(pkg));
}
- pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean access = (Boolean) newValue;
- if (access) {
- new ScaryWarningDialogFragment()
- .setPkgInfo(pkg, label)
- .show(getFragmentManager(), "dialog");
- } else {
- new FriendlyWarningDialogFragment()
- .setPkgInfo(pkg, label)
- .show(getFragmentManager(), "dialog");
- }
- return false;
- }
+ pref.setOnPreferenceClickListener(preference -> {
+ AppInfoBase.startAppInfoFragment(
+ ZenAccessDetails.class /* fragment */,
+ R.string.manage_zen_access_title /* titleRes */,
+ pkg,
+ app.uid,
+ this /* source */,
+ -1 /* requestCode */,
+ getMetricsCategory() /* sourceMetricsCategory */);
+ return true;
});
+
screen.addPreference(pref);
}
}
- private ArraySet<String> getPackagesRequestingNotificationPolicyAccess() {
- ArraySet<String> requestingPackages = new ArraySet<>();
- try {
- final String[] PERM = {
- android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
- };
- final ParceledListSlice list = AppGlobals.getPackageManager()
- .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
- ActivityManager.getCurrentUser());
- final List<PackageInfo> pkgs = list.getList();
- if (pkgs != null) {
- for (PackageInfo info : pkgs) {
- requestingPackages.add(info.packageName);
- }
- }
- } catch(RemoteException e) {
- Log.e(TAG, "Cannot reach packagemanager", e);
- }
- return requestingPackages;
- }
-
- private boolean hasAccess(String pkg) {
- return mNoMan.isNotificationPolicyAccessGrantedForPackage(pkg);
- }
-
- private static void setAccess(final Context context, final String pkg, final boolean access) {
- logSpecialPermissionChange(access, pkg, context);
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- final NotificationManager mgr = context.getSystemService(NotificationManager.class);
- mgr.setNotificationPolicyAccessGranted(pkg, access);
- }
- });
- }
-
- @VisibleForTesting
- static void logSpecialPermissionChange(boolean enable, String packageName, Context context) {
- int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_DND_ALLOW
- : SettingsEnums.APP_SPECIAL_PERMISSION_DND_DENY;
- FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
- logCategory, packageName);
- }
-
-
- private static void deleteRules(final Context context, final String pkg) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- final NotificationManager mgr = context.getSystemService(NotificationManager.class);
- mgr.removeAutomaticZenRules(pkg);
- }
- });
- }
-
- private final class SettingObserver extends ContentObserver {
- public SettingObserver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- reloadList();
- }
- }
-
/**
- * Warning dialog when allowing zen access warning about the privileges being granted.
+ * @return the summary for the current state of whether the app associated with the given
+ * {@param packageName} is allowed to enter picture-in-picture.
*/
- public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
- static final String KEY_PKG = "p";
- static final String KEY_LABEL = "l";
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DIALOG_ZEN_ACCESS_GRANT;
- }
-
- public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
- Bundle args = new Bundle();
- args.putString(KEY_PKG, pkg);
- args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
- setArguments(args);
- return this;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Bundle args = getArguments();
- final String pkg = args.getString(KEY_PKG);
- final String label = args.getString(KEY_LABEL);
-
- final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
- label);
- final String summary = getResources()
- .getString(R.string.zen_access_warning_dialog_summary);
- return new AlertDialog.Builder(getContext())
- .setMessage(summary)
- .setTitle(title)
- .setCancelable(true)
- .setPositiveButton(R.string.allow,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- setAccess(getContext(), pkg, true);
- }
- })
- .setNegativeButton(R.string.deny,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- // pass
- }
- })
- .create();
- }
- }
-
- /**
- * Warning dialog when revoking zen access warning that zen rule instances will be deleted.
- */
- public static class FriendlyWarningDialogFragment extends InstrumentedDialogFragment {
- static final String KEY_PKG = "p";
- static final String KEY_LABEL = "l";
-
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DIALOG_ZEN_ACCESS_REVOKE;
- }
-
- public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
- Bundle args = new Bundle();
- args.putString(KEY_PKG, pkg);
- args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
- setArguments(args);
- return this;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Bundle args = getArguments();
- final String pkg = args.getString(KEY_PKG);
- final String label = args.getString(KEY_LABEL);
-
- final String title = getResources().getString(
- R.string.zen_access_revoke_warning_dialog_title, label);
- final String summary = getResources()
- .getString(R.string.zen_access_revoke_warning_dialog_summary);
- return new AlertDialog.Builder(getContext())
- .setMessage(summary)
- .setTitle(title)
- .setCancelable(true)
- .setPositiveButton(R.string.okay,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- deleteRules(getContext(), pkg);
- setAccess(getContext(), pkg, false);
- }
- })
- .setNegativeButton(R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- // pass
- }
- })
- .create();
- }
+ private int getPreferenceSummary(String packageName) {
+ final boolean enabled = ZenAccessController.hasAccess(getContext(), packageName);
+ return enabled ? R.string.app_permission_summary_allowed
+ : R.string.app_permission_summary_not_allowed;
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
diff --git a/src/com/android/settings/notification/ZenRulePreference.java b/src/com/android/settings/notification/ZenRulePreference.java
index 613eb1d..8bc602a 100644
--- a/src/com/android/settings/notification/ZenRulePreference.java
+++ b/src/com/android/settings/notification/ZenRulePreference.java
@@ -171,6 +171,9 @@
getSettingsActivity(rule, si);
mIntent = AbstractZenModeAutomaticRulePreferenceController.getRuleIntent(action,
settingsActivity, mId);
+ if (mIntent.resolveActivity(mPm) == null) {
+ mIntent = null;
+ }
setKey(mId);
}
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 23136a4..d9af345 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -20,6 +20,8 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.settings.R;
import com.android.settings.accounts.AccountFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProvider;
@@ -50,6 +52,7 @@
private static final boolean DEBUG = false;
protected static FeatureFactory sFactory;
+ protected static Context sAppContext;
/**
* Returns a factory for creating feature controllers. Creates the factory if it does not
@@ -60,6 +63,9 @@
if (sFactory != null) {
return sFactory;
}
+ if (sAppContext == null) {
+ sAppContext = context.getApplicationContext();
+ }
if (DEBUG) Log.d(LOG_TAG, "getFactory");
final String clsName = context.getString(R.string.config_featureFactory);
@@ -76,6 +82,16 @@
return sFactory;
}
+ /**
+ * Returns an application {@link Context} used to create this {@link FeatureFactory}. If the
+ * factory has not been properly created yet (aka {@link #getFactory} has not been called), this
+ * will return null.
+ */
+ @Nullable
+ public static Context getAppContext() {
+ return sAppContext;
+ }
+
public abstract AssistGestureFeatureProvider getAssistGestureFeatureProvider();
public abstract SuggestionFeatureProvider getSuggestionFeatureProvider(Context context);
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index a222d36..20421d0 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -48,16 +48,11 @@
private RecyclerView mPanelSlices;
private PanelContent mPanel;
- private final MetricsFeatureProvider mMetricsProvider;
+ private MetricsFeatureProvider mMetricsProvider;
@VisibleForTesting
PanelSlicesAdapter mAdapter;
- public PanelFragment() {
- final Context context = getActivity();
- mMetricsProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
- }
-
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@@ -83,6 +78,7 @@
.getPanelFeatureProvider()
.getPanel(activity, panelType, mediaPackageName);
+ mMetricsProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider();
// Log panel opened.
mMetricsProvider.action(
0 /* attribution */,
diff --git a/src/com/android/settings/panel/PanelSlicesAdapter.java b/src/com/android/settings/panel/PanelSlicesAdapter.java
index c1cb049..47ff631 100644
--- a/src/com/android/settings/panel/PanelSlicesAdapter.java
+++ b/src/com/android/settings/panel/PanelSlicesAdapter.java
@@ -94,6 +94,7 @@
super(view);
sliceView = view.findViewById(R.id.slice_view);
sliceView.setMode(SliceView.MODE_LARGE);
+ sliceView.showTitleItems(true);
mPanelContent = panelContent;
}
diff --git a/src/com/android/settings/panel/SettingsPanelActivity.java b/src/com/android/settings/panel/SettingsPanelActivity.java
index 3819c80..8aee382 100644
--- a/src/com/android/settings/panel/SettingsPanelActivity.java
+++ b/src/com/android/settings/panel/SettingsPanelActivity.java
@@ -16,13 +16,11 @@
package com.android.settings.panel;
-import static com.android.settingslib.media.MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT;
import static com.android.settingslib.media.MediaOutputSliceConstants.EXTRA_PACKAGE_NAME;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.os.Bundle;
-import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -75,15 +73,8 @@
return;
}
- final String mediaPackageName =
- callingIntent.getStringExtra(EXTRA_PACKAGE_NAME);
-
- if (TextUtils.equals(ACTION_MEDIA_OUTPUT, callingIntent.getAction())
- && TextUtils.isEmpty(mediaPackageName)) {
- Log.e(TAG, "Missing EXTRA_PACKAGE_NAME, closing Panel Activity");
- finish();
- return;
- }
+ // We will use it once media output switch panel support remote device.
+ final String mediaPackageName = callingIntent.getStringExtra(EXTRA_PACKAGE_NAME);
setContentView(R.layout.settings_panel);
diff --git a/src/com/android/settings/panel/VolumePanel.java b/src/com/android/settings/panel/VolumePanel.java
index 62eca53..4ea7fe7 100644
--- a/src/com/android/settings/panel/VolumePanel.java
+++ b/src/com/android/settings/panel/VolumePanel.java
@@ -16,6 +16,7 @@
package com.android.settings.panel;
+import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_ALARM_URI;
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_CALL_URI;
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI;
@@ -55,6 +56,7 @@
final List<Uri> uris = new ArrayList<>();
uris.add(VOLUME_REMOTE_MEDIA_URI);
uris.add(VOLUME_MEDIA_URI);
+ uris.add(MEDIA_OUTPUT_INDICATOR_SLICE_URI);
uris.add(VOLUME_CALL_URI);
uris.add(VOLUME_RINGER_URI);
uris.add(VOLUME_ALARM_URI);
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index c9d1af3..16cecc8 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -45,10 +45,10 @@
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
+import android.util.Pair;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.LinearLayout;
@@ -1100,12 +1100,11 @@
}
@Override
- protected Intent saveAndVerifyInBackground() {
+ protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
+ final boolean success = mUtils.saveLockPassword(
+ mChosenPassword, mCurrentPassword, mRequestedQuality, mUserId);
Intent result = null;
- mUtils.saveLockPassword(mChosenPassword, mCurrentPassword, mRequestedQuality,
- mUserId);
-
- if (mHasChallenge) {
+ if (success && mHasChallenge) {
byte[] token;
try {
token = mUtils.verifyPassword(mChosenPassword, mChallenge, mUserId);
@@ -1120,8 +1119,7 @@
result = new Intent();
result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
}
-
- return result;
+ return Pair.create(success, result);
}
}
}
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index d5ad1ab..d89ad30 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -24,6 +24,7 @@
import android.content.res.Resources.Theme;
import android.os.Bundle;
import android.util.Log;
+import android.util.Pair;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -854,12 +855,11 @@
}
@Override
- protected Intent saveAndVerifyInBackground() {
- Intent result = null;
+ protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
final int userId = mUserId;
- mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
-
- if (mHasChallenge) {
+ final boolean success = mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
+ Intent result = null;
+ if (success && mHasChallenge) {
byte[] token;
try {
token = mUtils.verifyPattern(mChosenPattern, mChallenge, userId);
@@ -874,8 +874,7 @@
result = new Intent();
result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
}
-
- return result;
+ return Pair.create(success, result);
}
@Override
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index ec1e835..d3d5c49 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -198,7 +198,7 @@
} else if (isManagedProfile && isInternalActivity()
&& !lockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
mCredentialMode = CREDENTIAL_MANAGED;
- if (isBiometricAllowed(effectiveUserId)) {
+ if (isBiometricAllowed(effectiveUserId, mUserId)) {
showBiometricPrompt(bpBundle);
launchedBiometric = true;
} else {
@@ -207,7 +207,7 @@
}
} else {
mCredentialMode = CREDENTIAL_NORMAL;
- if (isBiometricAllowed(effectiveUserId)) {
+ if (isBiometricAllowed(effectiveUserId, mUserId)) {
// Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
// onAuthenticationError and do the right thing automatically.
showBiometricPrompt(bpBundle);
@@ -273,9 +273,10 @@
return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS) != 0;
}
- private boolean isBiometricAllowed(int effectiveUserId) {
+ private boolean isBiometricAllowed(int effectiveUserId, int realUserId) {
return !isStrongAuthRequired(effectiveUserId)
- && !isBiometricDisabledByAdmin(effectiveUserId);
+ && !isBiometricDisabledByAdmin(effectiveUserId)
+ && !mLockPatternUtils.hasPendingEscrowToken(realUserId);
}
private void showBiometricPrompt(Bundle bundle) {
@@ -304,9 +305,18 @@
private void showConfirmCredentials() {
mCCLaunched = true;
boolean launched = false;
+ // The only difference between CREDENTIAL_MANAGED and CREDENTIAL_NORMAL is that for
+ // CREDENTIAL_MANAGED, we launch the real confirm credential activity with an explicit
+ // but dummy challenge value (0L). This will result in ConfirmLockPassword calling
+ // verifyTiedProfileChallenge() (if it's a profile with unified challenge), due to the
+ // difference between ConfirmLockPassword.startVerifyPassword() and
+ // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here is
+ // necessary when this is part of the turning on work profile flow, because it forces
+ // unlocking the work profile even before the profile is running.
+ // TODO: Remove the duplication of checkPassword and verifyPassword in ConfirmLockPassword,
+ // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to use,
+ // which optionally accepts a challenge.
if (mCredentialMode == CREDENTIAL_MANAGED) {
- // We set the challenge as 0L, so it will force to unlock managed profile when it
- // unlocks primary profile screen lock, by calling verifyTiedProfileChallenge()
launched = mChooseLockSettingsHelper
.launchConfirmationActivityWithExternalAndChallenge(
0 /* request code */, null /* title */, mTitle, mDetails,
diff --git a/src/com/android/settings/password/SaveChosenLockWorkerBase.java b/src/com/android/settings/password/SaveChosenLockWorkerBase.java
index 5b6a812..2798b3d 100644
--- a/src/com/android/settings/password/SaveChosenLockWorkerBase.java
+++ b/src/com/android/settings/password/SaveChosenLockWorkerBase.java
@@ -21,10 +21,13 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.UserManager;
+import android.util.Pair;
+import android.widget.Toast;
import androidx.fragment.app.Fragment;
import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
/**
* An invisible retained worker fragment to track the AsyncWork that saves (and optionally
@@ -84,7 +87,7 @@
protected void start() {
if (mBlocking) {
- finish(saveAndVerifyInBackground());
+ finish(saveAndVerifyInBackground().second);
} else {
new Task().execute();
}
@@ -92,9 +95,10 @@
/**
* Executes the save and verify work in background.
- * @return Intent with challenge token or null.
+ * @return pair where the first is a boolean confirming whether the change was successful or not
+ * and second is the Intent which has the challenge token or is null.
*/
- protected abstract Intent saveAndVerifyInBackground();
+ protected abstract Pair<Boolean, Intent> saveAndVerifyInBackground();
protected void finish(Intent resultData) {
mFinished = true;
@@ -108,19 +112,24 @@
mBlocking = blocking;
}
- private class Task extends AsyncTask<Void, Void, Intent> {
+ private class Task extends AsyncTask<Void, Void, Pair<Boolean, Intent>> {
+
@Override
- protected Intent doInBackground(Void... params){
+ protected Pair<Boolean, Intent> doInBackground(Void... params){
return saveAndVerifyInBackground();
}
@Override
- protected void onPostExecute(Intent resultData) {
- finish(resultData);
+ protected void onPostExecute(Pair<Boolean, Intent> resultData) {
+ if (!resultData.first) {
+ Toast.makeText(getContext(), R.string.lockpassword_credential_changed,
+ Toast.LENGTH_LONG).show();
+ }
+ finish(resultData.second);
}
}
interface Listener {
- public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
+ void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
}
}
diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java
index d7445e6..4722c56 100644
--- a/src/com/android/settings/password/SetNewPasswordActivity.java
+++ b/src/com/android/settings/password/SetNewPasswordActivity.java
@@ -16,7 +16,7 @@
package com.android.settings.password;
-import static android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
@@ -83,7 +83,7 @@
if (ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
&& getIntent().hasExtra(EXTRA_PASSWORD_COMPLEXITY)) {
final boolean hasPermission = PasswordUtils.isCallingAppPermitted(
- this, activityToken, REQUEST_SCREEN_LOCK_COMPLEXITY);
+ this, activityToken, REQUEST_PASSWORD_COMPLEXITY);
if (hasPermission) {
mRequestedMinComplexity =
PasswordMetrics.sanitizeComplexityLevel(getIntent()
@@ -91,7 +91,7 @@
} else {
PasswordUtils.crashCallingApplication(activityToken,
"Must have permission "
- + REQUEST_SCREEN_LOCK_COMPLEXITY + " to use extra "
+ + REQUEST_PASSWORD_COMPLEXITY + " to use extra "
+ EXTRA_PASSWORD_COMPLEXITY);
finish();
return;
diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java
index 7fbb211..346e771 100644
--- a/src/com/android/settings/password/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/password/SetupChooseLockGeneric.java
@@ -16,7 +16,7 @@
package com.android.settings.password;
-import static android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
@@ -82,10 +82,10 @@
if(getIntent().hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)) {
IBinder activityToken = getActivityToken();
boolean hasPermission = PasswordUtils.isCallingAppPermitted(
- this, activityToken, REQUEST_SCREEN_LOCK_COMPLEXITY);
+ this, activityToken, REQUEST_PASSWORD_COMPLEXITY);
if (!hasPermission) {
PasswordUtils.crashCallingApplication(activityToken,
- "Must have permission " + REQUEST_SCREEN_LOCK_COMPLEXITY
+ "Must have permission " + REQUEST_PASSWORD_COMPLEXITY
+ " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
finish();
return;
diff --git a/src/com/android/settings/privacy/EnableContentCapturePreferenceController.java b/src/com/android/settings/privacy/EnableContentCapturePreferenceController.java
index b3ea9a7..47610aa 100644
--- a/src/com/android/settings/privacy/EnableContentCapturePreferenceController.java
+++ b/src/com/android/settings/privacy/EnableContentCapturePreferenceController.java
@@ -16,43 +16,34 @@
package com.android.settings.privacy;
+import android.annotation.NonNull;
import android.content.Context;
-import android.os.IBinder;
-import android.os.ServiceManager;
-import android.os.UserHandle;
import android.provider.Settings;
-import android.view.contentcapture.ContentCaptureManager;
import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.utils.ContentCaptureUtils;
-public class EnableContentCapturePreferenceController extends TogglePreferenceController {
+public final class EnableContentCapturePreferenceController extends TogglePreferenceController {
- private static final String KEY_SHOW_PASSWORD = "content_capture";
- private static final int MY_USER_ID = UserHandle.myUserId();
-
- public EnableContentCapturePreferenceController(Context context) {
- super(context, KEY_SHOW_PASSWORD);
+ public EnableContentCapturePreferenceController(@NonNull Context context, @NonNull String key) {
+ super(context, key);
}
@Override
public boolean isChecked() {
- boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.CONTENT_CAPTURE_ENABLED, 1, MY_USER_ID) == 1;
- return enabled;
+ return ContentCaptureUtils.isEnabledForUser(mContext);
}
@Override
public boolean setChecked(boolean isChecked) {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.CONTENT_CAPTURE_ENABLED, isChecked ? 1 : 0, MY_USER_ID);
+ ContentCaptureUtils.setEnabledForUser(mContext, isChecked);
return true;
}
@Override
public int getAvailabilityStatus() {
- // We cannot look for ContentCaptureManager, because it's not available if the service
- // didn't whitelist Settings
- IBinder service = ServiceManager.checkService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
- return service != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ boolean available = ContentCaptureUtils.isFeatureAvailable()
+ && ContentCaptureUtils.getServiceSettingsComponentName() == null;
+ return available ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
}
diff --git a/src/com/android/settings/privacy/EnableContentCaptureWithServiceSettingsPreferenceController.java b/src/com/android/settings/privacy/EnableContentCaptureWithServiceSettingsPreferenceController.java
new file mode 100644
index 0000000..1456c2d
--- /dev/null
+++ b/src/com/android/settings/privacy/EnableContentCaptureWithServiceSettingsPreferenceController.java
@@ -0,0 +1,72 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.utils.ContentCaptureUtils;
+import com.android.settings.widget.MasterSwitchPreference;
+
+public final class EnableContentCaptureWithServiceSettingsPreferenceController
+ extends TogglePreferenceController {
+
+ private static final String TAG = "ContentCaptureController";
+
+ public EnableContentCaptureWithServiceSettingsPreferenceController(@NonNull Context context,
+ @NonNull String key) {
+ super(context, key);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return ContentCaptureUtils.isEnabledForUser(mContext);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ ContentCaptureUtils.setEnabledForUser(mContext, isChecked);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+
+ ComponentName componentName = ContentCaptureUtils.getServiceSettingsComponentName();
+ if (componentName != null) {
+ preference.setIntent(new Intent(Intent.ACTION_MAIN).setComponent(componentName));
+ } else {
+ // Should not happen - preference should be disabled by controller
+ Log.w(TAG, "No component name for custom service settings");
+ preference.setSelectable(false);
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ boolean available = ContentCaptureUtils.isFeatureAvailable()
+ && ContentCaptureUtils.getServiceSettingsComponentName() != null;
+ return available ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+}
diff --git a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
index 704d1e5..7c55b51 100644
--- a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
+++ b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
@@ -182,10 +182,10 @@
return barViewInfos;
}
- private Drawable getPermissionGroupIcon(CharSequence permissionGroup) {
+ private Drawable getPermissionGroupIcon(String permissionGroup) {
Drawable icon = null;
try {
- icon = mPackageManager.getPermissionGroupInfo(permissionGroup.toString(), 0)
+ icon = mPackageManager.getPermissionGroupInfo(permissionGroup, 0)
.loadIcon(mPackageManager);
icon.setTintList(Utils.getColorAttr(mContext, android.R.attr.textColorSecondary));
} catch (PackageManager.NameNotFoundException e) {
@@ -195,10 +195,10 @@
return icon;
}
- private CharSequence getPermissionGroupLabel(CharSequence permissionGroup) {
+ private CharSequence getPermissionGroupLabel(String permissionGroup) {
CharSequence label = null;
try {
- label = mPackageManager.getPermissionGroupInfo(permissionGroup.toString(), 0)
+ label = mPackageManager.getPermissionGroupInfo(permissionGroup, 0)
.loadLabel(mPackageManager);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Cannot find group label for " + permissionGroup, e);
diff --git a/src/com/android/settings/privacy/PrivacyDashboardFragment.java b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
index be6701e..fa21f9d 100644
--- a/src/com/android/settings/privacy/PrivacyDashboardFragment.java
+++ b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
@@ -16,25 +16,22 @@
package com.android.settings.privacy;
-import android.app.ActionBar;
-import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
-import android.util.Log;
import android.view.View;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.LockScreenNotificationPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.ActionBarShadowController;
import java.util.ArrayList;
import java.util.List;
@@ -87,29 +84,11 @@
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- styleActionBar();
+ Utils.setActionBarShadowAnimation(getActivity(), getSettingsLifecycle(), getListView());
initLoadingBar();
}
@VisibleForTesting
- void styleActionBar() {
- final Activity activity = getActivity();
- final ActionBar actionBar = activity.getActionBar();
- final Lifecycle lifecycle = getSettingsLifecycle();
- final View scrollView = getListView();
-
- if (actionBar == null) {
- Log.w(TAG, "No actionbar, cannot style actionbar.");
- return;
- }
-
- actionBar.setElevation(0);
- if (lifecycle != null && scrollView != null) {
- ActionBarShadowController.attachToView(activity, lifecycle, scrollView);
- }
- }
-
- @VisibleForTesting
void initLoadingBar() {
mProgressHeader = setPinnedHeaderView(R.layout.progress_header);
mProgressAnimation = mProgressHeader.findViewById(R.id.progress_bar_animation);
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index dcb4acc..896f6e5 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -39,7 +39,7 @@
*/
public interface SearchFeatureProvider {
- int REQUEST_CODE = 0;
+ int REQUEST_CODE = 501;
/**
* Ensures the caller has necessary privilege to launch search result page.
diff --git a/src/com/android/settings/search/actionbar/SearchMenuController.java b/src/com/android/settings/search/actionbar/SearchMenuController.java
index af7141e..25d0d30 100644
--- a/src/com/android/settings/search/actionbar/SearchMenuController.java
+++ b/src/com/android/settings/search/actionbar/SearchMenuController.java
@@ -33,6 +33,7 @@
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.search.SearchFeatureProvider;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
@@ -93,7 +94,7 @@
FeatureFactory.getFactory(context).getMetricsFeatureProvider()
.action(context, SettingsEnums.ACTION_SEARCH_RESULTS);
- mHost.startActivityForResult(intent, 0 /* requestCode */);
+ mHost.startActivityForResult(intent, SearchFeatureProvider.REQUEST_CODE);
return true;
});
}
diff --git a/src/com/android/settings/sim/SimSelectNotification.java b/src/com/android/settings/sim/SimSelectNotification.java
index cf67f5e..891d171 100644
--- a/src/com/android/settings/sim/SimSelectNotification.java
+++ b/src/com/android/settings/sim/SimSelectNotification.java
@@ -16,6 +16,12 @@
package com.android.settings.sim;
+import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_ID;
+import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES;
+import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
+import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
+import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
+
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -23,19 +29,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import androidx.core.app.NotificationCompat;
-
-import com.android.internal.telephony.IccCardConstants;
import com.android.settings.R;
import com.android.settings.Settings.SimSettingsActivity;
-import com.android.settings.Utils;
-import java.util.List;
+import androidx.core.app.NotificationCompat;
public class SimSelectNotification extends BroadcastReceiver {
private static final String TAG = "SimSelectNotification";
@@ -46,71 +45,24 @@
@Override
public void onReceive(Context context, Intent intent) {
- final TelephonyManager telephonyManager = (TelephonyManager)
- context.getSystemService(Context.TELEPHONY_SERVICE);
- final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
- final int numSlots = telephonyManager.getSimCount();
-
- // Do not create notifications on single SIM devices or when provisioning i.e. Setup Wizard.
- if (numSlots < 2 || !Utils.isDeviceProvisioned(context)) {
- return;
- }
-
// Cancel any previous notifications
cancelNotification(context);
-
- // If sim state is not ABSENT or LOADED then ignore
- String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- if (!(IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus) ||
- IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus))) {
- Log.d(TAG, "sim state is not Absent or Loaded");
- return;
- } else {
- Log.d(TAG, "simstatus = " + simStatus);
- }
-
- int state;
- for (int i = 0; i < numSlots; i++) {
- state = telephonyManager.getSimState(i);
- if (!(state == TelephonyManager.SIM_STATE_ABSENT
- || state == TelephonyManager.SIM_STATE_READY
- || state == TelephonyManager.SIM_STATE_UNKNOWN)) {
- Log.d(TAG, "All sims not in valid state yet");
- return;
- }
- }
-
- List<SubscriptionInfo> sil = subscriptionManager.getActiveSubscriptionInfoList(true);
- if (sil == null || sil.size() < 1) {
- Log.d(TAG, "Subscription list is empty");
- return;
- }
-
- // Clear defaults for any subscriptions which no longer exist
- subscriptionManager.clearDefaultsForInactiveSubIds();
-
- boolean dataSelected = SubscriptionManager.isUsableSubIdValue(
- SubscriptionManager.getDefaultDataSubscriptionId());
- boolean smsSelected = SubscriptionManager.isUsableSubIdValue(
- SubscriptionManager.getDefaultSmsSubscriptionId());
-
- // If data and sms defaults are selected, dont show notification (Calls default is optional)
- if (dataSelected && smsSelected) {
- Log.d(TAG, "Data & SMS default sims are selected. No notification");
- return;
- }
-
// Create a notification to tell the user that some defaults are missing
createNotification(context);
- if (sil.size() == 1) {
+ int dialogType = intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE,
+ EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE);
+ if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES) {
+ int subId = intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_ID,
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+ int slotIndex = SubscriptionManager.getSlotIndex(subId);
// If there is only one subscription, ask if user wants to use if for everything
Intent newIntent = new Intent(context, SimDialogActivity.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
newIntent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY, SimDialogActivity.PREFERRED_PICK);
- newIntent.putExtra(SimDialogActivity.PREFERRED_SIM, sil.get(0).getSimSlotIndex());
+ newIntent.putExtra(SimDialogActivity.PREFERRED_SIM, slotIndex);
context.startActivity(newIntent);
- } else if (!dataSelected) {
+ } else if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA) {
// If there are mulitple, ensure they pick default data
Intent newIntent = new Intent(context, SimDialogActivity.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/src/com/android/settings/sim/SimSettings.java b/src/com/android/settings/sim/SimSettings.java
index b646b3b..e43c6a5 100644
--- a/src/com/android/settings/sim/SimSettings.java
+++ b/src/com/android/settings/sim/SimSettings.java
@@ -220,7 +220,7 @@
Log.d(TAG, "Register for call state change");
for (int i = 0; i < mPhoneCount; i++) {
int subId = mSelectableSubInfos.get(i).getSubscriptionId();
- tm.listen(getPhoneStateListener(i, subId),
+ tm.createForSubscriptionId(subId).listen(getPhoneStateListener(i),
PhoneStateListener.LISTEN_CALL_STATE);
}
}
@@ -239,13 +239,13 @@
}
}
- private PhoneStateListener getPhoneStateListener(int phoneId, int subId) {
+ private PhoneStateListener getPhoneStateListener(int phoneId) {
// Disable Sim selection for Data when voice call is going on as changing the default data
// sim causes a modem reset currently and call gets disconnected
// ToDo : Add subtext on disabled preference to let user know that default data sim cannot
// be changed while call is going on
final int i = phoneId;
- mPhoneStateListener[phoneId] = new PhoneStateListener(subId) {
+ mPhoneStateListener[phoneId] = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (DBG) log("PhoneStateListener.onCallStateChanged: state=" + state);
diff --git a/src/com/android/settings/slices/CustomSliceManager.java b/src/com/android/settings/slices/CustomSliceManager.java
deleted file mode 100644
index c9e473a..0000000
--- a/src/com/android/settings/slices/CustomSliceManager.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.slices;
-
-import android.content.Context;
-import android.net.Uri;
-import android.util.ArrayMap;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.settings.flashlight.FlashlightSlice;
-import com.android.settings.homepage.contextualcards.deviceinfo.BatteryInfoSlice;
-import com.android.settings.homepage.contextualcards.deviceinfo.DataUsageSlice;
-import com.android.settings.homepage.contextualcards.deviceinfo.DeviceInfoSlice;
-import com.android.settings.homepage.contextualcards.deviceinfo.EmergencyInfoSlice;
-import com.android.settings.homepage.contextualcards.deviceinfo.StorageSlice;
-import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
-import com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice;
-import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
-import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice;
-import com.android.settings.location.LocationSlice;
-import com.android.settings.media.MediaOutputSlice;
-import com.android.settings.network.telephony.MobileDataSlice;
-import com.android.settings.wifi.slice.ContextualWifiSlice;
-import com.android.settings.wifi.slice.WifiSlice;
-
-import java.util.Map;
-import java.util.WeakHashMap;
-
-/**
- * Manages custom {@link androidx.slice.Slice Slices}, which are all Slices not backed by
- * preferences.
- */
-public class CustomSliceManager {
-
- @VisibleForTesting
- final Map<Uri, Class<? extends CustomSliceable>> mUriMap;
-
- private final Context mContext;
- private final Map<Uri, CustomSliceable> mSliceableCache;
-
- public CustomSliceManager(Context context) {
- mContext = context.getApplicationContext();
- mUriMap = new ArrayMap<>();
- mSliceableCache = new WeakHashMap<>();
- addSlices();
- }
-
- /**
- * Return a {@link CustomSliceable} associated to the Uri.
- * <p>
- * Do not change this method signature to accommodate for a special-case slicable - a context is
- * the only thing that should be needed to create the object.
- */
- public CustomSliceable getSliceableFromUri(Uri uri) {
- final Uri newUri = removeParameterFromUri(uri);
- if (mSliceableCache.containsKey(newUri)) {
- return mSliceableCache.get(newUri);
- }
-
- final Class clazz = mUriMap.get(newUri);
- if (clazz == null) {
- throw new IllegalArgumentException("No Slice found for uri: " + uri);
- }
-
- final CustomSliceable sliceable = CustomSliceable.createInstance(mContext, clazz);
- mSliceableCache.put(newUri, sliceable);
- return sliceable;
- }
-
- private Uri removeParameterFromUri(Uri uri) {
- return uri != null ? uri.buildUpon().clearQuery().build() : null;
- }
-
- /**
- * Return a {@link CustomSliceable} associated to the Action.
- * <p>
- * Do not change this method signature to accommodate for a special-case sliceable - a context
- * is the only thing that should be needed to create the object.
- */
- public CustomSliceable getSliceableFromIntentAction(String action) {
- return getSliceableFromUri(Uri.parse(action));
- }
-
- /**
- * Returns {@code true} if {@param uri} is a valid Slice Uri handled by
- * {@link CustomSliceManager}.
- */
- public boolean isValidUri(Uri uri) {
- return mUriMap.containsKey(removeParameterFromUri(uri));
- }
-
- /**
- * Returns {@code true} if {@param action} is a valid intent action handled by
- * {@link CustomSliceManager}.
- */
- public boolean isValidAction(String action) {
- return isValidUri(Uri.parse(action));
- }
-
- private void addSlices() {
- mUriMap.put(CustomSliceRegistry.BATTERY_FIX_SLICE_URI, BatteryFixSlice.class);
- mUriMap.put(CustomSliceRegistry.BATTERY_INFO_SLICE_URI, BatteryInfoSlice.class);
- mUriMap.put(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI, BluetoothDevicesSlice.class);
- mUriMap.put(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI, ContextualWifiSlice.class);
- mUriMap.put(CustomSliceRegistry.DATA_USAGE_SLICE_URI, DataUsageSlice.class);
- mUriMap.put(CustomSliceRegistry.DEVICE_INFO_SLICE_URI, DeviceInfoSlice.class);
- mUriMap.put(CustomSliceRegistry.EMERGENCY_INFO_SLICE_URI, EmergencyInfoSlice.class);
- mUriMap.put(CustomSliceRegistry.FLASHLIGHT_SLICE_URI, FlashlightSlice.class);
- mUriMap.put(CustomSliceRegistry.LOCATION_SLICE_URI, LocationSlice.class);
- mUriMap.put(CustomSliceRegistry.LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
- mUriMap.put(CustomSliceRegistry.MOBILE_DATA_SLICE_URI, MobileDataSlice.class);
- mUriMap.put(CustomSliceRegistry.NOTIFICATION_CHANNEL_SLICE_URI,
- NotificationChannelSlice.class);
- mUriMap.put(CustomSliceRegistry.STORAGE_SLICE_URI, StorageSlice.class);
- mUriMap.put(CustomSliceRegistry.WIFI_SLICE_URI, WifiSlice.class);
- mUriMap.put(CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI, MediaOutputSlice.class);
- }
-}
diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java
index 3a1db69..fe000d1 100644
--- a/src/com/android/settings/slices/CustomSliceRegistry.java
+++ b/src/com/android/settings/slices/CustomSliceRegistry.java
@@ -24,11 +24,32 @@
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.SettingsSlicesContract;
+import android.util.ArrayMap;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.flashlight.FlashlightSlice;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
+import com.android.settings.homepage.contextualcards.deviceinfo.DataUsageSlice;
+import com.android.settings.homepage.contextualcards.deviceinfo.DeviceInfoSlice;
+import com.android.settings.homepage.contextualcards.deviceinfo.EmergencyInfoSlice;
+import com.android.settings.homepage.contextualcards.deviceinfo.StorageSlice;
+import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
+import com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice;
+import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice;
+import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
+import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice;
+import com.android.settings.location.LocationSlice;
+import com.android.settings.media.MediaOutputIndicatorSlice;
+import com.android.settings.media.MediaOutputSlice;
+import com.android.settings.network.telephony.MobileDataSlice;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
+import com.android.settings.wifi.slice.ContextualWifiSlice;
+import com.android.settings.wifi.slice.WifiSlice;
import com.android.settingslib.media.MediaOutputSliceConstants;
+import java.util.Map;
+
/**
* A registry of custom slice Uris.
*/
@@ -53,15 +74,7 @@
.appendEncodedPath(SettingsSlicesContract.PATH_SETTING_INTENT)
.appendPath(BatteryTipPreferenceController.PREF_NAME)
.build();
- /**
- * Backing Uri for the Battery info Slice.
- */
- public static final Uri BATTERY_INFO_SLICE_URI = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(SettingsSliceProvider.SLICE_AUTHORITY)
- .appendEncodedPath(SettingsSlicesContract.PATH_SETTING_INTENT)
- .appendPath("battery_card")
- .build();
+
/**
* Backing Uri for the Bluetooth Slice.
*/
@@ -83,6 +96,16 @@
.build();
/**
+ * Backing Uri for Contextual Notification channel Slice.
+ */
+ public static final Uri CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath("contextual_notification_channel")
+ .build();
+
+ /**
* Backing Uri for the Wifi Slice.
*/
public static final Uri CONTEXTUAL_WIFI_SLICE_URI = new Uri.Builder()
@@ -287,4 +310,63 @@
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath(MediaOutputSliceConstants.KEY_MEDIA_OUTPUT)
.build();
+
+ /**
+ * Backing Uri for the Media output indicator Slice.
+ */
+ public static Uri MEDIA_OUTPUT_INDICATOR_SLICE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_INTENT)
+ .appendPath("media_output_indicator")
+ .build();
+
+ @VisibleForTesting
+ static final Map<Uri, Class<? extends CustomSliceable>> sUriToSlice;
+
+ static {
+ sUriToSlice = new ArrayMap<>();
+
+ sUriToSlice.put(BATTERY_FIX_SLICE_URI, BatteryFixSlice.class);
+ sUriToSlice.put(BLUETOOTH_DEVICES_SLICE_URI, BluetoothDevicesSlice.class);
+ sUriToSlice.put(CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI,
+ ContextualNotificationChannelSlice.class);
+ sUriToSlice.put(CONTEXTUAL_WIFI_SLICE_URI, ContextualWifiSlice.class);
+ sUriToSlice.put(DATA_USAGE_SLICE_URI, DataUsageSlice.class);
+ sUriToSlice.put(DEVICE_INFO_SLICE_URI, DeviceInfoSlice.class);
+ sUriToSlice.put(EMERGENCY_INFO_SLICE_URI, EmergencyInfoSlice.class);
+ sUriToSlice.put(FLASHLIGHT_SLICE_URI, FlashlightSlice.class);
+ sUriToSlice.put(LOCATION_SLICE_URI, LocationSlice.class);
+ sUriToSlice.put(LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
+ sUriToSlice.put(MOBILE_DATA_SLICE_URI, MobileDataSlice.class);
+ sUriToSlice.put(NOTIFICATION_CHANNEL_SLICE_URI, NotificationChannelSlice.class);
+ sUriToSlice.put(STORAGE_SLICE_URI, StorageSlice.class);
+ sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
+ sUriToSlice.put(MEDIA_OUTPUT_SLICE_URI, MediaOutputSlice.class);
+ sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
+ }
+
+ public static Class<? extends CustomSliceable> getSliceClassByUri(Uri uri) {
+ return sUriToSlice.get(removeParameterFromUri(uri));
+ }
+
+ public static Uri removeParameterFromUri(Uri uri) {
+ return uri != null ? uri.buildUpon().clearQuery().build() : null;
+ }
+
+ /**
+ * Returns {@code true} if {@param uri} is a valid Slice Uri handled by
+ * {@link CustomSliceRegistry}.
+ */
+ public static boolean isValidUri(Uri uri) {
+ return sUriToSlice.containsKey(removeParameterFromUri(uri));
+ }
+
+ /**
+ * Returns {@code true} if {@param action} is a valid intent action handled by
+ * {@link CustomSliceRegistry}.
+ */
+ public static boolean isValidAction(String action) {
+ return isValidUri(Uri.parse(action));
+ }
}
diff --git a/src/com/android/settings/slices/CustomSliceable.java b/src/com/android/settings/slices/CustomSliceable.java
index 0b97bed..93d08a2 100644
--- a/src/com/android/settings/slices/CustomSliceable.java
+++ b/src/com/android/settings/slices/CustomSliceable.java
@@ -19,7 +19,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.net.Uri;
import androidx.slice.Slice;
@@ -106,16 +105,17 @@
/**
* Build an instance of a {@link CustomSliceable} which has a {@link Context}-only constructor.
*/
- static CustomSliceable createInstance(Context context, Class<CustomSliceable> sliceableClass) {
+ static CustomSliceable createInstance(Context context,
+ Class<? extends CustomSliceable> sliceable) {
try {
- final Constructor<CustomSliceable> sliceable =
- sliceableClass.getConstructor(Context.class);
- final Object[] params = new Object[]{context};
- return sliceable.newInstance(params);
+ final Constructor<? extends CustomSliceable> constructor =
+ sliceable.getConstructor(Context.class);
+ final Object[] params = new Object[]{context.getApplicationContext()};
+ return constructor.newInstance(params);
} catch (NoSuchMethodException | InstantiationException |
IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException(
- "Invalid sliceable class: " + sliceableClass, e);
+ "Invalid sliceable class: " + sliceable, e);
}
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 397b2fc..3bdcb13 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -28,12 +28,12 @@
import android.provider.SettingsSlicesContract;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
import androidx.slice.Slice;
import androidx.slice.SliceProvider;
@@ -53,7 +53,6 @@
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
/**
* A {@link SliceProvider} for Settings to enabled inline results in system apps.
@@ -118,17 +117,11 @@
private static final KeyValueListParser KEY_VALUE_LIST_PARSER = new KeyValueListParser(',');
@VisibleForTesting
- CustomSliceManager mCustomSliceManager;
-
- @VisibleForTesting
SlicesDatabaseAccessor mSlicesDatabaseAccessor;
@VisibleForTesting
Map<Uri, SliceData> mSliceWeakDataCache;
- @VisibleForTesting
- Map<Uri, SliceData> mSliceDataCache;
-
final Map<Uri, SliceBackgroundWorker> mPinnedWorkers = new ArrayMap<>();
public SettingsSliceProvider() {
@@ -138,17 +131,16 @@
@Override
public boolean onCreateSliceProvider() {
mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
- mSliceDataCache = new ConcurrentHashMap<>();
mSliceWeakDataCache = new WeakHashMap<>();
- mCustomSliceManager = FeatureFactory.getFactory(
- getContext()).getSlicesFeatureProvider().getCustomSliceManager(getContext());
return true;
}
@Override
public void onSlicePinned(Uri sliceUri) {
- if (mCustomSliceManager.isValidUri(sliceUri)) {
- final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(sliceUri);
+ if (CustomSliceRegistry.isValidUri(sliceUri)) {
+ final Context context = getContext();
+ final CustomSliceable sliceable = FeatureFactory.getFactory(context)
+ .getSlicesFeatureProvider().getSliceableFromUri(context, sliceUri);
final IntentFilter filter = sliceable.getIntentFilter();
if (filter != null) {
registerIntentToUri(filter, sliceUri);
@@ -173,7 +165,6 @@
public void onSliceUnpinned(Uri sliceUri) {
SliceBroadcastRelay.unregisterReceivers(getContext(), sliceUri);
ThreadUtils.postOnMainThread(() -> stopBackgroundWorker(sliceUri));
- mSliceDataCache.remove(sliceUri);
}
@Override
@@ -194,10 +185,11 @@
// Before adding a slice to {@link CustomSliceManager}, please get approval
// from the Settings team.
- if (mCustomSliceManager.isValidUri(sliceUri)) {
- final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(
- sliceUri);
- return sliceable.getSlice();
+ if (CustomSliceRegistry.isValidUri(sliceUri)) {
+ final Context context = getContext();
+ return FeatureFactory.getFactory(context)
+ .getSlicesFeatureProvider().getSliceableFromUri(context, sliceUri)
+ .getSlice();
}
if (CustomSliceRegistry.WIFI_CALLING_URI.equals(sliceUri)) {
@@ -228,7 +220,7 @@
}
// Remove the SliceData from the cache after it has been used to prevent a memory-leak.
- if (!mSliceDataCache.containsKey(sliceUri)) {
+ if (!getPinnedSlices().contains(sliceUri)) {
mSliceWeakDataCache.remove(sliceUri);
}
return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData);
@@ -398,11 +390,6 @@
ThreadUtils.postOnMainThread(() -> startBackgroundWorker(controller, uri));
- final List<Uri> pinnedSlices = getContext().getSystemService(
- SliceManager.class).getPinnedSlices();
- if (pinnedSlices.contains(uri)) {
- mSliceDataCache.put(uri, sliceData);
- }
mSliceWeakDataCache.put(uri, sliceData);
getContext().getContentResolver().notifyChange(uri, null /* content observer */);
diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java
index 559aa71..f19b1df 100644
--- a/src/com/android/settings/slices/SliceBackgroundWorker.java
+++ b/src/com/android/settings/slices/SliceBackgroundWorker.java
@@ -146,7 +146,7 @@
needNotify = true;
}
} else {
- needNotify = !results.equals(mCachedResults);
+ needNotify = !areListsTheSame(results, mCachedResults);
}
if (needNotify) {
@@ -155,6 +155,10 @@
}
}
+ protected boolean areListsTheSame(List<E> a, List<E> b) {
+ return a.equals(b);
+ }
+
/**
* Notify that data was updated and attempt to sync changes to the Slice.
*/
diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java
index b2ea583..9926a52 100644
--- a/src/com/android/settings/slices/SliceBroadcastReceiver.java
+++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java
@@ -61,11 +61,10 @@
final boolean isPlatformSlice = intent.getBooleanExtra(EXTRA_SLICE_PLATFORM_DEFINED,
false /* default */);
- final CustomSliceManager mCustomSliceManager = FeatureFactory.getFactory(
- context).getSlicesFeatureProvider().getCustomSliceManager(context);
- if (mCustomSliceManager.isValidAction(action)) {
+ if (CustomSliceRegistry.isValidAction(action)) {
final CustomSliceable sliceable =
- mCustomSliceManager.getSliceableFromIntentAction(action);
+ CustomSliceable.createInstance(context,
+ CustomSliceRegistry.getSliceClassByUri(Uri.parse(action)));
sliceable.onNotifyChange(intent);
return;
}
diff --git a/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java b/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java
index f4afa16..2ff071e 100644
--- a/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java
+++ b/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java
@@ -23,7 +23,6 @@
import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settings.notification.ZenModeSliceBuilder;
-import com.android.settings.overlay.FeatureFactory;
public class SliceDeepLinkSpringBoard extends Activity {
@@ -45,11 +44,10 @@
Intent launchIntent;
// TODO (b/80263568) Avoid duplicating this list of Slice Uris.
- final CustomSliceManager customSliceManager = FeatureFactory.getFactory(this)
- .getSlicesFeatureProvider().getCustomSliceManager(this);
- if (customSliceManager.isValidUri(sliceUri)) {
+ if (CustomSliceRegistry.isValidUri(sliceUri)) {
final CustomSliceable sliceable =
- customSliceManager.getSliceableFromUri(sliceUri);
+ CustomSliceable.createInstance(getApplicationContext(),
+ CustomSliceRegistry.getSliceClassByUri(sliceUri));
launchIntent = sliceable.getIntent();
} else if (CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) {
launchIntent = ZenModeSliceBuilder.getIntent(this /* context */);
diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java
index 1a9fd98..ae94f29 100644
--- a/src/com/android/settings/slices/SlicesFeatureProvider.java
+++ b/src/com/android/settings/slices/SlicesFeatureProvider.java
@@ -1,6 +1,7 @@
package com.android.settings.slices;
import android.content.Context;
+import android.net.Uri;
import com.android.settings.network.telephony.Enhanced4gLteSliceHelper;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -12,11 +13,23 @@
boolean DEBUG = false;
- SlicesIndexer getSliceIndexer(Context context);
-
SliceDataConverter getSliceDataConverter(Context context);
/**
+ * Starts a new UI session for the purpose of using Slices.
+ *
+ * A UI session is defined as an duration of time when user stays in a UI screen. Screen
+ * rotation does not break the continuation of session, going to a sub-page and coming out does
+ * not break the continuation either. Leaving the page and coming back breaks it.
+ */
+ void newUiSession();
+
+ /**
+ * Returns the token created in {@link #newUiSession}.
+ */
+ long getUiSessionToken();
+
+ /**
* Asynchronous call to index the data used to build Slices.
* If the data is already indexed, the data will not change.
*/
@@ -28,7 +41,14 @@
*/
void indexSliceData(Context context);
- CustomSliceManager getCustomSliceManager(Context context);
+
+ /**
+ * Return a {@link CustomSliceable} associated to the Uri.
+ * <p>
+ * Do not change this method signature to accommodate for a special-case sliceable - a context
+ * is the only thing that should be needed to create the object.
+ */
+ CustomSliceable getSliceableFromUri(Context context, Uri uri);
/**
* Gets new WifiCallingSliceHelper object
diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
index 508eb1c..297f2c1 100644
--- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
+++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
@@ -17,27 +17,23 @@
package com.android.settings.slices;
import android.content.Context;
+import android.net.Uri;
+import android.os.SystemClock;
import com.android.settings.network.telephony.Enhanced4gLteSliceHelper;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
import com.android.settingslib.utils.ThreadUtils;
+import java.util.Random;
+
/**
* Manages Slices in Settings.
*/
public class SlicesFeatureProviderImpl implements SlicesFeatureProvider {
+ private long mUiSessionToken;
private SlicesIndexer mSlicesIndexer;
private SliceDataConverter mSliceDataConverter;
- private CustomSliceManager mCustomSliceManager;
-
- @Override
- public SlicesIndexer getSliceIndexer(Context context) {
- if (mSlicesIndexer == null) {
- mSlicesIndexer = new SlicesIndexer(context.getApplicationContext());
- }
- return mSlicesIndexer;
- }
@Override
public SliceDataConverter getSliceDataConverter(Context context) {
@@ -48,11 +44,13 @@
}
@Override
- public CustomSliceManager getCustomSliceManager(Context context) {
- if (mCustomSliceManager == null) {
- mCustomSliceManager = new CustomSliceManager(context.getApplicationContext());
- }
- return mCustomSliceManager;
+ public void newUiSession() {
+ mUiSessionToken = SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public long getUiSessionToken() {
+ return mUiSessionToken;
}
@Override
@@ -76,4 +74,23 @@
public Enhanced4gLteSliceHelper getNewEnhanced4gLteSliceHelper(Context context) {
return new Enhanced4gLteSliceHelper(context);
}
+
+ @Override
+ public CustomSliceable getSliceableFromUri(Context context, Uri uri) {
+ final Uri newUri = CustomSliceRegistry.removeParameterFromUri(uri);
+ final Class clazz = CustomSliceRegistry.getSliceClassByUri(newUri);
+ if (clazz == null) {
+ throw new IllegalArgumentException("No Slice found for uri: " + uri);
+ }
+
+ final CustomSliceable sliceable = CustomSliceable.createInstance(context, clazz);
+ return sliceable;
+ }
+
+ private SlicesIndexer getSliceIndexer(Context context) {
+ if (mSlicesIndexer == null) {
+ mSlicesIndexer = new SlicesIndexer(context.getApplicationContext());
+ }
+ return mSlicesIndexer;
+ }
}
diff --git a/src/com/android/settings/slices/SlicesIndexer.java b/src/com/android/settings/slices/SlicesIndexer.java
index 1b3a25e..5f80047 100644
--- a/src/com/android/settings/slices/SlicesIndexer.java
+++ b/src/com/android/settings/slices/SlicesIndexer.java
@@ -84,7 +84,6 @@
} finally {
database.endTransaction();
}
- database.close();
}
@VisibleForTesting
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index f77dfca..0da0f21 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -32,7 +32,6 @@
import android.media.MediaRouter.Callback;
import android.os.Handler;
import android.os.Looper;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
@@ -40,7 +39,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.FeatureFlags;
@@ -63,15 +61,11 @@
/**
* Abstract class for audio switcher controller to notify subclass
* updating the current status of switcher entry. Subclasses must overwrite
- * {@link #setActiveBluetoothDevice(BluetoothDevice)} to set the
- * active device for corresponding profile.
*/
public abstract class AudioSwitchPreferenceController extends BasePreferenceController
- implements Preference.OnPreferenceChangeListener, BluetoothCallback,
- LifecycleObserver, OnStart, OnStop {
+ implements BluetoothCallback, LifecycleObserver, OnStart, OnStop {
private static final String TAG = "AudioSwitchPrefCtrl";
- private static final int INVALID_INDEX = -1;
protected final List<BluetoothDevice> mConnectedDevices;
protected final AudioManager mAudioManager;
@@ -129,35 +123,6 @@
}
@Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final String address = (String) newValue;
- if (!(preference instanceof ListPreference)) {
- return false;
- }
-
- final ListPreference listPreference = (ListPreference) preference;
- if (TextUtils.equals(address, mContext.getText(R.string.media_output_default_summary))) {
- // Switch to default device which address is device name
- mSelectedIndex = getDefaultDeviceIndex();
- setActiveBluetoothDevice(null);
- listPreference.setSummary(mContext.getText(R.string.media_output_default_summary));
- } else {
- // Switch to BT device which address is hardware address
- final int connectedDeviceIndex = getConnectedDeviceIndex(address);
- if (connectedDeviceIndex == INVALID_INDEX) {
- return false;
- }
- final BluetoothDevice btDevice = mConnectedDevices.get(connectedDeviceIndex);
- mSelectedIndex = connectedDeviceIndex;
- setActiveBluetoothDevice(btDevice);
- listPreference.setSummary(btDevice.getAliasName());
- }
- return true;
- }
-
- public abstract void setActiveBluetoothDevice(BluetoothDevice device);
-
- @Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(mPreferenceKey);
@@ -185,6 +150,12 @@
}
@Override
+ public void onBluetoothStateChanged(int bluetoothState) {
+ // To handle the case that Bluetooth on and no connected devices
+ updateState(mPreference);
+ }
+
+ @Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
updateState(mPreference);
}
@@ -236,21 +207,15 @@
}
/**
- * get A2dp connected device
+ * get A2dp devices on all states
+ * (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
*/
- protected List<BluetoothDevice> getConnectedA2dpDevices() {
- final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ protected List<BluetoothDevice> getConnectableA2dpDevices() {
final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
if (a2dpProfile == null) {
- return connectedDevices;
+ return new ArrayList<>();
}
- final List<BluetoothDevice> devices = a2dpProfile.getConnectedDevices();
- for (BluetoothDevice device : devices) {
- if (device.isConnected()) {
- connectedDevices.add(device);
- }
- }
- return connectedDevices;
+ return a2dpProfile.getConnectableDevices();
}
/**
@@ -277,6 +242,31 @@
}
/**
+ * get hearing aid profile devices on all states
+ * (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
+ * exclude other devices with same hiSyncId.
+ */
+ protected List<BluetoothDevice> getConnectableHearingAidDevices() {
+ final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ if (hapProfile == null) {
+ return connectedDevices;
+ }
+ final List<Long> devicesHiSyncIds = new ArrayList<>();
+ final List<BluetoothDevice> devices = hapProfile.getConnectableDevices();
+ for (BluetoothDevice device : devices) {
+ final long hiSyncId = hapProfile.getHiSyncId(device);
+ // device with same hiSyncId should not be shown in the UI.
+ // So do not add it into connectedDevices.
+ if (!devicesHiSyncIds.contains(hiSyncId)) {
+ devicesHiSyncIds.add(hiSyncId);
+ connectedDevices.add(device);
+ }
+ }
+ return connectedDevices;
+ }
+
+ /**
* Find active hearing aid device
*/
protected BluetoothDevice findActiveHearingAidDevice() {
@@ -306,52 +296,6 @@
*/
public abstract BluetoothDevice findActiveDevice();
- int getDefaultDeviceIndex() {
- // Default device is after all connected devices.
- return mConnectedDevices.size();
- }
-
- void setupPreferenceEntries(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
- BluetoothDevice activeDevice) {
- // default to current device
- mSelectedIndex = getDefaultDeviceIndex();
- // default device is after all connected devices.
- mediaOutputs[mSelectedIndex] = mContext.getText(R.string.media_output_default_summary);
- // use default device name as address
- mediaValues[mSelectedIndex] = mContext.getText(R.string.media_output_default_summary);
- for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
- final BluetoothDevice btDevice = mConnectedDevices.get(i);
- mediaOutputs[i] = btDevice.getAliasName();
- mediaValues[i] = btDevice.getAddress();
- if (btDevice.equals(activeDevice)) {
- // select the active connected device.
- mSelectedIndex = i;
- }
- }
- }
-
- void setPreference(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
- Preference preference) {
- final ListPreference listPreference = (ListPreference) preference;
- listPreference.setEntries(mediaOutputs);
- listPreference.setEntryValues(mediaValues);
- listPreference.setValueIndex(mSelectedIndex);
- listPreference.setSummary(mediaOutputs[mSelectedIndex]);
- mAudioSwitchPreferenceCallback.onPreferenceDataChanged(listPreference);
- }
-
- private int getConnectedDeviceIndex(String hardwareAddress) {
- if (mConnectedDevices != null) {
- for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
- final BluetoothDevice btDevice = mConnectedDevices.get(i);
- if (TextUtils.equals(btDevice.getAddress(), hardwareAddress)) {
- return i;
- }
- }
- }
- return INVALID_INDEX;
- }
-
private void register() {
mLocalBluetoothManager.getEventManager().registerCallback(this);
mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
diff --git a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
index a02c0b2..9157477 100644
--- a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
+++ b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
@@ -20,7 +20,9 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.text.TextUtils;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
import com.android.settings.R;
@@ -32,14 +34,56 @@
* This class allows switching between HFP-connected & HAP-connected BT devices
* while in on-call state.
*/
-public class HandsFreeProfileOutputPreferenceController extends
- AudioSwitchPreferenceController {
+public class HandsFreeProfileOutputPreferenceController extends AudioSwitchPreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final int INVALID_INDEX = -1;
public HandsFreeProfileOutputPreferenceController(Context context, String key) {
super(context, key);
}
@Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final String address = (String) newValue;
+ if (!(preference instanceof ListPreference)) {
+ return false;
+ }
+
+ final CharSequence defaultSummary = mContext.getText(R.string.media_output_default_summary);
+ final ListPreference listPreference = (ListPreference) preference;
+ if (TextUtils.equals(address, defaultSummary)) {
+ // Switch to default device which address is device name
+ mSelectedIndex = getDefaultDeviceIndex();
+ setActiveBluetoothDevice(null);
+ listPreference.setSummary(defaultSummary);
+ } else {
+ // Switch to BT device which address is hardware address
+ final int connectedDeviceIndex = getConnectedDeviceIndex(address);
+ if (connectedDeviceIndex == INVALID_INDEX) {
+ return false;
+ }
+ final BluetoothDevice btDevice = mConnectedDevices.get(connectedDeviceIndex);
+ mSelectedIndex = connectedDeviceIndex;
+ setActiveBluetoothDevice(btDevice);
+ listPreference.setSummary(btDevice.getAliasName());
+ }
+ return true;
+ }
+
+ private int getConnectedDeviceIndex(String hardwareAddress) {
+ if (mConnectedDevices != null) {
+ for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
+ final BluetoothDevice btDevice = mConnectedDevices.get(i);
+ if (TextUtils.equals(btDevice.getAddress(), hardwareAddress)) {
+ return i;
+ }
+ }
+ }
+ return INVALID_INDEX;
+ }
+
+ @Override
public void updateState(Preference preference) {
if (preference == null) {
// In case UI is not ready.
@@ -83,7 +127,41 @@
setPreference(mediaOutputs, mediaValues, preference);
}
- @Override
+ int getDefaultDeviceIndex() {
+ // Default device is after all connected devices.
+ return mConnectedDevices.size();
+ }
+
+ void setupPreferenceEntries(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
+ BluetoothDevice activeDevice) {
+ // default to current device
+ mSelectedIndex = getDefaultDeviceIndex();
+ // default device is after all connected devices.
+ final CharSequence defaultSummary = mContext.getText(R.string.media_output_default_summary);
+ mediaOutputs[mSelectedIndex] = defaultSummary;
+ // use default device name as address
+ mediaValues[mSelectedIndex] = defaultSummary;
+ for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
+ final BluetoothDevice btDevice = mConnectedDevices.get(i);
+ mediaOutputs[i] = btDevice.getAliasName();
+ mediaValues[i] = btDevice.getAddress();
+ if (btDevice.equals(activeDevice)) {
+ // select the active connected device.
+ mSelectedIndex = i;
+ }
+ }
+ }
+
+ void setPreference(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
+ Preference preference) {
+ final ListPreference listPreference = (ListPreference) preference;
+ listPreference.setEntries(mediaOutputs);
+ listPreference.setEntryValues(mediaValues);
+ listPreference.setValueIndex(mSelectedIndex);
+ listPreference.setSummary(mediaOutputs[mSelectedIndex]);
+ mAudioSwitchPreferenceCallback.onPreferenceDataChanged(listPreference);
+ }
+
public void setActiveBluetoothDevice(BluetoothDevice device) {
if (!Utils.isAudioModeOngoingCall(mContext)) {
return;
diff --git a/src/com/android/settings/sound/MediaOutputPreferenceController.java b/src/com/android/settings/sound/MediaOutputPreferenceController.java
index ce476ad..47810f7 100644
--- a/src/com/android/settings/sound/MediaOutputPreferenceController.java
+++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java
@@ -16,13 +16,14 @@
package com.android.settings.sound;
-import static android.bluetooth.IBluetoothHearingAid.HI_SYNC_ID_INVALID;
import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.content.Intent;
import android.media.AudioManager;
+import android.text.TextUtils;
import androidx.preference.Preference;
@@ -30,12 +31,16 @@
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.media.MediaOutputSliceConstants;
+
+import java.util.List;
/**
- * This class which allows switching between A2dp-connected & HAP-connected BT devices.
- * A few conditions will disable this switcher:
- * - No available BT device(s)
- * - Media stream captured by cast device
+ * This class allows launching MediaOutputSlice to switch output device.
+ * Preference would hide only when
+ * - Bluetooth = OFF
+ * - Bluetooth = ON and Connected Devices = 0 and Previously Connected = 0
+ * - Media stream captured by remote device
* - During a call.
*/
public class MediaOutputPreferenceController extends AudioSwitchPreferenceController {
@@ -66,40 +71,22 @@
return;
}
- mConnectedDevices.clear();
- // Otherwise, list all of the A2DP connected device and display the active device.
- if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
- mConnectedDevices.addAll(getConnectedA2dpDevices());
- mConnectedDevices.addAll(getConnectedHearingAidDevices());
+ boolean deviceConnectable = false;
+ BluetoothDevice activeDevice = null;
+ // Show preference if there is connected or previously connected device
+ // Find active device and set its name as the preference's summary
+ List<BluetoothDevice> connectableA2dpDevices = getConnectableA2dpDevices();
+ List<BluetoothDevice> connectableHADevices = getConnectableHearingAidDevices();
+ if (mAudioManager.getMode() == AudioManager.MODE_NORMAL
+ && ((connectableA2dpDevices != null && !connectableA2dpDevices.isEmpty())
+ || (connectableHADevices != null && !connectableHADevices.isEmpty()))) {
+ deviceConnectable = true;
+ activeDevice = findActiveDevice();
}
-
- final int numDevices = mConnectedDevices.size();
- mPreference.setVisible((numDevices == 0) ? false : true);
- CharSequence[] mediaOutputs = new CharSequence[numDevices + 1];
- CharSequence[] mediaValues = new CharSequence[numDevices + 1];
-
- // Setup devices entries, select active connected device
- setupPreferenceEntries(mediaOutputs, mediaValues, findActiveDevice());
-
- // Display connected devices, default device and show the active device
- setPreference(mediaOutputs, mediaValues, preference);
- }
-
- @Override
- public void setActiveBluetoothDevice(BluetoothDevice device) {
- if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
- return;
- }
- final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
- final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
- if (hapProfile != null && a2dpProfile != null && device == null) {
- hapProfile.setActiveDevice(null);
- a2dpProfile.setActiveDevice(null);
- } else if (hapProfile != null && hapProfile.getHiSyncId(device) != HI_SYNC_ID_INVALID) {
- hapProfile.setActiveDevice(device);
- } else if (a2dpProfile != null) {
- a2dpProfile.setActiveDevice(device);
- }
+ mPreference.setVisible(deviceConnectable);
+ mPreference.setSummary((activeDevice == null) ?
+ mContext.getText(R.string.media_output_default_summary) :
+ activeDevice.getAliasName());
}
@Override
@@ -112,4 +99,34 @@
}
return activeDevice;
}
+
+ /**
+ * Find active hearing aid device
+ */
+ @Override
+ protected BluetoothDevice findActiveHearingAidDevice() {
+ final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+
+ if (hearingAidProfile != null) {
+ List<BluetoothDevice> activeDevices = hearingAidProfile.getActiveDevices();
+ for (BluetoothDevice btDevice : activeDevices) {
+ if (btDevice != null) {
+ return btDevice;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ final Intent intent = new Intent()
+ .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/com/android/settings/utils/ContentCaptureUtils.java b/src/com/android/settings/utils/ContentCaptureUtils.java
new file mode 100644
index 0000000..a92651f
--- /dev/null
+++ b/src/com/android/settings/utils/ContentCaptureUtils.java
@@ -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.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureManager;
+
+public final class ContentCaptureUtils {
+
+ private static final String TAG = ContentCaptureUtils.class.getSimpleName();
+ private static final int MY_USER_ID = UserHandle.myUserId();
+
+ public static boolean isEnabledForUser(@NonNull Context context) {
+ boolean enabled = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.CONTENT_CAPTURE_ENABLED, 1, MY_USER_ID) == 1;
+ return enabled;
+ }
+
+ public static void setEnabledForUser(@NonNull Context context, boolean enabled) {
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.CONTENT_CAPTURE_ENABLED, enabled ? 1 : 0, MY_USER_ID);
+ }
+
+ public static boolean isFeatureAvailable() {
+ // We cannot look for ContentCaptureManager, because it's not available if the service
+ // didn't whitelist Settings
+ IBinder service = ServiceManager.checkService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+ return service != null;
+ }
+
+ @Nullable
+ public static ComponentName getServiceSettingsComponentName() {
+ try {
+ return ContentCaptureManager.getServiceSettingsComponentName();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Could not get service settings: " + e);
+ return null;
+ }
+ }
+
+ private ContentCaptureUtils() {
+ throw new UnsupportedOperationException("contains only static methods");
+ }
+}
diff --git a/src/com/android/settings/widget/AdaptiveHomepageIcon.java b/src/com/android/settings/widget/AdaptiveIcon.java
similarity index 76%
rename from src/com/android/settings/widget/AdaptiveHomepageIcon.java
rename to src/com/android/settings/widget/AdaptiveIcon.java
index bc7aaa7..04b33ae 100644
--- a/src/com/android/settings/widget/AdaptiveHomepageIcon.java
+++ b/src/com/android/settings/widget/AdaptiveIcon.java
@@ -35,14 +35,15 @@
import com.android.settings.homepage.AdaptiveIconShapeDrawable;
import com.android.settingslib.drawer.Tile;
-public class AdaptiveHomepageIcon extends LayerDrawable {
+public class AdaptiveIcon extends LayerDrawable {
private static final String TAG = "AdaptiveHomepageIcon";
@VisibleForTesting(otherwise = NONE)
int mBackgroundColor = -1;
+ private AdaptiveConstantState mAdaptiveConstantState;
- public AdaptiveHomepageIcon(Context context, Drawable foreground) {
+ public AdaptiveIcon(Context context, Drawable foreground) {
super(new Drawable[]{
new AdaptiveIconShapeDrawable(context.getResources()),
foreground
@@ -50,6 +51,7 @@
final int insetPx = context.getResources()
.getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx);
+ mAdaptiveConstantState = new AdaptiveConstantState(context, foreground);
}
public void setBackgroundColor(Context context, Tile tile) {
@@ -85,5 +87,36 @@
mBackgroundColor = color;
getDrawable(0).setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
Log.d(TAG, "Setting background color " + mBackgroundColor);
+ mAdaptiveConstantState.color = color;
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mAdaptiveConstantState;
+ }
+
+ @VisibleForTesting
+ static class AdaptiveConstantState extends ConstantState {
+ Context context;
+ Drawable drawable;
+ int color;
+
+ public AdaptiveConstantState(Context context, Drawable drawable) {
+ this.context = context;
+ this.drawable = drawable;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ final AdaptiveIcon icon = new AdaptiveIcon(context, drawable);
+ icon.setBackgroundColor(color);
+
+ return icon;
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
}
}
diff --git a/src/com/android/settings/widget/AppCheckBoxPreference.java b/src/com/android/settings/widget/AppCheckBoxPreference.java
index e56efd9..3ce67eb 100644
--- a/src/com/android/settings/widget/AppCheckBoxPreference.java
+++ b/src/com/android/settings/widget/AppCheckBoxPreference.java
@@ -21,7 +21,6 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
-import android.widget.TextView;
import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceViewHolder;
@@ -46,11 +45,6 @@
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- final TextView appendix = (TextView) holder.findViewById(R.id.appendix);
- if (appendix != null) {
- appendix.setVisibility(View.GONE);
- }
-
final LinearLayout layout = (LinearLayout) holder.findViewById(R.id.summary_container);
if (layout != null) {
// If summary doesn't exist, make it gone
diff --git a/src/com/android/settings/widget/AppSwitchPreference.java b/src/com/android/settings/widget/AppSwitchPreference.java
index 288096e..506ab76 100644
--- a/src/com/android/settings/widget/AppSwitchPreference.java
+++ b/src/com/android/settings/widget/AppSwitchPreference.java
@@ -17,10 +17,7 @@
package com.android.settings.widget;
import android.content.Context;
-import android.text.TextUtils;
-import android.view.View;
-import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
import com.android.settings.R;
@@ -31,12 +28,4 @@
super(context);
setLayoutResource(R.layout.preference_app);
}
-
- @Override
- public void onBindViewHolder(PreferenceViewHolder view) {
- super.onBindViewHolder(view);
-
- view.findViewById(R.id.summary_container)
- .setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
- }
}
diff --git a/src/com/android/settings/widget/SingleTargetGearPreference.java b/src/com/android/settings/widget/SingleTargetGearPreference.java
index 48876fa..f6496ed 100644
--- a/src/com/android/settings/widget/SingleTargetGearPreference.java
+++ b/src/com/android/settings/widget/SingleTargetGearPreference.java
@@ -18,9 +18,10 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.View;
-import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
@@ -28,23 +29,38 @@
* A preference with single target and a gear icon on the side.
*/
public class SingleTargetGearPreference extends Preference {
-
- public SingleTargetGearPreference(Context context, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
+ public SingleTargetGearPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- setWidgetLayoutResource(R.layout.preference_widget_gear_no_bg);
+ init();
}
public SingleTargetGearPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0 /* defStyleRes */);
+ super(context, attrs, defStyleAttr);
+ init();
}
public SingleTargetGearPreference(Context context, AttributeSet attrs) {
- this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
- android.R.attr.preferenceStyle));
+ super(context, attrs);
+ init();
}
public SingleTargetGearPreference(Context context) {
- this(context, null /* attrs */);
+ super(context);
+ init();
}
-}
\ No newline at end of file
+
+ private void init() {
+ setLayoutResource(R.layout.preference_single_target);
+ setWidgetLayoutResource(R.layout.preference_widget_gear_optional_background);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ final View divider = holder.findViewById(com.android.settingslib.R.id.two_target_divider);
+ if (divider != null) {
+ divider.setVisibility(View.INVISIBLE);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/AddWifiNetworkPreference.java b/src/com/android/settings/wifi/AddWifiNetworkPreference.java
new file mode 100644
index 0000000..cd2e4a8
--- /dev/null
+++ b/src/com/android/settings/wifi/AddWifiNetworkPreference.java
@@ -0,0 +1,83 @@
+/*
+ * 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.wifi;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageButton;
+
+import androidx.annotation.DrawableRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settings.wifi.dpp.WifiDppUtils;
+
+/**
+ * The Preference for users to add Wi-Fi networks in WifiSettings
+ */
+public class AddWifiNetworkPreference extends Preference {
+
+ private static final String TAG = "AddWifiNetworkPreference";
+
+ private boolean mInitialized;
+
+ public AddWifiNetworkPreference(Context context) {
+ super(context);
+
+ setLayoutResource(com.android.settingslib.R.layout.preference_access_point);
+ setWidgetLayoutResource(R.layout.wifi_button_preference_widget);
+ setIcon(R.drawable.ic_menu_add);
+ setTitle(R.string.wifi_add_network);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ if (!mInitialized) {
+ mInitialized = true;
+
+ final ImageButton imageButton = (ImageButton) holder.findViewById(R.id.button_icon);
+ imageButton.setImageDrawable(getDrawable(R.drawable.ic_scan_24dp));
+ imageButton.setContentDescription(
+ getContext().getString(R.string.wifi_dpp_scan_qr_code));
+ imageButton.setOnClickListener(view -> {
+ getContext().startActivity(
+ WifiDppUtils.getEnrolleeQrCodeScannerIntent(/* ssid */ null));
+ });
+
+ final View divider = (View) holder.findViewById(
+ com.android.settingslib.R.id.two_target_divider);
+ divider.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private Drawable getDrawable(@DrawableRes int iconResId) {
+ Drawable buttonIcon = null;
+
+ try {
+ buttonIcon = getContext().getDrawable(iconResId);
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return buttonIcon;
+ }
+}
diff --git a/src/com/android/settings/wifi/ButtonPreference.java b/src/com/android/settings/wifi/ButtonPreference.java
deleted file mode 100644
index 9a0abf6..0000000
--- a/src/com/android/settings/wifi/ButtonPreference.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.settings.wifi;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageButton;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settings.R;
-
-/**
- * This preference provides one button layout with Settings style.
- * It looks like below
- *
- * --------------------------------------------------------------
- * | icon | title | button |
- * --------------------------------------------------------------
- *
- * User can set icon / click listener for button.
- * By default, the button is invisible.
- */
-public class ButtonPreference extends Preference {
-
- private static final String TAG = "ButtonPreference";
-
- private ImageButton mImageButton;
- private Drawable mButtonIcon;
- private View.OnClickListener mClickListener;
-
- // Used for dummy pref.
- public ButtonPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- setWidgetLayoutResource(R.layout.wifi_button_preference_widget);
- mImageButton = null;
- mButtonIcon = null;
- mClickListener = null;
- }
-
- public ButtonPreference(Context context) {
- this(context, /* attrs */ null);
- }
-
- @Override
- public void onBindViewHolder(final PreferenceViewHolder view) {
- super.onBindViewHolder(view);
- initButton(view);
- }
-
- @Override
- public void setOrder(int order) {
- super.setOrder(order);
- setButtonVisibility();
- }
-
- @VisibleForTesting
- protected void initButton(final PreferenceViewHolder view) {
- if (mImageButton == null) {
- mImageButton = (ImageButton) view.findViewById(R.id.button_icon);
- }
- if (mImageButton != null) {
- mImageButton.setImageDrawable(mButtonIcon);
- mImageButton.setOnClickListener(mClickListener);
- }
- setButtonVisibility();
- }
-
- private void setButtonVisibility() {
- if(mImageButton != null) {
- mImageButton.setVisibility(mButtonIcon == null ? View.GONE : View.VISIBLE);
- }
- }
-
- /**
- * Sets the drawable to be displayed in button.
- */
- public ButtonPreference setButtonIcon(@DrawableRes int iconResId) {
- if (iconResId == 0) {
- return this;
- }
-
- try {
- mButtonIcon = getContext().getDrawable(iconResId);
- notifyChanged();
- } catch (Resources.NotFoundException exception) {
- Log.e(TAG, "Resource does not exist: " + iconResId);
- }
- return this;
- }
-
- /**
- * Register a callback to be invoked when button is clicked.
- */
- public ButtonPreference setButtonOnClickListener(View.OnClickListener listener) {
- if (listener != mClickListener) {
- mClickListener = listener;
- notifyChanged();
- }
- return this;
- }
-}
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
index ba29e3a..e9057e6 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
@@ -82,11 +82,15 @@
@VisibleForTesting
final static String EXTRA_APP_NAME = "com.android.settings.wifi.extra.APP_NAME";
+ final static String EXTRA_IS_SPECIFIED_SSID =
+ "com.android.settings.wifi.extra.REQUEST_IS_FOR_SINGLE_NETWORK";
private List<AccessPoint> mAccessPointList;
private FilterWifiTracker mFilterWifiTracker;
private AccessPointAdapter mDialogAdapter;
private NetworkRequestUserSelectionCallback mUserSelectionCallback;
+ private boolean mIsSpecifiedSsid;
+ private boolean mWaitingConnectCallback;
public static NetworkRequestDialogFragment newInstance() {
NetworkRequestDialogFragment dialogFragment = new NetworkRequestDialogFragment();
@@ -104,6 +108,11 @@
final TextView title = customTitle.findViewById(R.id.network_request_title_text);
title.setText(getTitle());
+ final Intent intent = getActivity().getIntent();
+ if (intent != null) {
+ mIsSpecifiedSsid = intent.getBooleanExtra(EXTRA_IS_SPECIFIED_SSID, false);
+ }
+
final ProgressBar progressBar = customTitle.findViewById(
R.id.network_request_title_progress);
progressBar.setVisibility(View.VISIBLE);
@@ -115,10 +124,13 @@
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setCustomTitle(customTitle)
.setAdapter(mDialogAdapter, this)
- .setPositiveButton(R.string.cancel, (dialog, which) -> getActivity().finish())
+ .setNegativeButton(R.string.cancel, (dialog, which) -> onCancel(dialog))
// Do nothings, will replace the onClickListener to avoid auto closing dialog.
.setNeutralButton(R.string.network_connection_request_dialog_showall,
null /* OnClickListener */);
+ if (mIsSpecifiedSsid) {
+ builder.setPositiveButton(R.string.wifi_connect, null /* OnClickListener */);
+ }
// Clicking list item is to connect wifi ap.
final AlertDialog dialog = builder.create();
@@ -126,6 +138,10 @@
.setOnItemClickListener(
(parent, view, position, id) -> this.onClick(dialog, position));
+ // Don't dismiss dialog when touching outside. User report it is easy to touch outside.
+ // This causes dialog to close. Which is concerned as a bad UX (b/128877712).
+ dialog.setCanceledOnTouchOutside(false);
+
dialog.setOnShowListener((dialogInterface) -> {
// Replace NeutralButton onClickListener to avoid closing dialog
final Button neutralBtn = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
@@ -136,8 +152,19 @@
notifyAdapterRefresh();
neutralBtn.setVisibility(View.GONE);
});
- });
+ // Replace Positive onClickListener to avoid closing dialog
+ if (mIsSpecifiedSsid) {
+ final Button positiveBtn = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ positiveBtn.setOnClickListener(v -> {
+ // When clicking connect button, should connect to the first and the only one
+ // list item.
+ this.onClick(dialog, 0 /* position */);
+ });
+ // Disable button in first, and enable it after there are some accesspoints in list.
+ positiveBtn.setEnabled(false);
+ }
+ });
return dialog;
}
@@ -184,6 +211,9 @@
if (wifiConfig != null) {
mUserSelectionCallback.select(wifiConfig);
+
+ mWaitingConnectCallback = true;
+ updateConnectButton(false);
}
}
}
@@ -195,6 +225,9 @@
if (getActivity() != null) {
getActivity().finish();
}
+ if (mUserSelectionCallback != null) {
+ mUserSelectionCallback.reject();
+ }
}
@Override
@@ -223,7 +256,7 @@
}
}
- private void showNeutralButton() {
+ private void showAllButton() {
final AlertDialog alertDialog = (AlertDialog) getDialog();
if (alertDialog == null) {
return;
@@ -235,6 +268,35 @@
}
}
+ private void updateConnectButton(boolean enabled) {
+ // The button is only showed in single SSID mode.
+ if (!mIsSpecifiedSsid) {
+ return;
+ }
+
+ final AlertDialog alertDialog = (AlertDialog) getDialog();
+ if (alertDialog == null) {
+ return;
+ }
+
+ final Button positiveBtn = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ if (positiveBtn != null) {
+ positiveBtn.setEnabled(enabled);
+ }
+ }
+
+ private void hideProgressIcon() {
+ final AlertDialog alertDialog = (AlertDialog) getDialog();
+ if (alertDialog == null) {
+ return;
+ }
+
+ final View progress = alertDialog.findViewById(R.id.network_request_title_progress);
+ if (progress != null) {
+ progress.setVisibility(View.GONE);
+ }
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -403,7 +465,9 @@
@Override
public void onUserSelectionConnectFailure(WifiConfiguration wificonfiguration) {
- stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE.ABORT);
+ // Do nothing when selection is failed, let user could try again easily.
+ mWaitingConnectCallback = false;
+ updateConnectButton(true);
}
private final class FilterWifiTracker {
@@ -427,10 +491,6 @@
mAccessPointKeys.add(key);
}
}
-
- if (mShowLimitedItem && (mAccessPointKeys.size() > MAX_NUMBER_LIST_ITEM)) {
- showNeutralButton();
- }
}
/**
@@ -457,6 +517,21 @@
}
}
+ // Update related UI buttons
+ if (mShowLimitedItem && (count >= MAX_NUMBER_LIST_ITEM)) {
+ showAllButton();
+ }
+ if (count > 0) {
+ hideProgressIcon();
+ }
+ // Enable connect button if there is Accesspoint item, except for the situation that
+ // user click but connected status doesn't come back yet.
+ if (count < 0) {
+ updateConnectButton(false);
+ } else if (!mWaitingConnectCallback) {
+ updateConnectButton(true);
+ }
+
return result;
}
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 665e253..0adbd8d 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -249,7 +249,7 @@
mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
mPrivacySettingsSpinner = mView.findViewById(R.id.privacy_settings);
if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_p2p_mac_randomization_supported)) {
+ com.android.internal.R.bool.config_wifi_connected_mac_randomization_supported)) {
View privacySettingsLayout = mView.findViewById(R.id.privacy_settings_fields);
privacySettingsLayout.setVisibility(View.VISIBLE);
}
diff --git a/src/com/android/settings/wifi/WifiNoInternetDialog.java b/src/com/android/settings/wifi/WifiNoInternetDialog.java
index e12529d..e33cab6 100644
--- a/src/com/android/settings/wifi/WifiNoInternetDialog.java
+++ b/src/com/android/settings/wifi/WifiNoInternetDialog.java
@@ -17,6 +17,7 @@
package com.android.settings.wifi;
import static android.net.ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
+import static android.net.ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
import static android.net.ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -53,8 +54,9 @@
private String mAction;
private boolean isKnownAction(Intent intent) {
- return intent.getAction().equals(ACTION_PROMPT_UNVALIDATED) ||
- intent.getAction().equals(ACTION_PROMPT_LOST_VALIDATION);
+ return intent.getAction().equals(ACTION_PROMPT_UNVALIDATED)
+ || intent.getAction().equals(ACTION_PROMPT_LOST_VALIDATION)
+ || intent.getAction().equals(ACTION_PROMPT_PARTIAL_CONNECTIVITY);
}
@Override
@@ -131,6 +133,11 @@
ap.mMessage = getString(R.string.no_internet_access_text);
ap.mPositiveButtonText = getString(R.string.yes);
ap.mNegativeButtonText = getString(R.string.no);
+ } else if (ACTION_PROMPT_PARTIAL_CONNECTIVITY.equals(mAction)) {
+ ap.mTitle = mNetworkName;
+ ap.mMessage = getString(R.string.partial_connectivity_text);
+ ap.mPositiveButtonText = getString(R.string.yes);
+ ap.mNegativeButtonText = getString(R.string.no);
} else {
ap.mTitle = getString(R.string.lost_internet_access_title);
ap.mMessage = getString(R.string.lost_internet_access_text);
@@ -146,7 +153,8 @@
ap.mView = checkbox;
mAlwaysAllow = (CheckBox) checkbox.findViewById(com.android.internal.R.id.alwaysUse);
- if (ACTION_PROMPT_UNVALIDATED.equals(mAction)) {
+ if (ACTION_PROMPT_UNVALIDATED.equals(mAction)
+ || ACTION_PROMPT_PARTIAL_CONNECTIVITY.equals(mAction)) {
mAlwaysAllow.setText(getString(R.string.no_internet_access_remember));
} else {
mAlwaysAllow.setText(getString(R.string.lost_internet_access_persist));
@@ -174,6 +182,11 @@
final boolean accept = (which == BUTTON_POSITIVE);
action = (accept ? "Connect" : "Ignore");
mCM.setAcceptUnvalidated(mNetwork, accept, always);
+ } else if (ACTION_PROMPT_PARTIAL_CONNECTIVITY.equals(mAction)) {
+ what = "PARTIAL_CONNECTIVITY";
+ final boolean accept = (which == BUTTON_POSITIVE);
+ action = (accept ? "Connect" : "Ignore");
+ mCM.setAcceptPartialConnectivity(mNetwork, accept, always);
} else {
what = "LOST_INTERNET";
final boolean avoid = (which == BUTTON_POSITIVE);
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index d6ddff6..585cf71 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -67,6 +67,7 @@
import com.android.settings.widget.SwitchBarController;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.search.SearchIndexable;
@@ -178,7 +179,8 @@
private PreferenceCategory mConnectedAccessPointPreferenceCategory;
private PreferenceCategory mAccessPointsPreferenceCategory;
- private ButtonPreference mAddPreference;
+ @VisibleForTesting
+ AddWifiNetworkPreference mAddWifiNetworkPreference;
@VisibleForTesting
Preference mConfigureWifiSettingsPreference;
@VisibleForTesting
@@ -236,19 +238,8 @@
(PreferenceCategory) findPreference(PREF_KEY_ACCESS_POINTS);
mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_WIFI_SETTINGS);
mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS);
-
- Context prefContext = getPrefContext();
- mAddPreference = new ButtonPreference(prefContext);
- mAddPreference.setIcon(R.drawable.ic_menu_add);
- mAddPreference.setTitle(R.string.wifi_add_network);
- mAddPreference.setButtonIcon(R.drawable.ic_scan_24dp);
- mAddPreference.setButtonOnClickListener((View v) -> {
- // Launch QR code scanner to join a network.
- getContext().startActivity(
- WifiDppUtils.getEnrolleeQrCodeScannerIntent(/* ssid */ null));
- });
+ mAddWifiNetworkPreference = new AddWifiNetworkPreference(getPrefContext());
mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE);
-
mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());
}
@@ -588,7 +579,7 @@
showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
break;
}
- } else if (preference == mAddPreference) {
+ } else if (preference == mAddWifiNetworkPreference) {
onAddNetworkPressed();
} else {
return super.onPreferenceTreeClick(preference);
@@ -807,8 +798,8 @@
}
}
removeCachedPrefs(mAccessPointsPreferenceCategory);
- mAddPreference.setOrder(index);
- mAccessPointsPreferenceCategory.addPreference(mAddPreference);
+ mAddWifiNetworkPreference.setOrder(index);
+ mAccessPointsPreferenceCategory.addPreference(mAddWifiNetworkPreference);
setAdditionalSettingsSummaries();
if (!hasAvailableAccessPoints) {
@@ -967,8 +958,13 @@
}
private void launchNetworkDetailsFragment(ConnectedAccessPointPreference pref) {
+ final AccessPoint accessPoint = pref.getAccessPoint();
+ final Context context = getContext();
+ final CharSequence title = SavedAccessPointsWifiSettings.usingDetailsFragment(context) ?
+ accessPoint.getTitle() : context.getText(R.string.pref_title_network_details);
+
new SubSettingLauncher(getContext())
- .setTitleRes(R.string.pref_title_network_details)
+ .setTitleText(title)
.setDestination(WifiNetworkDetailsFragment.class.getName())
.setArguments(pref.getExtras())
.setSourceMetricsCategory(getMetricsCategory())
diff --git a/src/com/android/settings/wifi/calling/DisclaimerItem.java b/src/com/android/settings/wifi/calling/DisclaimerItem.java
new file mode 100644
index 0000000..6fd8b70
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/DisclaimerItem.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Interface to control disclaimer item from {@link WifiCallingDisclaimerFragment}.
+ */
+@VisibleForTesting
+public abstract class DisclaimerItem {
+ private static final String SHARED_PREFERENCES_NAME = "wfc_disclaimer_prefs";
+
+ protected final Context mContext;
+ protected final int mSubId;
+ private final CarrierConfigManager mCarrierConfigManager;
+
+ DisclaimerItem(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ }
+
+ /**
+ * Called by the {@link WifiCallingDisclaimerFragment} when a user has clicked the agree button.
+ */
+ void onAgreed() {
+ setBooleanSharedPrefs(getPrefKey(), true);
+ }
+
+ /**
+ * Checks whether the disclaimer item need to be displayed or not.
+ *
+ * @return Returns {@code true} if disclaimer item need to be displayed,
+ * {@code false} if not displayed.
+ */
+ boolean shouldShow() {
+ if (getBooleanSharedPrefs(getPrefKey(), false)) {
+ logd("shouldShow: false due to a user has already agreed.");
+ return false;
+ }
+ logd("shouldShow: true");
+ return true;
+ }
+
+ /**
+ * Gets the configuration values for a particular sub id.
+ *
+ * @return The {@link PersistableBundle} instance containing the config value for a
+ * particular phone id, or default values.
+ */
+ protected PersistableBundle getCarrierConfig() {
+ PersistableBundle config = mCarrierConfigManager.getConfigForSubId(mSubId);
+ if (config != null) {
+ return config;
+ }
+ // Return static default defined in CarrierConfigManager.
+ return CarrierConfigManager.getDefaultConfig();
+ }
+
+ protected void logd(String msg) {
+ Log.d(getName(), "[" + mSubId + "] " + msg);
+ }
+
+ /**
+ * Gets a title id for disclaimer item.
+ *
+ * @return Title id for disclaimer item.
+ */
+ protected abstract int getTitleId();
+
+ /**
+ * Gets a message id for disclaimer item.
+ *
+ * @return Message id for disclaimer item.
+ */
+ protected abstract int getMessageId();
+
+ /**
+ * Gets a name of disclaimer item.
+ *
+ * @return Name of disclaimer item.
+ */
+ protected abstract String getName();
+
+ /**
+ * Gets a preference key to keep user's consent.
+ *
+ * @return Preference key to keep user's consent.
+ */
+ protected abstract String getPrefKey();
+
+ /**
+ * Gets the boolean value from shared preferences.
+ *
+ * @param key The key for the preference item.
+ * @param defValue Value to return if this preference does not exist.
+ * @return The boolean value of corresponding key, or defValue.
+ */
+ private boolean getBooleanSharedPrefs(String key, boolean defValue) {
+ SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
+ Context.MODE_PRIVATE);
+ return prefs.getBoolean(key + mSubId, defValue);
+ }
+
+ /**
+ * Sets the boolean value to shared preferences.
+ *
+ * @param key The key for the preference item.
+ * @param value The value to be set for shared preferences.
+ */
+ private void setBooleanSharedPrefs(String key, boolean value) {
+ SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
+ Context.MODE_PRIVATE);
+ prefs.edit().putBoolean(key + mSubId, value).apply();
+ }
+}
diff --git a/src/com/android/settings/wifi/calling/DisclaimerItemFactory.java b/src/com/android/settings/wifi/calling/DisclaimerItemFactory.java
new file mode 100644
index 0000000..6d8dc8f
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/DisclaimerItemFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Factory class to create disclaimer items list.
+ */
+@VisibleForTesting
+public final class DisclaimerItemFactory {
+
+ /**
+ * Creates disclaimer items list.
+ *
+ * @param context The application context
+ * @param subId The subscription id.
+ * @return The {@link DisclaimerItem} list instance, if there are no items, return empty list.
+ */
+ public static List<DisclaimerItem> create(Context context, int subId) {
+ List<DisclaimerItem> itemList = getDisclaimerItemList(context, subId);
+ Iterator itr = itemList.iterator();
+ while (itr.hasNext()) {
+ DisclaimerItem item = (DisclaimerItem) itr.next();
+ if (!item.shouldShow()) {
+ itr.remove();
+ }
+ }
+ return itemList;
+ }
+
+ private static List<DisclaimerItem> getDisclaimerItemList(Context context, int subId) {
+ List<DisclaimerItem> itemList = new ArrayList<DisclaimerItem>();
+
+ return itemList;
+ }
+}
diff --git a/src/com/android/settings/wifi/calling/DisclaimerItemListAdapter.java b/src/com/android/settings/wifi/calling/DisclaimerItemListAdapter.java
new file mode 100644
index 0000000..4b5d19c
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/DisclaimerItemListAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+
+import java.util.List;
+
+/**
+ * Adapter for disclaimer items list.
+ */
+public class DisclaimerItemListAdapter extends
+ RecyclerView.Adapter<DisclaimerItemListAdapter.DisclaimerItemViewHolder> {
+
+ private List<DisclaimerItem> mDisclaimerItemList;
+
+ public DisclaimerItemListAdapter(List<DisclaimerItem> list) {
+ mDisclaimerItemList = list;
+ }
+
+ @Override
+ public DisclaimerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.wfc_simple_disclaimer_item, null, false);
+ return new DisclaimerItemViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(DisclaimerItemViewHolder holder, int position) {
+ holder.titleView.setText(mDisclaimerItemList.get(position).getTitleId());
+ holder.descriptionView.setText(mDisclaimerItemList.get(position).getMessageId());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDisclaimerItemList.size();
+ }
+
+ public static class DisclaimerItemViewHolder extends RecyclerView.ViewHolder {
+ @VisibleForTesting
+ static final int ID_DISCLAIMER_ITEM_TITLE = R.id.disclaimer_title;
+ @VisibleForTesting
+ static final int ID_DISCLAIMER_ITEM_DESCRIPTION = R.id.disclaimer_desc;
+
+ public final TextView titleView;
+ public final TextView descriptionView;
+
+ public DisclaimerItemViewHolder(View itemView) {
+ super(itemView);
+ titleView = itemView.findViewById(ID_DISCLAIMER_ITEM_TITLE);
+ descriptionView = itemView.findViewById(ID_DISCLAIMER_ITEM_DESCRIPTION);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java b/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java
new file mode 100644
index 0000000..a44fcbe
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.RadioButton;
+import android.widget.TextView;
+import androidx.appcompat.app.AlertDialog.Builder;
+import com.android.settings.CustomListPreference;
+import com.android.settings.R;
+
+/**
+ * ListPreference contain the entry summary.
+ */
+public class ListWithEntrySummaryPreference extends CustomListPreference {
+ private static final String LOG_TAG = "ListWithEntrySummaryPreference";
+ private final Context mContext;
+ private CharSequence[] mSummaries;
+
+ /**
+ * ListWithEntrySummaryPreference constructor.
+ *
+ * @param context The context of view.
+ * @param attrs The attributes of the XML tag that is inflating the linear layout.
+ */
+ public ListWithEntrySummaryPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ TypedArray array = context.obtainStyledAttributes(attrs,
+ R.styleable.ListWithEntrySummaryPreference, 0, 0);
+ mSummaries = array.getTextArray(R.styleable.ListWithEntrySummaryPreference_entrySummaries);
+ array.recycle();
+ }
+
+ /**
+ * Sets the summaries of mode items to be shown in the mode select dialog.
+ *
+ * @param summariesResId The summaries of mode items.
+ */
+ public void setEntrySummaries(int summariesResId) {
+ mSummaries = getContext().getResources().getTextArray(summariesResId);
+ }
+
+ /**
+ * Sets the summaries of mode items to be shown in the mode select dialog.
+ *
+ * @param summaries The summaries of mode items.
+ */
+ public void setEntrySummaries(CharSequence[] summaries) {
+ mSummaries = summaries;
+ }
+
+ private CharSequence getEntrySummary(int index) {
+ if (mSummaries == null) {
+ Log.w(LOG_TAG, "getEntrySummary : mSummaries is null");
+ return "";
+ }
+ return mSummaries[index];
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(Builder builder,
+ DialogInterface.OnClickListener listener) {
+ ListAdapter la = (ListAdapter) new SelectorAdapter(mContext,
+ R.xml.single_choice_list_item_2, this);
+ builder.setSingleChoiceItems(la, findIndexOfValue(getValue()), listener);
+ super.onPrepareDialogBuilder(builder, listener);
+ }
+
+ private static class SelectorAdapter extends ArrayAdapter<CharSequence> {
+ private final Context mContext;
+ private ListWithEntrySummaryPreference mSelector;
+
+ /**
+ * SelectorAdapter constructor.
+ *
+ * @param context The current context.
+ * @param rowResourceId The resource id of the XML tag that is inflating the linear layout.
+ * @param listPreference The instance of ListWithEntrySummaryPreference.
+ */
+ public SelectorAdapter(Context context, int rowResourceId,
+ ListWithEntrySummaryPreference listPreference) {
+ super(context, rowResourceId, listPreference.getEntryValues());
+ mContext = context;
+ mSelector = listPreference;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View row = inflater.inflate(R.xml.single_choice_list_item_2, parent, false);
+
+ TextView title = (TextView) row.findViewById(R.id.title);
+ title.setText(mSelector.getEntries()[position]);
+
+ TextView summary = (TextView) row.findViewById(R.id.summary);
+ summary.setText(mSelector.getEntrySummary(position));
+
+ RadioButton rb = (RadioButton) row.findViewById(R.id.radio);
+ if (position == mSelector.findIndexOfValue(mSelector.getValue())) {
+ rb.setChecked(true);
+ }
+
+ return row;
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final Parcelable superState = super.onSaveInstanceState();
+
+ final SavedState myState = new SavedState(superState);
+ myState.mEntries = getEntries();
+ myState.mEntryValues = getEntryValues();
+ myState.mSummaries = mSummaries;
+ return myState;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !state.getClass().equals(SavedState.class)) {
+ // Didn't save state for us in onSaveInstanceState
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ SavedState myState = (SavedState) state;
+ super.onRestoreInstanceState(myState.getSuperState());
+ setEntries(myState.mEntries);
+ setEntryValues(myState.mEntryValues);
+ mSummaries = myState.mSummaries;
+ }
+
+ /**
+ * We save entries, entryValues and summaries into bundle.
+ * At onCreate of fragment, dialog will be restored if it was open. In this case,
+ * we need to restore entries, entryValues and summaries. Without those information,
+ * crash when entering multi window during wfc modes dialog shown.
+ */
+ private static class SavedState extends BaseSavedState {
+ private CharSequence[] mEntries;
+ private CharSequence[] mEntryValues;
+ private CharSequence[] mSummaries;
+
+ public SavedState(Parcel source) {
+ super(source);
+ mEntries = source.readCharSequenceArray();
+ mEntryValues = source.readCharSequenceArray();
+ mSummaries = source.readCharSequenceArray();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeCharSequenceArray(mEntries);
+ dest.writeCharSequenceArray(mEntryValues);
+ dest.writeCharSequenceArray(mSummaries);
+ }
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/src/com/android/settings/wifi/calling/WifiCallingDisclaimerFragment.java b/src/com/android/settings/wifi/calling/WifiCallingDisclaimerFragment.java
new file mode 100644
index 0000000..7763226
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/WifiCallingDisclaimerFragment.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.telephony.SubscriptionManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.core.InstrumentedFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment for displaying disclaimers for WFC.
+ */
+public class WifiCallingDisclaimerFragment extends InstrumentedFragment
+ implements View.OnClickListener {
+ private static final String TAG = "WifiCallingDisclaimerFragment";
+
+ private static final String STATE_IS_SCROLL_TO_BOTTOM = "state_is_scroll_to_bottom";
+
+ private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<DisclaimerItem>();
+ private Button mAgreeButton;
+ private Button mDisagreeButton;
+ private boolean mScrollToBottom;
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.WIFI_CALLING;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Bundle args = getArguments();
+ final int subId = (args != null) ? args.getInt(WifiCallingSettingsForSub.EXTRA_SUB_ID)
+ : SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+
+ mDisclaimerItemList = DisclaimerItemFactory.create(getActivity(), subId);
+ if (mDisclaimerItemList.isEmpty()) {
+ finish(Activity.RESULT_OK);
+ return;
+ }
+
+ if (savedInstanceState != null) {
+ mScrollToBottom = savedInstanceState.getBoolean(
+ STATE_IS_SCROLL_TO_BOTTOM, mScrollToBottom);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ final View view = inflater.inflate(R.layout.wfc_disclaimer_fragment, container, false);
+
+ mAgreeButton = view.findViewById(R.id.agree_button);
+ mAgreeButton.setOnClickListener(this);
+ mDisagreeButton = view.findViewById(R.id.disagree_button);
+ mDisagreeButton.setOnClickListener(this);
+
+ final RecyclerView recyclerView = (RecyclerView) view.findViewById(
+ R.id.disclaimer_item_list);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ recyclerView.setAdapter(new DisclaimerItemListAdapter(mDisclaimerItemList));
+
+ RecyclerView.ItemDecoration itemDecoration =
+ new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL);
+ recyclerView.addItemDecoration(itemDecoration);
+
+ recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ if (!recyclerView.canScrollVertically(1 /* scrolling down */)) {
+ mScrollToBottom = true;
+ updateButtonState();
+ recyclerView.removeOnScrollListener(this);
+ }
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateButtonState();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(STATE_IS_SCROLL_TO_BOTTOM, mScrollToBottom);
+ }
+
+ private void updateButtonState() {
+ mAgreeButton.setEnabled(mScrollToBottom);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mAgreeButton) {
+ for (DisclaimerItem item : mDisclaimerItemList) {
+ item.onAgreed();
+ }
+ finish(Activity.RESULT_OK);
+ } else if (v == mDisagreeButton) {
+ finish(Activity.RESULT_CANCELED);
+ }
+ }
+
+ @VisibleForTesting
+ void finish(int result) {
+ Activity activity = getActivity();
+ activity.setResult(result, null);
+ activity.finish();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index 6fe1795..525b59b 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -40,7 +40,6 @@
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
-import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceScreen;
@@ -54,6 +53,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
import com.android.settings.widget.SwitchBar;
/**
@@ -70,9 +70,13 @@
private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
- private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
+ @VisibleForTesting
+ static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
+ @VisibleForTesting
+ static final int REQUEST_CHECK_WFC_DISCLAIMER = 2;
public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
+ public static final String EXTRA_SUB_ID = "EXTRA_SUB_ID";
protected static final String FRAGMENT_BUNDLE_SUBID = "subId";
@@ -82,8 +86,8 @@
//UI objects
private SwitchBar mSwitchBar;
private Switch mSwitch;
- private ListPreference mButtonWfcMode;
- private ListPreference mButtonWfcRoamingMode;
+ private ListWithEntrySummaryPreference mButtonWfcMode;
+ private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
private Preference mUpdateAddress;
private TextView mEmptyView;
@@ -141,20 +145,17 @@
}
};
+ /*
+ * Launch carrier emergency address managemnent activity
+ */
private final OnPreferenceClickListener mUpdateAddressListener =
- new OnPreferenceClickListener() {
- /*
- * Launch carrier emergency address managemnent activity
- */
- @Override
- public boolean onPreferenceClick(Preference preference) {
- Intent carrierAppIntent = getCarrierActivityIntent();
- if (carrierAppIntent != null) {
- carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE);
- startActivity(carrierAppIntent);
- }
- return true;
+ preference -> {
+ Intent carrierAppIntent = getCarrierActivityIntent();
+ if (carrierAppIntent != null) {
+ carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE);
+ startActivity(carrierAppIntent);
}
+ return true;
};
private final ProvisioningManager.Callback mProvisioningCallback =
@@ -174,11 +175,9 @@
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- final SettingsActivity activity = (SettingsActivity) getActivity();
-
mEmptyView = getView().findViewById(android.R.id.empty);
setEmptyView(mEmptyView);
- final Resources res = SubscriptionManager.getResourcesForSubId(getContext(), mSubId);
+ final Resources res = getResourcesForSubId();
String emptyViewText = res.getString(R.string.wifi_calling_off_explanation,
res.getString(R.string.wifi_calling_off_explanation_2));
mEmptyView.setText(emptyViewText);
@@ -265,13 +264,13 @@
mTelephonyManager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
.createForSubscriptionId(mSubId);
- mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE);
+ mButtonWfcMode = findPreference(BUTTON_WFC_MODE);
mButtonWfcMode.setOnPreferenceChangeListener(this);
- mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE);
+ mButtonWfcRoamingMode = findPreference(BUTTON_WFC_ROAMING_MODE);
mButtonWfcRoamingMode.setOnPreferenceChangeListener(this);
- mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS);
+ mUpdateAddress = findPreference(PREFERENCE_EMERGENCY_ADDRESS);
mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener);
mIntentFilter = new IntentFilter();
@@ -329,10 +328,14 @@
if (!isWifiOnlySupported) {
mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only);
mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only);
+ mButtonWfcMode.setEntrySummaries(R.array.wifi_calling_mode_summaries_without_wifi_only);
+
mButtonWfcRoamingMode.setEntries(
R.array.wifi_calling_mode_choices_v2_without_wifi_only);
mButtonWfcRoamingMode.setEntryValues(
R.array.wifi_calling_mode_values_without_wifi_only);
+ mButtonWfcRoamingMode.setEntrySummaries(
+ R.array.wifi_calling_mode_summaries_without_wifi_only);
}
@@ -418,14 +421,17 @@
return;
}
- // Call address management activity before turning on WFC
- Intent carrierAppIntent = getCarrierActivityIntent();
- if (carrierAppIntent != null) {
- carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
- startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
- } else {
- updateWfcMode(true);
- }
+ // Launch disclaimer fragment before turning on WFC
+ final Context context = getActivity();
+ final Bundle args = new Bundle();
+ args.putInt(EXTRA_SUB_ID, mSubId);
+ new SubSettingLauncher(context)
+ .setDestination(WifiCallingDisclaimerFragment.class.getName())
+ .setArguments(args)
+ .setTitleRes(R.string.wifi_calling_settings_title)
+ .setSourceMetricsCategory(getMetricsCategory())
+ .setResultListener(this, REQUEST_CHECK_WFC_DISCLAIMER)
+ .launch();
}
/*
@@ -478,12 +484,30 @@
final Context context = getActivity();
- if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) {
- Log.d(TAG, "WFC emergency address activity result = " + resultCode);
+ Log.d(TAG, "WFC activity request = " + requestCode + " result = " + resultCode);
- if (resultCode == Activity.RESULT_OK) {
- updateWfcMode(true);
- }
+ switch (requestCode) {
+ case REQUEST_CHECK_WFC_EMERGENCY_ADDRESS:
+ if (resultCode == Activity.RESULT_OK) {
+ updateWfcMode(true);
+ }
+ break;
+ case REQUEST_CHECK_WFC_DISCLAIMER:
+ if (resultCode == Activity.RESULT_OK) {
+ // Call address management activity before turning on WFC
+ Intent carrierAppIntent = getCarrierActivityIntent();
+ if (carrierAppIntent != null) {
+ carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
+ startActivityForResult(carrierAppIntent,
+ REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
+ } else {
+ updateWfcMode(true);
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unexpected request: " + requestCode);
+ break;
}
}
@@ -570,4 +594,9 @@
}
return resId;
}
+
+ @VisibleForTesting
+ Resources getResourcesForSubId() {
+ return SubscriptionManager.getResourcesForSubId(getContext(), mSubId, false);
+ }
}
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index 7aa2fac..cf59dbd 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -16,21 +16,18 @@
package com.android.settings.wifi.details;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.KeyguardManager;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.CancellationSignal;
-import android.os.Looper;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkAddress;
@@ -46,6 +43,7 @@
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.widget.ImageView;
import android.widget.Toast;
@@ -55,18 +53,21 @@
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.datausage.WifiDataUsageSummaryPreferenceController;
import com.android.settings.development.featureflags.FeatureFlagPersistent;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.wifi.WifiDialog;
import com.android.settings.wifi.WifiDialog.WifiDialogListener;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -76,6 +77,8 @@
import com.android.settingslib.widget.ActionButtonsPreference;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -137,7 +140,9 @@
private WifiConfiguration mWifiConfig;
private WifiInfo mWifiInfo;
private final WifiManager mWifiManager;
+ private final WifiTracker mWifiTracker;
private final MetricsFeatureProvider mMetricsFeatureProvider;
+ private boolean mIsOutOfRange;
// UI elements - in order of appearance
private ActionButtonsPreference mButtonsPref;
@@ -154,6 +159,9 @@
private Preference mDnsPref;
private PreferenceCategory mIpv6Category;
private Preference mIpv6AddressPref;
+ private Lifecycle mLifecycle;
+ Preference mDataUsageSummaryPref;
+ WifiDataUsageSummaryPreferenceController mSummaryHeaderController;
private final IconInjector mIconInjector;
private final IntentFilter mFilter;
@@ -174,7 +182,7 @@
// fall through
case WifiManager.NETWORK_STATE_CHANGED_ACTION:
case WifiManager.RSSI_CHANGED_ACTION:
- updateLiveNetworkInfo();
+ updateNetworkInfo();
break;
}
}
@@ -203,15 +211,19 @@
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
- // If the network just validated or lost Internet access, refresh network state.
- // Don't do this on every NetworkCapabilities change because refreshNetworkState
- // sends IPCs to the system server from the UI thread, which can cause jank.
+ // If the network just validated or lost Internet access or detected partial internet
+ // connectivity, refresh network state. Don't do this on every NetworkCapabilities
+ // change because refreshNetworkState sends IPCs to the system server from the UI
+ // thread, which can cause jank.
if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
- if (hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED) ||
- hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)) {
- refreshNetworkState();
+ if (hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
+ mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ refreshEntityHeader();
}
mNetworkCapabilities = nc;
+ refreshButtons();
updateIpLayerInfo();
}
}
@@ -224,6 +236,29 @@
}
};
+ private final WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() {
+ /** Called when the state of Wifi has changed. */
+ public void onWifiStateChanged(int state) {
+ Log.d(TAG, "onWifiStateChanged(" + state + ")");
+ // Do nothing.
+ }
+
+ /** Called when the connection state of wifi has changed. */
+ public void onConnectedChanged() {
+ Log.d(TAG, "onConnectedChanged");
+ // Do nothing.
+ }
+
+ /**
+ * Called to indicate the list of AccessPoints has been updated and
+ * {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
+ */
+ public void onAccessPointsChanged() {
+ Log.d(TAG, "onAccessPointsChanged");
+ updateNetworkInfo();
+ }
+ };
+
public static WifiDetailPreferenceController newInstance(
AccessPoint accessPoint,
ConnectivityManager connectivityManager,
@@ -266,7 +301,19 @@
mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+ mLifecycle = lifecycle;
lifecycle.addObserver(this);
+
+ if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) {
+ mWifiTracker = WifiTrackerFactory.create(
+ mFragment.getActivity(),
+ mWifiListener,
+ mLifecycle,
+ true /*includeSaved*/,
+ true /*includeScans*/);
+ } else {
+ mWifiTracker = null;
+ }
}
@Override
@@ -317,6 +364,17 @@
private void setupEntityHeader(PreferenceScreen screen) {
LayoutPreference headerPref = screen.findPreference(KEY_HEADER);
+
+ if (usingDataUsageHeader(mContext)) {
+ headerPref.setVisible(false);
+ mDataUsageSummaryPref = screen.findPreference("status_header");
+ mDataUsageSummaryPref.setVisible(true);
+ mSummaryHeaderController =
+ new WifiDataUsageSummaryPreferenceController(mFragment.getActivity(),
+ mLifecycle, (PreferenceFragmentCompat) mFragment, mAccessPoint.getSsid());
+ return;
+ }
+
mEntityHeaderController =
EntityHeaderController.newInstance(
mFragment.getActivity(), mFragment,
@@ -330,6 +388,15 @@
mEntityHeaderController.setLabel(mAccessPoint.getTitle());
}
+ private void refreshEntityHeader() {
+ if (usingDataUsageHeader(mContext)) {
+ mSummaryHeaderController.updateState(mDataUsageSummaryPref);
+ } else {
+ mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
+ .done(mFragment.getActivity(), true /* rebind */);
+ }
+ }
+
@Override
public void onResume() {
// Ensure mNetwork is set before any callbacks above are delivered, since our
@@ -337,7 +404,7 @@
mNetwork = mWifiManager.getCurrentNetwork();
mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
- updateLiveNetworkInfo();
+ updateNetworkInfo();
mContext.registerReceiver(mReceiver, mFilter);
mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
mHandler);
@@ -354,60 +421,118 @@
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
}
- // TODO(b/124707751): Refactoring the code later, keeping it currently for stability.
- protected void updateSavedNetworkInfo() {
- mSignalStrengthPref.setVisible(false);
- mFrequencyPref.setVisible(false);
- mTxLinkSpeedPref.setVisible(false);
- mRxLinkSpeedPref.setVisible(false);
-
- // MAC Address Pref
- mMacAddressPref.setSummary(mWifiConfig.getRandomizedMacAddress().toString());
-
- // TODO(b/124700353): Change header to data usage chart
- mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
- .done(mFragment.getActivity(), true /* rebind */);
-
- updateIpLayerInfo();
-
- // Update whether the forget button should be displayed.
- mButtonsPref.setButton1Visible(canForgetNetwork());
- }
-
- private void updateLiveNetworkInfo() {
- // No need to fetch LinkProperties and NetworkCapabilities, they are updated by the
- // callbacks. mNetwork doesn't change except in onResume.
- mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
- mWifiInfo = mWifiManager.getConnectionInfo();
- if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
- exitActivity();
+ private void updateNetworkInfo() {
+ if(!updateAccessPoint()) {
return;
}
- // Update whether the forget button should be displayed.
- mButtonsPref.setButton1Visible(canForgetNetwork());
+ // refresh header
+ refreshEntityHeader();
- refreshNetworkState();
+ // refresh Buttons
+ refreshButtons();
// Update Connection Header icon and Signal Strength Preference
refreshRssiViews();
-
- // MAC Address Pref
- mMacAddressPref.setSummary(mWifiInfo.getMacAddress());
-
- // Transmit Link Speed Pref
- int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps();
- mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0);
- mTxLinkSpeedPref.setSummary(mContext.getString(
- R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps()));
-
- // Receive Link Speed Pref
- int rxLinkSpeedMbps = mWifiInfo.getRxLinkSpeedMbps();
- mRxLinkSpeedPref.setVisible(rxLinkSpeedMbps >= 0);
- mRxLinkSpeedPref.setSummary(mContext.getString(
- R.string.rx_link_speed, mWifiInfo.getRxLinkSpeedMbps()));
-
// Frequency Pref
+ refreshFrequency();
+ // Transmit Link Speed Pref
+ refreshTxSpeed();
+ // Receive Link Speed Pref
+ refreshRxSpeed();
+ // IP related information
+ updateIpLayerInfo();
+ // MAC Address Pref
+ refreshMacAddress();
+
+ }
+
+ private boolean updateAccessPoint() {
+ boolean changed = false;
+ if (mWifiTracker != null) {
+ updateAccessPointFromScannedList();
+ // refresh UI if signal level changed for disconnect network.
+ changed = mRssiSignalLevel != mAccessPoint.getLevel();
+ }
+
+ if (mAccessPoint.isActive()) {
+ // No need to fetch LinkProperties and NetworkCapabilities, they are updated by the
+ // callbacks. mNetwork doesn't change except in onResume.
+ mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
+ mWifiInfo = mWifiManager.getConnectionInfo();
+ if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
+ exitActivity();
+ return false;
+ }
+
+ changed |= mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ // If feature for saved network not enabled, always return true.
+ return mWifiTracker == null || changed;
+ }
+
+ return changed;
+ }
+
+ private void updateAccessPointFromScannedList() {
+ mIsOutOfRange = true;
+
+ if (mAccessPoint.getConfig() == null) {
+ return;
+ }
+
+ for (AccessPoint ap : mWifiTracker.getAccessPoints()) {
+ if (ap.getConfig() != null
+ && mAccessPoint.matches(ap.getConfig())) {
+ mAccessPoint = ap;
+ mIsOutOfRange = false;
+ return;
+ }
+ }
+ }
+
+ private void exitActivity() {
+ if (DEBUG) {
+ Log.d(TAG, "Exiting the WifiNetworkDetailsPage");
+ }
+ mFragment.getActivity().finish();
+ }
+
+ private void refreshRssiViews() {
+ int signalLevel = mAccessPoint.getLevel();
+
+ // Disappears signal view if not in range. e.g. for saved networks.
+ if (mIsOutOfRange) {
+ mSignalStrengthPref.setVisible(false);
+ mRssiSignalLevel = -1;
+ return;
+ }
+
+ if (mRssiSignalLevel == signalLevel) {
+ return;
+ }
+ mRssiSignalLevel = signalLevel;
+ Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
+
+ wifiIcon.setTintList(Utils.getColorAccent(mContext));
+ if (mEntityHeaderController != null) {
+ mEntityHeaderController.setIcon(wifiIcon).done(mFragment.getActivity(),
+ true /* rebind */);
+ }
+
+ Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
+ wifiIconDark.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal));
+ mSignalStrengthPref.setIcon(wifiIconDark);
+
+ mSignalStrengthPref.setSummary(mSignalStr[mRssiSignalLevel]);
+ mSignalStrengthPref.setVisible(true);
+ }
+
+ private void refreshFrequency() {
+ if (mWifiInfo == null) {
+ mFrequencyPref.setVisible(false);
+ return;
+ }
+
final int frequency = mWifiInfo.getFrequency();
String band = null;
if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
@@ -420,40 +545,63 @@
Log.e(TAG, "Unexpected frequency " + frequency);
}
mFrequencyPref.setSummary(band);
-
- updateIpLayerInfo();
+ mFrequencyPref.setVisible(true);
}
- private void exitActivity() {
- if (DEBUG) {
- Log.d(TAG, "Exiting the WifiNetworkDetailsPage");
- }
- mFragment.getActivity().finish();
- }
-
- private void refreshNetworkState() {
- mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
- mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
- .done(mFragment.getActivity(), true /* rebind */);
- }
-
- private void refreshRssiViews() {
- int signalLevel = mAccessPoint.getLevel();
-
- if (mRssiSignalLevel == signalLevel) {
+ private void refreshTxSpeed() {
+ if (mWifiInfo == null) {
+ mTxLinkSpeedPref.setVisible(false);
return;
}
- mRssiSignalLevel = signalLevel;
- Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
- wifiIcon.setTintList(Utils.getColorAccent(mContext));
- mEntityHeaderController.setIcon(wifiIcon).done(mFragment.getActivity(), true /* rebind */);
+ int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps();
+ mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0);
+ mTxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps()));
+ }
- Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
- wifiIconDark.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal));
- mSignalStrengthPref.setIcon(wifiIconDark);
+ private void refreshRxSpeed() {
+ if (mWifiInfo == null) {
+ mRxLinkSpeedPref.setVisible(false);
+ return;
+ }
- mSignalStrengthPref.setSummary(mSignalStr[mRssiSignalLevel]);
+ int rxLinkSpeedMbps = mWifiInfo.getRxLinkSpeedMbps();
+ mRxLinkSpeedPref.setVisible(rxLinkSpeedMbps >= 0);
+ mRxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.rx_link_speed, mWifiInfo.getRxLinkSpeedMbps()));
+ }
+
+ private void refreshMacAddress() {
+ String macAddress = getMacAddress();
+ if (macAddress == null) {
+ mMacAddressPref.setVisible(false);
+ return;
+ }
+
+ mMacAddressPref.setVisible(true);
+ mMacAddressPref.setSummary(macAddress);
+ }
+
+ private String getMacAddress() {
+ if (mWifiInfo != null) {
+ // get MAC address from connected network information
+ return mWifiInfo.getMacAddress();
+ }
+
+ // return randomized MAC address
+ if (mWifiConfig.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
+ return mWifiConfig.getRandomizedMacAddress().toString();
+ }
+
+ // return device MAC address
+ final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+ if (macAddresses != null && macAddresses.length > 0) {
+ return macAddresses[0];
+ }
+
+ Log.e(TAG, "Can't get device MAC address!");
+ return null;
}
private void updatePreference(Preference pref, String detailText) {
@@ -465,13 +613,17 @@
}
}
- private void updateIpLayerInfo() {
+ private void refreshButtons() {
+ mButtonsPref.setButton1Visible(canForgetNetwork());
mButtonsPref.setButton2Visible(canSignIntoNetwork());
mButtonsPref.setButton3Visible(canShareNetwork());
mButtonsPref.setVisible(
canSignIntoNetwork() || canForgetNetwork() || canShareNetwork());
+ }
- if (mNetwork == null || mLinkProperties == null) {
+ private void updateIpLayerInfo() {
+ // Hide IP layer info if not a connected network.
+ if (!mAccessPoint.isActive() || mNetwork == null || mLinkProperties == null) {
mIpAddressPref.setVisible(false);
mSubnetPref.setVisible(false);
mGatewayPref.setVisible(false);
@@ -619,48 +771,9 @@
* Share the wifi network with QR code.
*/
private void shareNetwork() {
- final KeyguardManager keyguardManager = (KeyguardManager) mContext.getSystemService(
- Context.KEYGUARD_SERVICE);
- if (keyguardManager.isKeyguardSecure()) {
- // Show authentication screen to confirm credentials (pin, pattern or password) for
- // the current user of the device.
- final String title = mContext.getString(
- R.string.lockpassword_confirm_your_pattern_header);
- final String description = String.format(
- mContext.getString(R.string.wifi_sharing_message),
- mAccessPoint.getSsidStr());
-
- final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(mContext)
- .setTitle(title)
- .setDescription(description);
-
- if (keyguardManager.isDeviceSecure()) {
- builder.setDeviceCredentialAllowed(true);
- }
-
- final BiometricPrompt bp = builder.build();
- final Handler handler = new Handler(Looper.getMainLooper());
- bp.authenticate(new CancellationSignal(),
- runnable -> handler.post(runnable),
- mAuthenticationCallback);
- } else {
- launchWifiDppConfiguratorActivity();
- }
+ WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorActivity());
}
- private BiometricPrompt.AuthenticationCallback mAuthenticationCallback =
- new BiometricPrompt.AuthenticationCallback() {
- @Override
- public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
- launchWifiDppConfiguratorActivity();
- }
-
- @Override
- public void onAuthenticationError(int errorCode, CharSequence errString) {
- //Do nothing
- }
- };
-
/**
* Sign in to the captive portal found on this wifi network associated with this preference.
*/
@@ -706,4 +819,8 @@
return mContext.getDrawable(Utils.getWifiIconResource(level)).mutate();
}
}
+
+ private boolean usingDataUsageHeader(Context context) {
+ return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER);
+ }
}
diff --git a/src/com/android/settings/wifi/details/WifiDetailSavedNetworkPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailSavedNetworkPreferenceController.java
deleted file mode 100644
index 3407890..0000000
--- a/src/com/android/settings/wifi/details/WifiDetailSavedNetworkPreferenceController.java
+++ /dev/null
@@ -1,66 +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.wifi.details;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.wifi.WifiManager;
-import android.os.Handler;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.wifi.AccessPoint;
-
-public class WifiDetailSavedNetworkPreferenceController extends WifiDetailPreferenceController {
-
- WifiDetailSavedNetworkPreferenceController(AccessPoint accessPoint,
- ConnectivityManager connectivityManager, Context context,
- Fragment fragment, Handler handler,
- Lifecycle lifecycle,
- WifiManager wifiManager,
- MetricsFeatureProvider metricsFeatureProvider,
- IconInjector injector) {
- super(accessPoint, connectivityManager, context, fragment, handler, lifecycle, wifiManager,
- metricsFeatureProvider, injector);
- }
-
- public static WifiDetailSavedNetworkPreferenceController newInstance(
- AccessPoint accessPoint,
- ConnectivityManager connectivityManager,
- Context context,
- Fragment fragment,
- Handler handler,
- Lifecycle lifecycle,
- WifiManager wifiManager,
- MetricsFeatureProvider metricsFeatureProvider) {
- return new WifiDetailSavedNetworkPreferenceController(
- accessPoint, connectivityManager, context, fragment, handler, lifecycle,
- wifiManager, metricsFeatureProvider, new IconInjector(context));
- }
-
- @Override
- public void onPause() {
- // Do nothing
- }
-
- @Override
- public void onResume() {
- updateSavedNetworkInfo();
- }
-}
diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
index fd2e14a..66587ed 100644
--- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
+++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
@@ -51,9 +51,6 @@
private static final String TAG = "WifiNetworkDetailsFrg";
- // Extra for if current fragment shows saved network status or not.
- public static final String EXTRA_IS_SAVED_NETWORK = "SavedNetwork";
-
private AccessPoint mAccessPoint;
private WifiDetailPreferenceController mWifiDetailPreferenceController;
@@ -126,38 +123,23 @@
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
- final boolean isDisplaySavedNetworkDetails =
- getArguments().getBoolean(EXTRA_IS_SAVED_NETWORK, false /* defaultValue */);
- if (isDisplaySavedNetworkDetails) {
- mWifiDetailPreferenceController =
- WifiDetailSavedNetworkPreferenceController.newInstance(
- mAccessPoint,
- cm,
- context,
- this,
- new Handler(Looper.getMainLooper()), // UI thread.
- getSettingsLifecycle(),
- context.getSystemService(WifiManager.class),
- mMetricsFeatureProvider);
- } else {
- mWifiDetailPreferenceController = WifiDetailPreferenceController.newInstance(
- mAccessPoint,
- cm,
- context,
- this,
- new Handler(Looper.getMainLooper()), // UI thread.
- getSettingsLifecycle(),
- context.getSystemService(WifiManager.class),
- mMetricsFeatureProvider);
- }
+ mWifiDetailPreferenceController = WifiDetailPreferenceController.newInstance(
+ mAccessPoint,
+ cm,
+ context,
+ this,
+ new Handler(Looper.getMainLooper()), // UI thread.
+ getSettingsLifecycle(),
+ context.getSystemService(WifiManager.class),
+ mMetricsFeatureProvider);
controllers.add(mWifiDetailPreferenceController);
controllers.add(new WifiMeteredPreferenceController(context, mAccessPoint.getConfig()));
- WifiPrivacyPreferenceController preferenceController = new WifiPrivacyPreferenceController(
+ WifiPrivacyPreferenceController privacyController = new WifiPrivacyPreferenceController(
context);
- preferenceController.setWifiConfiguration(mAccessPoint.getConfig());
- preferenceController.setIsEphemeral(mAccessPoint.isEphemeral());
- controllers.add(preferenceController);
+ privacyController.setWifiConfiguration(mAccessPoint.getConfig());
+ privacyController.setIsEphemeral(mAccessPoint.isEphemeral());
+ controllers.add(privacyController);
return controllers;
}
diff --git a/src/com/android/settings/wifi/details/WifiPrivacyPreferenceController.java b/src/com/android/settings/wifi/details/WifiPrivacyPreferenceController.java
index 97ddc53..a549e21 100644
--- a/src/com/android/settings/wifi/details/WifiPrivacyPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiPrivacyPreferenceController.java
@@ -59,7 +59,7 @@
@Override
public int getAvailabilityStatus() {
return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_p2p_mac_randomization_supported) ?
+ com.android.internal.R.bool.config_wifi_connected_mac_randomization_supported) ?
AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
index 9e731ba..3a9308e 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
@@ -29,6 +29,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -99,6 +100,7 @@
if (!isConfigurationChange) {
mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS;
+ changeFocusAndAnnounceChange(mButtonRight, mTitle);
}
}
@@ -168,15 +170,17 @@
mButtonLeft.setVisibility(View.INVISIBLE);
}
- if (!isConfigurationChange) {
- mLatestStatusCode = code;
- }
-
if (isGoingInitiator()) {
mSummary.setText(R.string.wifi_dpp_sharing_wifi_with_this_device);
}
+
mProgressBar.setVisibility(isGoingInitiator() ? View.VISIBLE : View.INVISIBLE);
mButtonRight.setVisibility(isGoingInitiator() ? View.INVISIBLE : View.VISIBLE);
+
+ if (!isConfigurationChange) {
+ mLatestStatusCode = code;
+ changeFocusAndAnnounceChange(mButtonRight, mSummary);
+ }
}
private boolean hasRetryButton(int code) {
@@ -277,6 +281,7 @@
mButtonRight.setVisibility(View.INVISIBLE);
startWifiDppConfiguratorInitiator();
updateSummary();
+ mTitleSummaryContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
});
if (savedInstanceState != null) {
@@ -288,6 +293,8 @@
} else {
showErrorUi(mLatestStatusCode, /* isConfigurationChange */ true);
}
+ } else {
+ changeFocusAndAnnounceChange(mButtonRight, mTitleSummaryContainer);
}
}
@@ -354,4 +361,17 @@
mSummary.setText(getString(R.string.wifi_dpp_add_device_to_wifi, getSsid()));
}
}
+
+ /**
+ * This fragment will change UI display and text messages for events. To improve Talkback user
+ * experienience, using this method to focus on a right component and announce a changed text
+ * after an UI changing event.
+ *
+ * @param focusView The UI component which will be focused
+ * @param announceView The UI component's text will be talked
+ */
+ private void changeFocusAndAnnounceChange(View focusView, View announceView) {
+ focusView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ announceView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
index 72e845f..ddba933 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
@@ -23,6 +23,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.Button;
import android.widget.ListView;
@@ -105,5 +106,11 @@
mButtonRight = view.findViewById(R.id.button_right);
mButtonRight.setVisibility(View.GONE);
+
+ if (savedInstanceState == null) {
+ // For Talkback to describe this fragment
+ mTitleSummaryContainer.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
}
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
index 0a2c09b..7308741 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
@@ -75,6 +75,7 @@
private static final String KEY_WIFI_PRESHARED_KEY = "key_wifi_preshared_key";
private static final String KEY_WIFI_HIDDEN_SSID = "key_wifi_hidden_ssid";
private static final String KEY_WIFI_NETWORK_ID = "key_wifi_network_id";
+ private static final String KEY_IS_HOTSPOT = "key_is_hotspot";
private FragmentManager mFragmentManager;
@@ -104,14 +105,15 @@
mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode);
- String security = savedInstanceState.getString(KEY_WIFI_SECURITY);
- String ssid = savedInstanceState.getString(KEY_WIFI_SSID);
- String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY);
- boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID);
- int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID);
+ final String security = savedInstanceState.getString(KEY_WIFI_SECURITY);
+ final String ssid = savedInstanceState.getString(KEY_WIFI_SSID);
+ final String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY);
+ final boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID);
+ final int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID);
+ final boolean isHotspot = savedInstanceState.getBoolean(KEY_IS_HOTSPOT);
mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid,
- preSharedKey, hiddenSsid, networkId);
+ preSharedKey, hiddenSsid, networkId, isHotspot);
} else {
handleIntent(getIntent());
}
@@ -361,6 +363,7 @@
outState.putString(KEY_WIFI_PRESHARED_KEY, mWifiNetworkConfig.getPreSharedKey());
outState.putBoolean(KEY_WIFI_HIDDEN_SSID, mWifiNetworkConfig.getHiddenSsid());
outState.putInt(KEY_WIFI_NETWORK_ID, mWifiNetworkConfig.getNetworkId());
+ outState.putBoolean(KEY_IS_HOTSPOT, mWifiNetworkConfig.isHotspot());
}
super.onSaveInstanceState(outState);
@@ -393,7 +396,8 @@
wifiConfiguration.getPrintableSsid(),
wifiConfiguration.preSharedKey,
/* hiddenSsid */ false,
- wifiConfiguration.networkId);
+ wifiConfiguration.networkId,
+ /* isHotspot */ false);
}
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
index fab495d..eafbe68 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
@@ -38,6 +38,7 @@
private ImageView mDevicesCheckCircleGreenHeaderIcon;
protected TextView mTitle;
protected TextView mSummary;
+ protected View mTitleSummaryContainer;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -48,6 +49,10 @@
view.findViewById(R.id.devices_check_circle_green_icon);
mTitle = view.findViewById(android.R.id.title);
mSummary = view.findViewById(android.R.id.summary);
+
+ // This is the LinearLayout which groups mTitle and mSummary for Talkback to announce the
+ // content in a way that reflects its natural groupings.
+ mTitleSummaryContainer = view.findViewById(R.id.title_summary_container);
}
protected void setHeaderIconImageResource(int resId) {
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
index daa41d9f..4e6a0d6 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -29,6 +30,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.wifi.qrcode.QrCodeGenerator;
@@ -43,6 +45,7 @@
private static final String TAG = "WifiDppQrCodeGeneratorFragment";
private ImageView mQrCodeView;
+ private TextView mPasswordView;
private String mQrCode;
@Override
@@ -60,6 +63,14 @@
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ // setTitle for Talkback
+ final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
+ if (wifiNetworkConfig.isHotspot()) {
+ getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
+ } else {
+ getActivity().setTitle(R.string.wifi_dpp_share_wifi);
+ }
+
setHasOptionsMenu(true);
final ActionBar actionBar = getActivity().getActionBar();
if (actionBar != null) {
@@ -86,7 +97,8 @@
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
MenuItem menuItem;
- if (wifiNetworkConfig.isSupportWifiDpp(getActivity())) {
+ if (!wifiNetworkConfig.isHotspot() &&
+ wifiNetworkConfig.isSupportWifiDpp(getActivity())) {
menuItem = menu.add(0, Menu.FIRST, 0, R.string.next_label);
menuItem.setIcon(R.drawable.ic_scan_24dp);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -127,9 +139,30 @@
setHeaderIconImageResource(R.drawable.ic_qrcode_24dp);
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
- mTitle.setText(R.string.wifi_dpp_share_wifi);
- mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_with_another_device,
- wifiNetworkConfig.getSsid()));
+ if (wifiNetworkConfig.isHotspot()) {
+ mTitle.setText(R.string.wifi_dpp_share_hotspot);
+ } else {
+ mTitle.setText(R.string.wifi_dpp_share_wifi);
+ }
+
+ final String password = wifiNetworkConfig.getPreSharedKey();
+ mPasswordView = view.findViewById(R.id.password);
+ if (TextUtils.isEmpty(password)) {
+ mSummary.setText(getString(
+ R.string.wifi_dpp_scan_open_network_qr_code_with_another_device,
+ wifiNetworkConfig.getSsid()));
+
+ mPasswordView.setVisibility(View.GONE);
+ } else {
+ mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_with_another_device,
+ wifiNetworkConfig.getSsid()));
+
+ if (wifiNetworkConfig.isHotspot()) {
+ mPasswordView.setText(getString(R.string.wifi_dpp_hotspot_password, password));
+ } else {
+ mPasswordView.setText(getString(R.string.wifi_dpp_wifi_password, password));
+ }
+ }
mQrCode = wifiNetworkConfig.getQrCode();
setQrCode();
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index a19069b..905d3a0 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -41,9 +41,11 @@
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.StringRes;
import androidx.lifecycle.ViewModelProviders;
import com.android.settings.R;
@@ -51,12 +53,17 @@
import com.android.settings.wifi.qrcode.QrCamera;
import com.android.settings.wifi.qrcode.QrDecorateView;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
import java.util.List;
public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment implements
SurfaceTextureListener,
QrCamera.ScannerCallback,
- WifiManager.ActionListener {
+ WifiManager.ActionListener,
+ WifiTracker.WifiListener {
private static final String TAG = "WifiDppQrCodeScannerFragment";
/** Message sent to hide error message */
@@ -79,6 +86,8 @@
private static final String KEY_LATEST_ERROR_CODE = "key_latest_error_code";
private static final String KEY_WIFI_CONFIGURATION = "key_wifi_configuration";
+ private static final int ARG_RESTART_CAMERA = 1;
+
private ProgressBar mProgressBar;
private QrCamera mCamera;
private TextureView mTextureView;
@@ -99,6 +108,68 @@
private int mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_FAILURE_NONE;
+ private WifiTracker mWifiTracker;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_HIDE_ERROR_MESSAGE:
+ mErrorMessage.setVisibility(View.INVISIBLE);
+ break;
+
+ case MESSAGE_SHOW_ERROR_MESSAGE:
+ final String errorMessage = (String) msg.obj;
+
+ mErrorMessage.setVisibility(View.VISIBLE);
+ mErrorMessage.setText(errorMessage);
+ mErrorMessage.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
+ // Cancel any pending messages to hide error view and requeue the message so
+ // user has time to see error
+ removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
+ sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
+ SHOW_ERROR_MESSAGE_INTERVAL);
+
+ if (msg.arg1 == ARG_RESTART_CAMERA) {
+ mProgressBar.setVisibility(View.INVISIBLE);
+ restartCamera();
+ }
+ break;
+
+ case MESSAGE_SCAN_WIFI_DPP_SUCCESS:
+ mErrorMessage.setVisibility(View.INVISIBLE);
+
+ if (mScanWifiDppSuccessListener == null) {
+ return;
+ }
+ mScanWifiDppSuccessListener.onScanWifiDppSuccess((WifiQrCode)msg.obj);
+
+ if (!mIsConfiguratorMode) {
+ mProgressBar.setVisibility(View.VISIBLE);
+ startWifiDppEnrolleeInitiator((WifiQrCode)msg.obj);
+ updateEnrolleeSummary();
+ mSummary.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+ break;
+
+ case MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS:
+ mErrorMessage.setVisibility(View.INVISIBLE);
+
+ final WifiNetworkConfig wifiNetworkConfig = (WifiNetworkConfig)msg.obj;
+ mWifiConfiguration = wifiNetworkConfig.getWifiConfigurationOrNull();
+ wifiNetworkConfig.connect(getContext(),
+ /* listener */ WifiDppQrCodeScannerFragment.this);
+ break;
+
+ default:
+ return;
+ }
+ }
+ };
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -174,6 +245,16 @@
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ mWifiTracker = WifiTrackerFactory.create(getActivity(), /* wifiListener */ this,
+ getSettingsLifecycle(), /* includeSaved */ false, /* includeScans */ true);
+
+ // setTitle for Talkback
+ if (mIsConfiguratorMode) {
+ getActivity().setTitle(R.string.wifi_dpp_add_device_to_network);
+ } else {
+ getActivity().setTitle(R.string.wifi_dpp_scan_qr_code);
+ }
+
final ActionBar actionBar = getActivity().getActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
@@ -283,7 +364,7 @@
try {
mWifiQrCode = new WifiQrCode(qrCode);
} catch (IllegalArgumentException e) {
- mHandler.sendEmptyMessage(MESSAGE_SHOW_ERROR_MESSAGE);
+ showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
return false;
}
@@ -293,14 +374,14 @@
if (!mIsConfiguratorMode && WifiQrCode.SCHEME_ZXING_WIFI_NETWORK_CONFIG.equals(scheme)) {
final String ssidQrCode = mWifiQrCode.getWifiNetworkConfig().getSsid();
if (!TextUtils.isEmpty(mSsid) && !mSsid.equals(ssidQrCode)) {
- mHandler.sendEmptyMessage(MESSAGE_SHOW_ERROR_MESSAGE);
+ showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
return false;
}
}
// It's impossible to provision other device with ZXing Wi-Fi Network config format
if (mIsConfiguratorMode && WifiQrCode.SCHEME_ZXING_WIFI_NETWORK_CONFIG.equals(scheme)) {
- mHandler.sendEmptyMessage(MESSAGE_SHOW_ERROR_MESSAGE);
+ showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
return false;
}
@@ -377,56 +458,18 @@
}
}
- public void showErrorMessage(String message) {
- mErrorMessage.setVisibility(View.VISIBLE);
- mErrorMessage.setText(message);
-
- mHandler.removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
- mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
- SHOW_ERROR_MESSAGE_INTERVAL);
+ private void showErrorMessage(@StringRes int messageResId) {
+ final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
+ getString(messageResId));
+ message.sendToTarget();
}
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_HIDE_ERROR_MESSAGE:
- mErrorMessage.setVisibility(View.INVISIBLE);
- break;
-
- case MESSAGE_SHOW_ERROR_MESSAGE:
- showErrorMessage(getString(R.string.wifi_dpp_could_not_detect_valid_qr_code));
- break;
-
- case MESSAGE_SCAN_WIFI_DPP_SUCCESS:
- mErrorMessage.setVisibility(View.INVISIBLE);
-
- if (mScanWifiDppSuccessListener == null) {
- return;
- }
- mScanWifiDppSuccessListener.onScanWifiDppSuccess((WifiQrCode)msg.obj);
-
- if (!mIsConfiguratorMode) {
- mProgressBar.setVisibility(View.VISIBLE);
- startWifiDppEnrolleeInitiator((WifiQrCode)msg.obj);
- updateEnrolleeSummary();
- }
- break;
-
- case MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS:
- mErrorMessage.setVisibility(View.INVISIBLE);
-
- final WifiNetworkConfig wifiNetworkConfig = (WifiNetworkConfig)msg.obj;
- mWifiConfiguration = wifiNetworkConfig.getWifiConfigurationOrNull();
- wifiNetworkConfig.connect(getContext(),
- /* listener */ WifiDppQrCodeScannerFragment.this);
- break;
-
- default:
- return;
- }
- }
- };
+ private void showErrorMessageAndRestartCamera(@StringRes int messageResId) {
+ final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
+ getString(messageResId));
+ message.arg1 = ARG_RESTART_CAMERA;
+ message.sendToTarget();
+ }
@Override
public void onSaveInstanceState(Bundle outState) {
@@ -457,9 +500,7 @@
Log.e(TAG, "Invalid networkId " + newNetworkId);
mLatestStatusCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC;
updateEnrolleeSummary();
- mProgressBar.setVisibility(View.INVISIBLE);
- showErrorMessage(getString(R.string.wifi_dpp_check_connection_try_again));
- restartCamera();
+ showErrorMessageAndRestartCamera(R.string.wifi_dpp_check_connection_try_again);
}
@Override
@@ -471,23 +512,22 @@
public void onFailure(int code) {
Log.d(TAG, "EasyConnectEnrolleeStatusCallback.onFailure " + code);
+ int errorMessageResId = 0;
switch (code) {
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI:
- showErrorMessage(getString(R.string.wifi_dpp_could_not_detect_valid_qr_code));
+ errorMessageResId = R.string.wifi_dpp_could_not_detect_valid_qr_code;
break;
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION:
- showErrorMessage(
- getString(R.string.wifi_dpp_failure_authentication_or_configuration));
+ errorMessageResId = R.string.wifi_dpp_failure_authentication_or_configuration;
break;
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE:
- showErrorMessage(getString(R.string.wifi_dpp_failure_not_compatible));
+ errorMessageResId = R.string.wifi_dpp_failure_not_compatible;
break;
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION:
- showErrorMessage(
- getString(R.string.wifi_dpp_failure_authentication_or_configuration));
+ errorMessageResId = R.string.wifi_dpp_failure_authentication_or_configuration;
break;
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY:
@@ -504,11 +544,11 @@
return;
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT:
- showErrorMessage(getString(R.string.wifi_dpp_failure_timeout));
+ errorMessageResId = R.string.wifi_dpp_failure_timeout;
break;
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC:
- showErrorMessage(getString(R.string.wifi_dpp_failure_generic));
+ errorMessageResId = R.string.wifi_dpp_failure_generic;
break;
case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED:
@@ -525,8 +565,7 @@
mLatestStatusCode = code;
updateEnrolleeSummary();
- mProgressBar.setVisibility(View.INVISIBLE);
- restartCamera();
+ showErrorMessageAndRestartCamera(errorMessageResId);
}
@Override
@@ -544,21 +583,39 @@
@Override
public void onSuccess() {
- final Intent resultIntent = new Intent();
- resultIntent.putExtra(WifiDialogActivity.KEY_WIFI_CONFIGURATION, mWifiConfiguration);
+ if (isEnrollingWifiNetworkReachable()) {
+ final Intent resultIntent = new Intent();
+ resultIntent.putExtra(WifiDialogActivity.KEY_WIFI_CONFIGURATION, mWifiConfiguration);
- final Activity hostActivity = getActivity();
- hostActivity.setResult(Activity.RESULT_OK, resultIntent);
- hostActivity.finish();
+ final Activity hostActivity = getActivity();
+ hostActivity.setResult(Activity.RESULT_OK, resultIntent);
+ hostActivity.finish();
+ } else {
+ Log.d(TAG, "Enroll Wi-Fi network succeeded but it's not reachable");
+ showErrorMessageAndRestartCamera(R.string.wifi_dpp_check_connection_try_again);
+ }
}
@Override
public void onFailure(int reason) {
Log.d(TAG, "Wi-Fi connect onFailure reason - " + reason);
+ showErrorMessageAndRestartCamera(R.string.wifi_dpp_check_connection_try_again);
+ }
- mProgressBar.setVisibility(View.INVISIBLE);
- showErrorMessage(getString(R.string.wifi_dpp_check_connection_try_again));
- restartCamera();
+ private boolean isEnrollingWifiNetworkReachable() {
+ if (mWifiConfiguration == null) {
+ Log.e(TAG, "Connect succeeded but lost WifiConfiguration");
+ return false;
+ }
+
+ final List<AccessPoint> scannedAccessPoints = mWifiTracker.getAccessPoints();
+ for (AccessPoint accessPoint : scannedAccessPoints) {
+ if (accessPoint.matches(mWifiConfiguration) &&
+ accessPoint.isReachable()) {
+ return true;
+ }
+ }
+ return false;
}
// Check is Easy Connect handshaking or not
@@ -599,4 +656,25 @@
mSummary.setText(description);
}
}
+
+ /** Called when the state of Wifi has changed. */
+ @Override
+ public void onWifiStateChanged(int state) {
+ // Do nothing.
+ }
+
+ /** Called when the connection state of wifi has changed. */
+ @Override
+ public void onConnectedChanged() {
+ // Do nothing.
+ }
+
+ /**
+ * Called to indicate the list of AccessPoints has been updated and
+ * getAccessPoints should be called to get the latest information.
+ */
+ @Override
+ public void onAccessPointsChanged() {
+ // Do nothing.
+ }
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 4d8cca5..bf78b3f 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -16,14 +16,21 @@
package com.android.settings.wifi.dpp;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
+import android.hardware.biometrics.BiometricPrompt;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
+import com.android.settings.R;
+
import com.android.settingslib.wifi.AccessPoint;
import java.util.List;
@@ -70,6 +77,9 @@
/** The data corresponding to {@code WifiConfiguration} networkId */
public static final String EXTRA_WIFI_NETWORK_ID = "networkId";
+ /** The data to recognize if it's a Wi-Fi hotspot for configuration */
+ public static final String EXTRA_IS_HOTSPOT = "isHotspot";
+
/** Used by {@link android.provider.Settings#ACTION_PROCESS_WIFI_EASY_CONNECT_URI} to
* indicate test mode UI should be shown. Test UI does not make API calls. Value is a boolean.*/
public static final String EXTRA_TEST = "test";
@@ -142,24 +152,12 @@
return str.substring(begin, end+1);
}
- private static String getSecurityString(AccessPoint accessPoint) {
- switch(accessPoint.getSecurity()) {
- case AccessPoint.SECURITY_WEP:
- return WifiQrCode.SECURITY_WEP;
- case AccessPoint.SECURITY_PSK:
- return WifiQrCode.SECURITY_WPA_PSK;
- case AccessPoint.SECURITY_SAE:
- return WifiQrCode.SECURITY_SAE;
- default:
- return WifiQrCode.SECURITY_NO_PASSWORD;
- }
- }
-
static String getSecurityString(WifiConfiguration config) {
if (config.allowedKeyManagement.get(KeyMgmt.SAE)) {
return WifiQrCode.SECURITY_SAE;
}
- if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ||
+ config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
return WifiQrCode.SECURITY_WPA_PSK;
}
return (config.wepKeys[0] == null) ?
@@ -171,6 +169,9 @@
* security. It may return null if the security is not supported by QR code generator nor
* scanner.
*
+ * Do not use this method for Wi-Fi hotspot network, use
+ * {@code getHotspotConfiguratorIntentOrNull} instead.
+ *
* @param context The context to use for the content resolver
* @param wifiManager An instance of {@link WifiManager}
* @param accessPoint An instance of {@link AccessPoint}
@@ -187,15 +188,63 @@
return null;
}
- final WifiConfiguration wifiConfig = accessPoint.getConfig();
- final String ssid = removeFirstAndLastDoubleQuotes(wifiConfig.SSID);
- final String security = getSecurityString(accessPoint);
- String preSharedKey = wifiConfig.preSharedKey;
+ final WifiConfiguration wifiConfiguration = accessPoint.getConfig();
+ setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
+
+ if (wifiConfiguration.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ throw new IllegalArgumentException("Invalid network ID");
+ } else {
+ intent.putExtra(EXTRA_WIFI_NETWORK_ID, wifiConfiguration.networkId);
+ }
+
+ return intent;
+ }
+
+ /**
+ * Returns an intent to launch QR code generator for the Wi-Fi hotspot. It may return null if
+ * the security is not supported by QR code generator.
+ *
+ * @param context The context to use for the content resolver
+ * @param wifiManager An instance of {@link WifiManager}
+ * @param wifiConfiguration {@link WifiConfiguration} of the Wi-Fi hotspot
+ * @return Intent for launching QR code generator
+ */
+ public static Intent getHotspotConfiguratorIntentOrNull(Context context,
+ WifiManager wifiManager, WifiConfiguration wifiConfiguration) {
+ final Intent intent = new Intent(context, WifiDppConfiguratorActivity.class);
+ if (isSupportHotspotConfiguratorQrCodeGenerator(wifiConfiguration)) {
+ intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
+ } else {
+ return null;
+ }
+
+ setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
+
+ intent.putExtra(EXTRA_WIFI_NETWORK_ID, WifiConfiguration.INVALID_NETWORK_ID);
+ intent.putExtra(EXTRA_IS_HOTSPOT, true);
+
+ return intent;
+ }
+
+ /**
+ * Set all extra except {@code EXTRA_WIFI_NETWORK_ID} for the intent to
+ * launch configurator activity later.
+ *
+ * @param intent the target to set extra
+ * @param wifiManager an instance of {@code WifiManager}
+ * @param wifiConfiguration the Wi-Fi network for launching configurator activity
+ */
+ private static void setConfiguratorIntentExtra(Intent intent, WifiManager wifiManager,
+ WifiConfiguration wifiConfiguration) {
+ final String ssid = removeFirstAndLastDoubleQuotes(wifiConfiguration.SSID);
+ final String security = getSecurityString(wifiConfiguration);
+ String preSharedKey = wifiConfiguration.preSharedKey;
if (preSharedKey != null) {
// When the value of this key is read, the actual key is not returned, just a "*".
// Call privileged system API to obtain actual key.
- preSharedKey = removeFirstAndLastDoubleQuotes(getPresharedKey(wifiManager, wifiConfig));
+ preSharedKey = removeFirstAndLastDoubleQuotes(getPresharedKey(wifiManager,
+ wifiConfiguration));
}
if (!TextUtils.isEmpty(ssid)) {
@@ -207,13 +256,6 @@
if (!TextUtils.isEmpty(preSharedKey)) {
intent.putExtra(EXTRA_WIFI_PRE_SHARED_KEY, preSharedKey);
}
- if (wifiConfig.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
- throw new IllegalArgumentException("Invalid network ID");
- } else {
- intent.putExtra(EXTRA_WIFI_NETWORK_ID, wifiConfig.networkId);
- }
-
- return intent;
}
/**
@@ -228,6 +270,50 @@
isSupportConfiguratorQrCodeGenerator(accessPoint);
}
+ /**
+ * Shows authentication screen to confirm credentials (pin, pattern or password) for the current
+ * user of the device.
+ *
+ * @param context The {@code Context} used to get {@code KeyguardManager} service
+ * @param successRunnable The {@code Runnable} which will be executed if the user does not setup
+ * device security or if lock screen is unlocked
+ */
+ public static void showLockScreen(Context context, Runnable successRunnable) {
+ final KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(
+ Context.KEYGUARD_SERVICE);
+
+ if (keyguardManager.isKeyguardSecure()) {
+ final BiometricPrompt.AuthenticationCallback authenticationCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ successRunnable.run();
+ }
+
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ //Do nothing
+ }
+ };
+
+ final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
+ .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title));
+
+ if (keyguardManager.isDeviceSecure()) {
+ builder.setDeviceCredentialAllowed(true);
+ }
+
+ final BiometricPrompt bp = builder.build();
+ final Handler handler = new Handler(Looper.getMainLooper());
+ bp.authenticate(new CancellationSignal(),
+ runnable -> handler.post(runnable),
+ authenticationCallback);
+ } else {
+ successRunnable.run();
+ }
+ }
+
private static boolean isSupportConfiguratorQrCodeScanner(Context context,
AccessPoint accessPoint) {
if (!isWifiDppEnabled(context)) {
@@ -254,4 +340,13 @@
return false;
}
+
+ private static boolean isSupportHotspotConfiguratorQrCodeGenerator(
+ WifiConfiguration wifiConfiguration) {
+ // QR code generator produces QR code with ZXing's Wi-Fi network config format,
+ // it supports PSK and WEP and non security
+ // KeyMgmt.NONE is for WEP or non security
+ return wifiConfiguration.allowedKeyManagement.get(KeyMgmt.WPA2_PSK) ||
+ wifiConfiguration.allowedKeyManagement.get(KeyMgmt.NONE);
+ }
}
diff --git a/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java b/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java
index a483073..fdc74d8 100644
--- a/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java
+++ b/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java
@@ -30,7 +30,6 @@
import android.text.TextUtils;
import android.util.Log;
-import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
/**
@@ -52,15 +51,17 @@
private String mPreSharedKey;
private boolean mHiddenSsid;
private int mNetworkId;
+ private boolean mIsHotspot;
@VisibleForTesting
WifiNetworkConfig(String security, String ssid, String preSharedKey,
- boolean hiddenSsid, int networkId) {
+ boolean hiddenSsid, int networkId, boolean isHotspot) {
mSecurity = security;
mSsid = ssid;
mPreSharedKey = preSharedKey;
mHiddenSsid = hiddenSsid;
mNetworkId = networkId;
+ mIsHotspot = isHotspot;
}
public WifiNetworkConfig(WifiNetworkConfig config) {
@@ -69,6 +70,7 @@
mPreSharedKey = config.mPreSharedKey;
mHiddenSsid = config.mHiddenSsid;
mNetworkId = config.mNetworkId;
+ mIsHotspot = config.mIsHotspot;
}
/**
@@ -86,23 +88,26 @@
* android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_SCANNER
*/
public static WifiNetworkConfig getValidConfigOrNull(Intent intent) {
- String security = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SECURITY);
- String ssid = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SSID);
- String preSharedKey = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY);
- boolean hiddenSsid = intent.getBooleanExtra(WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID, false);
- int networkId = intent.getIntExtra(WifiDppUtils.EXTRA_WIFI_NETWORK_ID,
+ final String security = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SECURITY);
+ final String ssid = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SSID);
+ final String preSharedKey = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY);
+ final boolean hiddenSsid = intent.getBooleanExtra(WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID,
+ false);
+ final int networkId = intent.getIntExtra(WifiDppUtils.EXTRA_WIFI_NETWORK_ID,
WifiConfiguration.INVALID_NETWORK_ID);
+ final boolean isHotspot = intent.getBooleanExtra(WifiDppUtils.EXTRA_IS_HOTSPOT, false);
- return getValidConfigOrNull(security, ssid, preSharedKey, hiddenSsid, networkId);
+ return getValidConfigOrNull(security, ssid, preSharedKey, hiddenSsid, networkId, isHotspot);
}
public static WifiNetworkConfig getValidConfigOrNull(String security, String ssid,
- String preSharedKey, boolean hiddenSsid, int networkId) {
+ String preSharedKey, boolean hiddenSsid, int networkId, boolean isHotspot) {
if (!isValidConfig(security, ssid, preSharedKey, hiddenSsid)) {
return null;
}
- return new WifiNetworkConfig(security, ssid, preSharedKey, hiddenSsid, networkId);
+ return new WifiNetworkConfig(security, ssid, preSharedKey, hiddenSsid, networkId,
+ isHotspot);
}
public static boolean isValidConfig(WifiNetworkConfig config) {
@@ -174,31 +179,30 @@
return barcode;
}
- @Keep
public String getSecurity() {
return mSecurity;
}
- @Keep
public String getSsid() {
return mSsid;
}
- @Keep
public String getPreSharedKey() {
return mPreSharedKey;
}
- @Keep
public boolean getHiddenSsid() {
return mHiddenSsid;
}
- @Keep
public int getNetworkId() {
return mNetworkId;
}
+ public boolean isHotspot() {
+ return mIsHotspot;
+ }
+
public void connect(Context context, WifiManager.ActionListener listener) {
WifiConfiguration wifiConfiguration = getWifiConfigurationOrNull();
if (wifiConfiguration == null) {
diff --git a/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java b/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java
index ce92f90..84f1c36 100644
--- a/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java
@@ -34,11 +34,14 @@
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.wifi.AddNetworkFragment;
+import com.android.settings.wifi.savedaccesspoints.SavedNetworkComparator;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.WifiSavedConfigUtils;
import com.android.settingslib.wifi.WifiTracker;
import com.android.settingslib.wifi.WifiTrackerFactory;
+import java.util.Collections;
import java.util.List;
public class WifiNetworkListFragment extends SettingsPreferenceFragment implements
@@ -218,7 +221,7 @@
final WifiNetworkConfig networkConfig = WifiNetworkConfig.getValidConfigOrNull(
selectedAccessPoint.getSecurityString(/* concise */ true),
wifiConfig.getPrintableSsid(), wifiConfig.preSharedKey, /* hiddenSsid */ false,
- wifiConfig.networkId);
+ wifiConfig.networkId, /* isHotspot */ false);
if (mOnChooseNetworkListener != null) {
mOnChooseNetworkListener.onChooseNetwork(networkConfig);
}
@@ -232,7 +235,8 @@
/* ssid */ WifiNetworkConfig.FAKE_SSID,
/* preSharedKey */ WifiNetworkConfig.FAKE_PASSWORD,
/* hiddenSsid */ true,
- /* networkId */ WifiConfiguration.INVALID_NETWORK_ID));
+ /* networkId */ WifiConfiguration.INVALID_NETWORK_ID,
+ /* isHotspot*/ false));
}
} else {
return super.onPreferenceTreeClick(preference);
@@ -255,17 +259,6 @@
return false;
}
- // Can only use saved network for DPP configuration. For ephemeral connections networkId
- // is invalid.
- if (!accessPoint.isSaved()) {
- return false;
- }
-
- // Ignore access points that are out of range.
- if (!accessPoint.isReachable()) {
- return false;
- }
-
return true;
}
@@ -289,44 +282,47 @@
return;
}
- // AccessPoints are sorted by the WifiTracker
- final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
-
- mAccessPointsPreferenceCategory.setVisible(true);
-
- cacheRemoveAllPrefs(mAccessPointsPreferenceCategory);
+ // TODO(b/128942314): Lists reachable AccessPoints on top of the list
+ final List<AccessPoint> savedAccessPoints =
+ WifiSavedConfigUtils.getAllConfigs(getContext(), mWifiManager);
+ Collections.sort(savedAccessPoints, SavedNetworkComparator.INSTANCE);
int index = 0;
- for (; index < accessPoints.size(); index++) {
- AccessPoint accessPoint = accessPoints.get(index);
- // Check if this access point is valid for DPP.
- if (isValidForDppConfiguration(accessPoint)) {
- final String key = accessPoint.getKey();
+ mAccessPointsPreferenceCategory.removeAll();
+ for (AccessPoint savedAccessPoint : savedAccessPoints) {
+ if (isValidForDppConfiguration(savedAccessPoint)) {
+ // Replaces with an AccessPoint from scanned result for signal information
+ savedAccessPoint = getScannedAccessPointIfAvailable(savedAccessPoint);
+ final AccessPointPreference preference =
+ createAccessPointPreference(savedAccessPoint);
- final AccessPointPreference pref = (AccessPointPreference) getCachedPreference(key);
- if (pref != null) {
- pref.setOrder(index);
- continue;
- }
- final AccessPointPreference preference = createAccessPointPreference(accessPoint);
- preference.setKey(key);
- preference.setOrder(index);
+ preference.setOrder(index++);
+ preference.setEnabled(savedAccessPoint.isReachable());
+ savedAccessPoint.setListener(this);
- mAccessPointsPreferenceCategory.addPreference(preference);
- accessPoint.setListener(this);
preference.refresh();
+ mAccessPointsPreferenceCategory.addPreference(preference);
}
}
- removeCachedPrefs(mAccessPointsPreferenceCategory);
mAddPreference.setOrder(index);
mAccessPointsPreferenceCategory.addPreference(mAddPreference);
if (mIsTest) {
- mFakeNetworkPreference.setOrder(index + 1);
mAccessPointsPreferenceCategory.addPreference(mFakeNetworkPreference);
}
}
+ private AccessPoint getScannedAccessPointIfAvailable(AccessPoint savedAccessPoint) {
+ final List<AccessPoint> scannedAccessPoints = mWifiTracker.getAccessPoints();
+ final WifiConfiguration savedWifiConfiguration = savedAccessPoint.getConfig();
+ for (AccessPoint scannedAccessPoint : scannedAccessPoints) {
+ if (scannedAccessPoint.matches(savedWifiConfiguration)) {
+ return scannedAccessPoint;
+ }
+ }
+ return savedAccessPoint;
+ }
+
private AccessPointPreference createAccessPointPreference(AccessPoint accessPoint) {
return new AccessPointPreference(accessPoint, getPrefContext(), mUserBadgeCache,
R.drawable.ic_wifi_signal_0, /* forSavedNetworks */ false);
diff --git a/src/com/android/settings/wifi/dpp/WifiQrCode.java b/src/com/android/settings/wifi/dpp/WifiQrCode.java
index 10971cf..8296a62 100644
--- a/src/com/android/settings/wifi/dpp/WifiQrCode.java
+++ b/src/com/android/settings/wifi/dpp/WifiQrCode.java
@@ -133,7 +133,7 @@
password = removeBackSlash(password);
mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid, password,
- hiddenSsid, WifiConfiguration.INVALID_NETWORK_ID);
+ hiddenSsid, WifiConfiguration.INVALID_NETWORK_ID, /* isHotspot */ false);
if (mWifiNetworkConfig == null) {
throw new IllegalArgumentException("Invalid format");
diff --git a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java
index 867c2f8..3f600e6 100644
--- a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java
+++ b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java
@@ -108,7 +108,6 @@
}
final Bundle savedState = new Bundle();
mSelectedAccessPoint.saveWifiState(savedState);
- savedState.putBoolean(WifiNetworkDetailsFragment.EXTRA_IS_SAVED_NETWORK, true);
new SubSettingLauncher(getContext())
.setTitleText(mSelectedAccessPoint.getTitle())
@@ -199,11 +198,7 @@
* Checks if showing WifiNetworkDetailsFragment when clicking saved network item.
*/
public static boolean usingDetailsFragment(Context context) {
- if (FeatureFlagUtils.isEnabled(context, FeatureFlags.MOBILE_NETWORK_V2)
- && FeatureFlagPersistent.isEnabled(context, FeatureFlags.NETWORK_INTERNET_V2)) {
- return false; // TODO(b/124695272): mark true when UI is ready.
- }
- return false;
+ return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_SAVED_SCREEN);
}
boolean isSubscriptionsFeatureEnabled() {
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
index fa8c267..4a799d1 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
@@ -25,6 +25,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.CustomSliceable;
@@ -35,7 +36,9 @@
private static final String TAG = "ContextualWifiSlice";
@VisibleForTesting
- boolean mPreviouslyDisplayed;
+ static long sActiveUiSession = -1000;
+ @VisibleForTesting
+ static boolean sPreviouslyDisplayed;
public ContextualWifiSlice(Context context) {
super(context);
@@ -48,13 +51,19 @@
@Override
public Slice getSlice() {
- if (!mPreviouslyDisplayed && !TextUtils.equals(getActiveSSID(), WifiSsid.NONE)) {
+ final long currentUiSession = FeatureFactory.getFactory(mContext)
+ .getSlicesFeatureProvider().getUiSessionToken();
+ if (currentUiSession != sActiveUiSession) {
+ sActiveUiSession = currentUiSession;
+ sPreviouslyDisplayed = false;
+ }
+ if (!sPreviouslyDisplayed && !TextUtils.equals(getActiveSSID(), WifiSsid.NONE)) {
Log.d(TAG, "Wifi is connected, no point showing any suggestion.");
return null;
}
- // Set mPreviouslyDisplayed to true - we will show *something* on the screen. So we should
+ // Set sPreviouslyDisplayed to true - we will show *something* on the screen. So we should
// keep showing this card to keep UI stable, even if wifi connects to a network later.
- mPreviouslyDisplayed = true;
+ sPreviouslyDisplayed = true;
return super.getSlice();
}
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index a2debb8..f2c919b 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -32,6 +32,8 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.NetworkInfo.DetailedState;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -63,7 +65,10 @@
import com.android.settingslib.wifi.WifiTracker;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* {@link CustomSliceable} for Wi-Fi, used by generic clients.
@@ -92,26 +97,7 @@
mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */);
final boolean isWifiEnabled = isWifiEnabled();
-
- final IconCompat icon = IconCompat.createWithResource(mContext,
- R.drawable.ic_settings_wireless);
- final String title = mContext.getString(R.string.wifi_settings);
- final CharSequence summary = getSummary();
- final PendingIntent toggleAction = getBroadcastIntent(mContext);
- final PendingIntent primaryAction = getPrimaryAction();
- final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon,
- ListBuilder.ICON_IMAGE, title);
- final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
- null /* actionTitle */, isWifiEnabled);
-
- final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
- .setAccentColor(COLOR_NOT_TINTED)
- .addRow(new ListBuilder.RowBuilder()
- .setTitle(title)
- .setSubtitle(summary)
- .addEndItem(toggleSliceAction)
- .setPrimaryAction(primarySliceAction));
-
+ ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* accessPoint */);
if (!isWifiEnabled) {
return listBuilder.build();
}
@@ -136,7 +122,12 @@
final CharSequence placeholder = mContext.getText(R.string.summary_placeholder);
for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) {
if (i < apCount) {
- listBuilder.addRow(getAccessPointRow(results.get(i)));
+ final AccessPoint accessPoint = results.get(i);
+ if (accessPoint.isActive()) {
+ // update summary
+ listBuilder = getListBuilder(isWifiEnabled, accessPoint);
+ }
+ listBuilder.addRow(getAccessPointRow(accessPoint));
} else if (needLoadingRow) {
listBuilder.addRow(getLoadingRow());
needLoadingRow = false;
@@ -148,6 +139,28 @@
return listBuilder.build();
}
+ private ListBuilder getListBuilder(boolean isWifiEnabled, AccessPoint accessPoint) {
+ final IconCompat icon = IconCompat.createWithResource(mContext,
+ R.drawable.ic_settings_wireless);
+ final String title = mContext.getString(R.string.wifi_settings);
+ final CharSequence summary = getSummary(accessPoint);
+ final PendingIntent toggleAction = getBroadcastIntent(mContext);
+ final PendingIntent primaryAction = getPrimaryAction();
+ final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon,
+ ListBuilder.ICON_IMAGE, title);
+ final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
+ null /* actionTitle */, isWifiEnabled);
+
+ return new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
+ .setAccentColor(COLOR_NOT_TINTED)
+ .setKeywords(getKeywords())
+ .addRow(new ListBuilder.RowBuilder()
+ .setTitle(title)
+ .setSubtitle(summary)
+ .addEndItem(toggleSliceAction)
+ .setPrimaryAction(primarySliceAction));
+ }
+
private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) {
final CharSequence title = getAccessPointName(accessPoint);
final IconCompat levelIcon = getAccessPointLevelIcon(accessPoint);
@@ -166,7 +179,7 @@
}
private CharSequence getAccessPointName(AccessPoint accessPoint) {
- final CharSequence name = accessPoint.getConfigName();
+ final CharSequence name = accessPoint.getTitle();
final Spannable span = new SpannableString(name);
@ColorInt final int color = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorPrimary);
@@ -305,12 +318,46 @@
}
}
+ private CharSequence getSummary(AccessPoint accessPoint) {
+ if (accessPoint == null) {
+ return getSummary();
+ }
+
+ final NetworkInfo networkInfo = accessPoint.getNetworkInfo();
+ if (networkInfo == null) {
+ return getSummary();
+ }
+
+ final State state = networkInfo.getState();
+ DetailedState detailedState;
+ if (state == State.CONNECTING) {
+ detailedState = DetailedState.CONNECTING;
+ } else if (state == State.CONNECTED) {
+ detailedState = DetailedState.CONNECTED;
+ } else {
+ return getSummary();
+ }
+
+ final String[] formats = mContext.getResources().getStringArray(
+ R.array.wifi_status_with_ssid);
+ final int index = detailedState.ordinal();
+ return String.format(formats[index], accessPoint.getTitle());
+ }
+
private PendingIntent getPrimaryAction() {
final Intent intent = getIntent();
return PendingIntent.getActivity(mContext, 0 /* requestCode */,
intent, 0 /* flags */);
}
+ private Set<String> getKeywords() {
+ final String keywords = mContext.getString(R.string.keywords_wifi);
+ return Arrays.asList(TextUtils.split(keywords, ","))
+ .stream()
+ .map(String::trim)
+ .collect(Collectors.toSet());
+ }
+
@Override
public Class getBackgroundWorkerClass() {
return WifiScanWorker.class;
@@ -355,7 +402,6 @@
@Override
public void onConnectedChanged() {
- notifySliceChange();
}
@Override
@@ -370,10 +416,43 @@
final List<AccessPoint> resultList = new ArrayList<>();
for (AccessPoint ap : accessPoints) {
if (ap.isReachable()) {
- resultList.add(ap);
+ resultList.add(clone(ap));
+ if (resultList.size() >= DEFAULT_EXPANDED_ROW_COUNT) {
+ break;
+ }
}
}
updateResults(resultList);
}
+
+ private AccessPoint clone(AccessPoint accessPoint) {
+ final Bundle savedState = new Bundle();
+ accessPoint.saveWifiState(savedState);
+ return new AccessPoint(mContext, savedState);
+ }
+
+ @Override
+ protected boolean areListsTheSame(List<AccessPoint> a, List<AccessPoint> b) {
+ if (!a.equals(b)) {
+ return false;
+ }
+
+ // compare access point states one by one
+ final int listSize = a.size();
+ for (int i = 0; i < listSize; i++) {
+ if (getState(a.get(i)) != getState(b.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private State getState(AccessPoint accessPoint) {
+ final NetworkInfo networkInfo = accessPoint.getNetworkInfo();
+ if (networkInfo != null) {
+ return networkInfo.getState();
+ }
+ return null;
+ }
}
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
index 2ae6b58..8f6d489 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
@@ -43,9 +43,6 @@
implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
private static final String WIFI_TETHER_SETTINGS = "wifi_tether";
- private static final IntentFilter AIRPLANE_INTENT_FILTER = new IntentFilter(
- Intent.ACTION_AIRPLANE_MODE_CHANGED);
- private static final int ID_NULL = -1;
private final ConnectivityManager mConnectivityManager;
private final String[] mWifiRegexs;
@@ -103,8 +100,6 @@
@Override
public void onStart() {
if (mPreference != null) {
- mContext.registerReceiver(mReceiver, AIRPLANE_INTENT_FILTER);
- clearSummaryForAirplaneMode();
if (mWifiTetherSoftApManager != null) {
mWifiTetherSoftApManager.registerSoftApCallback();
}
@@ -114,7 +109,6 @@
@Override
public void onStop() {
if (mPreference != null) {
- mContext.unregisterReceiver(mReceiver);
if (mWifiTetherSoftApManager != null) {
mWifiTetherSoftApManager.unRegisterSoftApCallback();
}
@@ -146,19 +140,6 @@
});
}
- //
- // Everything below is copied from WifiApEnabler
- //
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
- clearSummaryForAirplaneMode(R.string.wifi_hotspot_off_subtext);
- }
- }
- };
-
@VisibleForTesting
void handleWifiApStateChanged(int state, int reason) {
switch (state) {
@@ -174,7 +155,6 @@
break;
case WifiManager.WIFI_AP_STATE_DISABLED:
mPreference.setSummary(R.string.wifi_hotspot_off_subtext);
- clearSummaryForAirplaneMode();
break;
default:
if (reason == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
@@ -182,7 +162,6 @@
} else {
mPreference.setSummary(R.string.wifi_error);
}
- clearSummaryForAirplaneMode();
}
}
@@ -194,21 +173,4 @@
BidiFormatter.getInstance().unicodeWrap(
(wifiConfig == null) ? s : wifiConfig.SSID)));
}
-
- private void clearSummaryForAirplaneMode() {
- clearSummaryForAirplaneMode(ID_NULL);
- }
-
- private void clearSummaryForAirplaneMode(int defaultId) {
- boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
- if (isAirplaneMode) {
- mPreference.setSummary(R.string.wifi_tether_disabled_by_airplane);
- } else if (defaultId != ID_NULL){
- mPreference.setSummary(defaultId);
- }
- }
- //
- // Everything above is copied from WifiApEnabler
- //
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
index eb6a123..b7ddcae 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
@@ -17,13 +17,18 @@
package com.android.settings.wifi.tether;
import android.content.Context;
+import android.content.Intent;
import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+import android.view.View;
import androidx.annotation.VisibleForTesting;
import androidx.preference.EditTextPreference;
import androidx.preference.Preference;
+import com.android.settings.R;
import com.android.settings.widget.ValidatedEditTextPreference;
+import com.android.settings.wifi.dpp.WifiDppUtils;
public class WifiTetherSSIDPreferenceController extends WifiTetherBasePreferenceController
implements ValidatedEditTextPreference.Validator {
@@ -56,6 +61,23 @@
mSSID = DEFAULT_SSID;
}
((ValidatedEditTextPreference) mPreference).setValidator(this);
+
+ if (mWifiManager.isWifiApEnabled() && config != null) {
+ final Intent intent = WifiDppUtils.getHotspotConfiguratorIntentOrNull(mContext,
+ mWifiManager, config);
+
+ if (intent == null) {
+ Log.e(TAG, "Invalid security to share hotspot");
+ ((WifiTetherSsidPreference) mPreference).setButtonVisible(false);
+ } else {
+ ((WifiTetherSsidPreference) mPreference).setButtonOnClickListener(
+ view -> shareHotspotNetwork(intent));
+ ((WifiTetherSsidPreference) mPreference).setButtonVisible(true);
+ }
+ } else {
+ ((WifiTetherSsidPreference) mPreference).setButtonVisible(false);
+ }
+
updateSsidDisplay((EditTextPreference) mPreference);
}
@@ -80,4 +102,13 @@
preference.setText(mSSID);
preference.setSummary(mSSID);
}
+
+ private void shareHotspotNetwork(Intent intent) {
+ WifiDppUtils.showLockScreen(mContext, () -> mContext.startActivity(intent));
+ }
+
+ @VisibleForTesting
+ boolean isQrCodeButtonAvailable() {
+ return ((WifiTetherSsidPreference) mPreference).isQrCodeButtonAvailable();
+ }
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSsidPreference.java b/src/com/android/settings/wifi/tether/WifiTetherSsidPreference.java
new file mode 100644
index 0000000..64014d9
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherSsidPreference.java
@@ -0,0 +1,115 @@
+/*
+ * 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.wifi.tether;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageButton;
+
+import androidx.annotation.DrawableRes;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.widget.ValidatedEditTextPreference;
+
+/**
+ * Support a QR code share button for {@code EditTextPreference} that supports input validation.
+ */
+public class WifiTetherSsidPreference extends ValidatedEditTextPreference {
+ private static final String TAG = "WifiTetherSsidPreference";
+
+ private ImageButton mImageButton;
+ private Drawable mButtonIcon;
+ private View.OnClickListener mClickListener;
+ private boolean mVisible;
+
+ public WifiTetherSsidPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ initialize();
+ }
+
+ public WifiTetherSsidPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ initialize();
+ }
+
+ public WifiTetherSsidPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ initialize();
+ }
+
+ public WifiTetherSsidPreference(Context context) {
+ super(context);
+
+ initialize();
+ }
+
+ private void initialize() {
+ setWidgetLayoutResource(R.layout.wifi_button_preference_widget);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ if (mImageButton == null) {
+ mImageButton = (ImageButton) holder.findViewById(R.id.button_icon);
+
+ mImageButton.setContentDescription(
+ getContext().getString(R.string.wifi_dpp_share_hotspot));
+ setButtonIcon(R.drawable.ic_qrcode_24dp);
+ mImageButton.setImageDrawable(mButtonIcon);
+ }
+
+ if (mVisible) {
+ mImageButton.setOnClickListener(mClickListener);
+ mImageButton.setVisibility(View.VISIBLE);
+ } else {
+ mImageButton.setVisibility(View.GONE);
+ }
+ }
+
+ public void setButtonOnClickListener(View.OnClickListener listener) {
+ mClickListener = listener;
+ }
+
+ public void setButtonVisible(boolean visible) {
+ mVisible = visible;
+ }
+
+ private void setButtonIcon(@DrawableRes int iconResId) {
+ try {
+ mButtonIcon = getContext().getDrawable(iconResId);
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ }
+
+ @VisibleForTesting
+ boolean isQrCodeButtonAvailable() {
+ return mVisible && mClickListener != null;
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
index 8d87b32..0f31d19 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@@ -61,7 +61,6 @@
static {
WIFI_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- WIFI_INTENT_FILTER.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
}
WifiTetherSwitchBarController(Context context, SwitchWidgetController switchBar) {
@@ -119,8 +118,6 @@
final int state = intent.getIntExtra(
WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
handleWifiApStateChanged(state);
- } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
- updateWifiSwitch();
}
}
};
@@ -154,13 +151,7 @@
}
private void updateWifiSwitch() {
- boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
- if (!isAirplaneMode) {
- mSwitchBar.setEnabled(!mDataSaverBackend.isDataSaverEnabled());
- } else {
- mSwitchBar.setEnabled(false);
- }
+ mSwitchBar.setEnabled(!mDataSaverBackend.isDataSaverEnabled());
}
@Override
diff --git a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
index ce03d95..675108d 100644
--- a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
+++ b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
@@ -6,6 +6,7 @@
com.android.settings.bluetooth.BluetoothDeviceNamePreferenceController
com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController
com.android.settings.datausage.DataUsageSummaryPreferenceController
+com.android.settings.datausage.WifiDataUsageSummaryPreferenceController
com.android.settings.fuelgauge.RestrictAppPreferenceController
com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController
com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 776a4d0..1fe4bbe 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -91,4 +91,7 @@
<!-- Email address for the homepage contextual cards feedback -->
<string name="config_contextual_card_feedback_email" translatable="false">test@test.test</string>
+
+ <!-- Grayscale settings intent -->
+ <string name="config_grayscale_settings_intent" translate="false">intent:#Intent;action=test.test;end</string>
</resources>
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index 37186ca..3d3aff4 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -28,6 +28,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActionBar;
+import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -51,15 +53,18 @@
import android.os.storage.VolumeInfo;
import android.util.IconDrawableFactory;
import android.widget.EditText;
+import android.widget.ScrollView;
import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.fragment.app.FragmentActivity;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@@ -259,4 +264,21 @@
public void isPackageEnabled_noApp_returnFalse() {
assertThat(Utils.isPackageEnabled(mContext, PACKAGE_NAME)).isFalse();
}
+
+ @Test
+ public void setActionBarShadowAnimation_nullParameters_shouldNotCrash() {
+ // no crash here
+ Utils.setActionBarShadowAnimation(null, null, null);
+ }
+
+ @Test
+ public void setActionBarShadowAnimation_shouldSetElevationToZero() {
+ final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
+ final ActionBar actionBar = activity.getActionBar();
+
+ Utils.setActionBarShadowAnimation(activity, activity.getLifecycle(),
+ new ScrollView(mContext));
+
+ assertThat(actionBar.getElevation()).isEqualTo(0.f);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
index 6a2348c..8c75e33 100644
--- a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.verify;
import android.accounts.Account;
+import android.app.ActivityManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -55,6 +56,7 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
import org.robolectric.shadows.ShadowContentResolver;
import org.robolectric.shadows.ShadowPackageManager;
@@ -101,6 +103,19 @@
}
@Test
+ public void onStart_lowRamDevice_doNothing() {
+ final AvatarViewMixin mixin = spy(new AvatarViewMixin(mActivity, mImageView));
+
+ final ShadowActivityManager activityManager =
+ Shadow.extract(mContext.getSystemService(ActivityManager.class));
+ activityManager.setIsLowRamDevice(true);
+
+ mixin.onStart();
+
+ verify(mixin, never()).hasAccount();
+ }
+
+ @Test
@Config(qualifiers = "mcc999",
shadows = {
BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java
index b8d0f20..c3ae904 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java
@@ -46,7 +46,7 @@
public void seUp() {
mContext = RuntimeEnvironment.application;
mView = ApplicationViewHolder.newView(new FrameLayout(mContext));
- mHolder = new ApplicationViewHolder(mView, false /* useStableHeight */);
+ mHolder = new ApplicationViewHolder(mView);
}
@Test
@@ -67,10 +67,6 @@
mHolder.setSummary(R.string.disabled);
assertThat(mHolder.mSummary.getText()).isEqualTo(mContext.getText(R.string.disabled));
- assertThat(mHolder.mSummaryContainer.getVisibility()).isEqualTo(View.VISIBLE);
-
- mHolder.setSummary(null);
- assertThat(mHolder.mSummaryContainer.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -104,7 +100,7 @@
@Test
public void twoTouchTarget() {
mView = ApplicationViewHolder.newView(new FrameLayout(mContext), true);
- mHolder = new ApplicationViewHolder(mView, false /* useStableHeight */);
+ mHolder = new ApplicationViewHolder(mView);
assertThat(mHolder.mSwitch).isNotNull();
assertThat(mHolder.mWidgetContainer.getChildCount()).isEqualTo(1);
}
@@ -112,8 +108,9 @@
@Test
public void updateSwitch() {
mView = ApplicationViewHolder.newView(new FrameLayout(mContext), true);
- mHolder = new ApplicationViewHolder(mView, false /* useStableHeight */);
- mHolder.updateSwitch(v -> {}, true, true);
+ mHolder = new ApplicationViewHolder(mView);
+ mHolder.updateSwitch(v -> {
+ } /* listener */, true, true);
assertThat(mHolder.mSwitch.isChecked()).isTrue();
assertThat(mHolder.mSwitch.isEnabled()).isTrue();
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
index 419fc49..27c3e7d 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
@@ -235,16 +235,6 @@
}
@Test
- public void shouldUseStableItemHeight() {
- assertThat(ManageApplications.ApplicationsAdapter.shouldUseStableItemHeight(
- LIST_TYPE_MAIN))
- .isTrue();
- assertThat(ManageApplications.ApplicationsAdapter.shouldUseStableItemHeight(
- LIST_TYPE_NOTIFICATION))
- .isTrue();
- }
-
- @Test
public void onRebuildComplete_shouldHideLoadingView() {
final Context context = RuntimeEnvironment.application;
final RecyclerView recyclerView = mock(RecyclerView.class);
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java
index 8f5b0ab..74fb98f 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java
@@ -64,7 +64,7 @@
mController = new MusicViewHolderController(mContext, mSource, fsUuid, new UserHandle(0));
View view = ApplicationViewHolder.newView(new FrameLayout(mContext));
- mHolder = new ApplicationViewHolder(view, false /* useStableHeight */);
+ mHolder = new ApplicationViewHolder(view);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java
index c453474..5475b9c 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java
@@ -63,7 +63,7 @@
mController = new PhotosViewHolderController(mContext, mSource, fsUuid, new UserHandle(0));
final View view = ApplicationViewHolder.newView(new FrameLayout(mContext));
- mHolder = new ApplicationViewHolder(view, false /* useStableHeight */);
+ mHolder = new ApplicationViewHolder(view);
}
@Test
@@ -71,19 +71,19 @@
mController.setupView(mHolder);
assertThat(mHolder.mSummary.getText().toString())
- .isEqualTo(Formatter.formatFileSize(mContext, 0));
+ .isEqualTo(Formatter.formatFileSize(mContext, 0));
}
@Test
public void storageShouldRepresentStorageStatsQuery() throws Exception {
when(mSource.getExternalStorageStats(nullable(String.class), nullable(UserHandle.class)))
- .thenReturn(new StorageStatsSource.ExternalStorageStats(1, 0, 1, 10, 0));
+ .thenReturn(new StorageStatsSource.ExternalStorageStats(1, 0, 1, 10, 0));
mController.queryStats();
mController.setupView(mHolder);
assertThat(mHolder.mSummary.getText().toString())
- .isEqualTo(Formatter.formatFileSize(mContext, 11));
+ .isEqualTo(Formatter.formatFileSize(mContext, 11));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/ZenAccessControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/ZenAccessControllerTest.java
deleted file mode 100644
index 5bce86c..0000000
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/ZenAccessControllerTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.specialaccess;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowActivityManager;
-
-@RunWith(RobolectricTestRunner.class)
-public class ZenAccessControllerTest {
-
- private Context mContext;
- private ZenAccessController mController;
- private ShadowActivityManager mActivityManager;
-
- @Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
- mController = new ZenAccessController(mContext, "key");
- mActivityManager = Shadow.extract(mContext.getSystemService(Context.ACTIVITY_SERVICE));
- }
-
- @Test
- public void isAvailable_byDefault_true() {
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Test
- public void isAvailable_lowMemory_false() {
- mActivityManager.setIsLowRamDevice(true);
- assertThat(mController.isAvailable()).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
new file mode 100644
index 0000000..6041e9d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.specialaccess.zenaccess;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.NotificationManager;
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowNotificationManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenAccessControllerTest {
+
+ private static final String TEST_PKG = "com.test.package";
+
+ private FakeFeatureFactory mFeatureFactory;
+ private Context mContext;
+ private ZenAccessController mController;
+ private ShadowActivityManager mActivityManager;
+
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mController = new ZenAccessController(mContext, "key");
+ mActivityManager = Shadow.extract(mContext.getSystemService(Context.ACTIVITY_SERVICE));
+ }
+
+ @Test
+ public void isAvailable_byDefault_true() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_lowMemory_false() {
+ mActivityManager.setIsLowRamDevice(true);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ ZenAccessController.logSpecialPermissionChange(true, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_ALLOW),
+ eq("app"));
+
+ ZenAccessController.logSpecialPermissionChange(false, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_DENY),
+ eq("app"));
+ }
+
+ @Test
+ @Config(shadows = ShadowNotificationManager.class)
+ public void hasAccess_granted_yes() {
+ final ShadowNotificationManager snm = Shadow.extract(mContext.getSystemService(
+ NotificationManager.class));
+ snm.setNotificationPolicyAccessGrantedForPackage(TEST_PKG);
+ assertThat(ZenAccessController.hasAccess(mContext, TEST_PKG)).isTrue();
+ }
+
+ @Test
+ @Config(shadows = ShadowNotificationManager.class)
+ public void hasAccess_notGranted_no() {
+ assertThat(ZenAccessController.hasAccess(mContext, TEST_PKG)).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
new file mode 100644
index 0000000..cba1a51
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.applications.specialaccess.zenaccess;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenAccessSettingObserverMixinTest {
+
+ @Mock
+ private ZenAccessSettingObserverMixin.Listener mListener;
+
+ private Context mContext;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private ZenAccessSettingObserverMixin mMixin;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+
+ mMixin = new ZenAccessSettingObserverMixin(mContext, mListener);
+
+ mLifecycle.addObserver(mMixin);
+ }
+
+ @Test
+ public void onStart_lowMemory_shouldNotRegisterListener() {
+ final ShadowActivityManager sam = Shadow.extract(
+ mContext.getSystemService(ActivityManager.class));
+ sam.setIsLowRamDevice(true);
+
+ mLifecycle.handleLifecycleEvent(ON_START);
+
+ mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+
+ verify(mListener, never()).onZenAccessPolicyChanged();
+ }
+
+ @Test
+ public void onStart_highMemory_shouldRegisterListener() {
+ final ShadowActivityManager sam = Shadow.extract(
+ mContext.getSystemService(ActivityManager.class));
+ sam.setIsLowRamDevice(false);
+
+ mLifecycle.handleLifecycleEvent(ON_START);
+
+ mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+
+ verify(mListener).onZenAccessPolicyChanged();
+ }
+
+ @Test
+ public void onStop_shouldUnregisterListener() {
+ final ShadowActivityManager sam = Shadow.extract(
+ mContext.getSystemService(ActivityManager.class));
+ sam.setIsLowRamDevice(false);
+
+ mLifecycle.handleLifecycleEvent(ON_START);
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+
+ mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+
+ verify(mListener, never()).onZenAccessPolicyChanged();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java b/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
index 5587783..b6ef32c 100644
--- a/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
+++ b/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
@@ -256,20 +256,21 @@
@Test
public void testGetLabelBackupTransport() throws Exception {
- String label = "test_label";
+ CharSequence label = "test_label";
- when(mBackupManager.getDataManagementLabel(anyString())).thenReturn(label);
+ when(mBackupManager.getDataManagementLabelForUser(anyInt(), anyString())).thenReturn(label);
- String backupLabel = mBackupSettingsHelper.getLabelFromBackupTransport();
+ CharSequence backupLabel = mBackupSettingsHelper.getLabelFromBackupTransport();
assertThat(backupLabel).isEqualTo(label);
}
@Test
public void testGetLabelBackupTransport_RemoteException() throws Exception {
- when(mBackupManager.getDataManagementLabel(anyString())).thenThrow(new RemoteException());
+ when(mBackupManager.getDataManagementLabelForUser(anyInt(), anyString()))
+ .thenThrow(new RemoteException());
- String backupLabel = mBackupSettingsHelper.getLabelFromBackupTransport();
+ CharSequence backupLabel = mBackupSettingsHelper.getLabelFromBackupTransport();
assertThat(backupLabel).isNull();
}
@@ -291,31 +292,31 @@
@Test
public void testGetLabelForBackupSettings_WithLabelFromTransport() throws Exception {
- String label = "test_label";
+ CharSequence label = "test_label";
- when(mBackupManager.getDataManagementLabel(anyString())).thenReturn(label);
+ when(mBackupManager.getDataManagementLabelForUser(anyInt(), anyString())).thenReturn(label);
- String backupLabel = mBackupSettingsHelper.getLabelForBackupSettings();
+ CharSequence backupLabel = mBackupSettingsHelper.getLabelForBackupSettings();
assertThat(backupLabel).isEqualTo(label);
}
@Test
public void testGetLabelForBackupSettings_WithEmptyLabelFromTransport() throws Exception {
- String label = "";
+ CharSequence label = "";
- when(mBackupManager.getDataManagementLabel(anyString())).thenReturn(label);
+ when(mBackupManager.getDataManagementLabelForUser(anyInt(), anyString())).thenReturn(label);
- String backupLabel = mBackupSettingsHelper.getLabelForBackupSettings();
+ CharSequence backupLabel = mBackupSettingsHelper.getLabelForBackupSettings();
assertThat(backupLabel).isEqualTo(mContext.getString(DEFAULT_LABEL_RESOURCE));
}
@Test
public void testGetLabelForBackupSettings_WithoutLabelFromTransport() throws Exception {
- when(mBackupManager.getDataManagementLabel(anyString())).thenReturn(null);
+ when(mBackupManager.getDataManagementLabelForUser(anyInt(), anyString())).thenReturn(null);
- String backupLabel = mBackupSettingsHelper.getLabelForBackupSettings();
+ CharSequence backupLabel = mBackupSettingsHelper.getLabelForBackupSettings();
assertThat(backupLabel).isEqualTo(mContext.getString(DEFAULT_LABEL_RESOURCE));
}
diff --git a/tests/robotests/src/com/android/settings/backup/DataManagementPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/backup/DataManagementPreferenceControllerTest.java
index ac92fb7..d3688d3 100644
--- a/tests/robotests/src/com/android/settings/backup/DataManagementPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/backup/DataManagementPreferenceControllerTest.java
@@ -43,7 +43,7 @@
private DataManagementPreferenceController mController;
private PrivacySettingsConfigData mPSCD;
private Preference mPreference;
- private String mTitle;
+ private CharSequence mTitle;
@Mock
private Intent mIntent;
diff --git a/tests/robotests/src/com/android/settings/core/HideNonSystemOverlayMixinTest.java b/tests/robotests/src/com/android/settings/core/HideNonSystemOverlayMixinTest.java
new file mode 100644
index 0000000..579cba0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/core/HideNonSystemOverlayMixinTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.core;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.android.controller.ActivityController;
+
+@RunWith(RobolectricTestRunner.class)
+public class HideNonSystemOverlayMixinTest {
+
+ private ActivityController<TestActivity> mActivityController;
+
+ @Before
+ public void setUp() {
+ RuntimeEnvironment.application.setTheme(R.style.Theme_AppCompat);
+ mActivityController = Robolectric.buildActivity(TestActivity.class);
+ }
+
+ @Test
+ public void startActivity_shouldHideNonSystemOverlay() {
+ mActivityController.setup();
+ TestActivity activity = mActivityController.get();
+
+ // Activity start: HIDE_NON_SYSTEM_OVERLAY should be set.
+ final WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
+ assertThat(attrs.privateFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+ .isNotEqualTo(0);
+ }
+
+ @Test
+ public void stopActivity_shouldUnhideNonSystemOverlay() {
+ mActivityController.setup().stop();
+ TestActivity activity = mActivityController.get();
+
+ final WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
+ assertThat(attrs.privateFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+ .isEqualTo(0);
+ }
+
+ public static class TestActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
index 4347637..eab3dde 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -164,7 +164,8 @@
verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
CARRIER_NAME, 1 /* numPlans */, intent);
verify(mSummaryPreference).setChartEnabled(true);
- verify(mSummaryPreference).setWifiMode(false, null);
+ verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
+ false /* isSingleWifi */);
}
@Test
@@ -188,7 +189,8 @@
verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
CARRIER_NAME, 0 /* numPlans */, intent);
verify(mSummaryPreference).setChartEnabled(true);
- verify(mSummaryPreference).setWifiMode(false, null);
+ verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
+ false /* isSingleWifi */);
}
@Test
@@ -214,7 +216,8 @@
0 /* numPlans */,
null /* launchIntent */);
verify(mSummaryPreference).setChartEnabled(true);
- verify(mSummaryPreference).setWifiMode(false, null);
+ verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
+ false /* isSingleWifi */);
}
@Test
@@ -240,7 +243,8 @@
0 /* numPlans */,
null /* launchIntent */);
verify(mSummaryPreference).setChartEnabled(false);
- verify(mSummaryPreference).setWifiMode(false, null);
+ verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
+ false /* isSingleWifi */);
}
@Test
@@ -321,7 +325,8 @@
verify(mSummaryPreference).setLimitInfo(captor.capture());
CharSequence value = captor.getValue();
assertThat(value.toString()).isEqualTo("1.00 MB data warning / 1.00 MB data limit");
- verify(mSummaryPreference).setWifiMode(false, null);
+ verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
+ false /* isSingleWifi */);
}
@Test
@@ -340,7 +345,8 @@
when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_ABSENT);
mController.updateState(mSummaryPreference);
- verify(mSummaryPreference).setWifiMode(true, info.period);
+ verify(mSummaryPreference).setWifiMode(true /* isWifiMode */, info.period /* usagePeriod */,
+ false /* isSingleWifi */);
verify(mSummaryPreference).setLimitInfo(null);
verify(mSummaryPreference).setUsageNumbers(info.usageLevel, -1L, true);
verify(mSummaryPreference).setChartEnabled(false);
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
index 525b82e..35e6b1d 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
@@ -488,7 +488,7 @@
new Intent());
mSummaryPreference.setUsageNumbers(1000000L, -1L, true);
final String cycleText = "The quick fox";
- mSummaryPreference.setWifiMode(true, cycleText);
+ mSummaryPreference.setWifiMode(true /* isWifiMode */, cycleText, false /* isSingleWifi */);
doReturn(200L).when(mSummaryPreference).getHistoricalUsageLevel();
bindViewHolder();
@@ -524,7 +524,8 @@
@Test
public void testSetWifiMode_noUsageInfo_shouldDisableLaunchButton() {
mSummaryPreference = spy(mSummaryPreference);
- mSummaryPreference.setWifiMode(true, "Test cycle text");
+ mSummaryPreference.setWifiMode(true /* isWifiMode */, "Test cycle text",
+ false /* isSingleWifi */);
doReturn(0L).when(mSummaryPreference).getHistoricalUsageLevel();
bindViewHolder();
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
index 8fae45c..18ddd8a 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
@@ -16,18 +16,14 @@
package com.android.settings.deviceinfo;
-import static android.content.Context.CLIPBOARD_SERVICE;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.Activity;
-import android.content.ClipboardManager;
import android.content.Context;
import android.os.Process;
import android.os.UserManager;
@@ -35,7 +31,6 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.InstrumentedPreferenceFragment;
@@ -62,13 +57,8 @@
public class BuildNumberPreferenceControllerTest {
private static final String KEY_BUILD_NUMBER = "build_number";
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Activity mActivity;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private InstrumentedPreferenceFragment mFragment;
- @Mock(answer = RETURNS_DEEP_STUBS)
- private PreferenceScreen mScreen;
private ShadowUserManager mShadowUserManager;
@@ -200,14 +190,4 @@
assertThat(activityResultHandled).isTrue();
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
}
-
- @Test
- public void copy_shouldCopyBuildNumberToClipboard() {
- mController.copy();
-
- final ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(
- CLIPBOARD_SERVICE);
- final CharSequence data = clipboard.getPrimaryClip().getItemAt(0).getText();
- assertThat(data.toString()).isEqualTo(mController.getSummary());
- }
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceControllerTest.java
new file mode 100644
index 0000000..3784f08
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceControllerTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.deviceinfo.firmwareversion;
+
+import static android.content.Context.CLIPBOARD_SERVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ClipboardManager;
+import android.content.Context;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SimpleBuildNumberPreferenceControllerTest {
+
+ private SimpleBuildNumberPreferenceController mController;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mController = new SimpleBuildNumberPreferenceController(mContext, "test");
+ }
+
+ @Test
+ public void copy_shouldCopyBuildNumberToClipboard() {
+ mController.copy();
+
+ final ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(
+ CLIPBOARD_SERVICE);
+ final CharSequence data = clipboard.getPrimaryClip().getItemAt(0).getText();
+ assertThat(data.toString()).isEqualTo(mController.getSummary());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java
index 7999dc8..3509330 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java
@@ -94,13 +94,13 @@
public void setChecked_on_setPowerSaveMode() {
mController.setChecked(true);
- verify(mPowerManager).setPowerSaveMode(true);
+ verify(mPowerManager).setPowerSaveModeEnabled(true);
}
@Test
public void setChecked_off_unsetPowerSaveMode() {
mController.setChecked(false);
- verify(mPowerManager).setPowerSaveMode(false);
+ verify(mPowerManager).setPowerSaveModeEnabled(false);
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java
index d1d21f3..a6c8534 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java
@@ -62,7 +62,7 @@
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
Settings.Global.putInt(mContext.getContentResolver(),
- Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
mController.updateState(mPreference);
@@ -74,7 +74,7 @@
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL);
Settings.Global.putInt(mContext.getContentResolver(),
- Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
mController.updateState(mPreference);
@@ -87,7 +87,7 @@
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL);
Settings.Global.putInt(mContext.getContentResolver(),
- Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_DYNAMIC);
+ Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC);
mController.updateState(mPreference);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java
index 6d412ee..6012dbb 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java
@@ -31,16 +31,16 @@
@Test
public void getDefaultKey_routine_returnsCorrectValue() {
- Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_DYNAMIC);
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC);
assertThat(mController.getDefaultKey())
.isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_ROUTINE);
}
@Test
public void getDefaultKey_automatic_returnsCorrectValue() {
- Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
assertThat(mController.getDefaultKey())
.isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_PERCENTAGE);
@@ -48,8 +48,8 @@
@Test
public void getDefaultKey_none_returnsCorrectValue() {
- Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
assertThat(mController.getDefaultKey())
.isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_NO_SCHEDULE);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java
index 8aac9d8..03c9b0a 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java
@@ -39,16 +39,16 @@
@Test
public void updateSeekBar_routineMode_hasCorrectProperties() {
- Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_DYNAMIC);
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC);
mController.updateSeekBar();
assertThat(mController.mSeekBarPreference.isVisible()).isFalse();
}
@Test
public void updateSeekBar_percentageMode_hasCorrectProperties() {
- Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5);
mController.updateSeekBar();
assertThat(mController.mSeekBarPreference.isVisible()).isTrue();
@@ -56,8 +56,8 @@
@Test
public void updateSeekBar_noneMode_hasCorrectProperties() {
- Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVER_MODE,
- PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
mController.updateSeekBar();
assertThat(mController.mSeekBarPreference.isVisible()).isFalse();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceControllerTest.java
index a441864..0ee9cfc 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverStickyPreferenceControllerTest.java
@@ -42,7 +42,7 @@
}
private int getAutoDisableSetting() {
- return Settings.System.getInt(mContext.getContentResolver(),
+ return Settings.Global.getInt(mContext.getContentResolver(),
LOW_POWER_STICKY_AUTO_DISABLE_ENABLED,
1);
}
diff --git a/tests/robotests/src/com/android/settings/gestures/WakeScreenGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/WakeScreenGesturePreferenceControllerTest.java
deleted file mode 100644
index 471914a..0000000
--- a/tests/robotests/src/com/android/settings/gestures/WakeScreenGesturePreferenceControllerTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.gestures;
-
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
-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.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.hardware.display.AmbientDisplayConfiguration;
-
-import com.android.settings.aware.AwareFeatureProvider;
-import com.android.settings.testutils.FakeFeatureFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class WakeScreenGesturePreferenceControllerTest {
-
- private static final String KEY_WAKE_SCREEN = "gesture_wake_screen";
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Context mContext;
- @Mock
- private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
- private WakeScreenGesturePreferenceController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- AwareFeatureProvider featureProvider =
- FakeFeatureFactory.setupForTest().getAwareFeatureProvider();
- when(featureProvider.isSupported(any())).thenReturn(true);
- when(featureProvider.isEnabled(any())).thenReturn(true);
- mController = new WakeScreenGesturePreferenceController(mContext, KEY_WAKE_SCREEN);
- mController.setConfig(mAmbientDisplayConfiguration);
- }
-
- @Test
- public void testIsChecked_configIsSet_shouldReturnTrue() {
- // Set the setting to be enabled.
- when(mAmbientDisplayConfiguration.wakeScreenGestureEnabled(anyInt())).thenReturn(true);
- assertThat(mController.isChecked()).isTrue();
- }
-
- @Test
- public void testIsChecked_configIsNotSet_shouldReturnFalse() {
- // Set the setting to be disabled.
- when(mAmbientDisplayConfiguration.wakeScreenGestureEnabled(anyInt())).thenReturn(false);
- assertThat(mController.isChecked()).isFalse();
- }
-
- @Test
- public void getAvailabilityStatus_gestureNotSupported_UNSUPPORTED_ON_DEVICE() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- when(mAmbientDisplayConfiguration.wakeScreenGestureAvailable()).thenReturn(false);
- final int availabilityStatus = mController.getAvailabilityStatus();
-
- assertThat(availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE);
- }
-
- @Test
- public void getAvailabilityStatus_gestureSupported_AVAILABLE() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- when(mAmbientDisplayConfiguration.wakeScreenGestureAvailable()).thenReturn(true);
- final int availabilityStatus = mController.getAvailabilityStatus();
-
- assertThat(availabilityStatus).isEqualTo(AVAILABLE);
- }
-
- @Test
- public void getAvailabilityStatus_gestureSupported_DISABLED_DEPENDENT_SETTING() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(false);
- when(mAmbientDisplayConfiguration.wakeScreenGestureAvailable()).thenReturn(true);
- final int availabilityStatus = mController.getAvailabilityStatus();
-
- assertThat(availabilityStatus).isEqualTo(DISABLED_DEPENDENT_SETTING);
- }
-
- @Test
- public void canHandleClicks_onlyWhenAlwaysOn() {
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(false);
- assertThat(mController.canHandleClicks()).isEqualTo(false);
-
- reset(mAmbientDisplayConfiguration);
- when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- assertThat(mController.canHandleClicks()).isEqualTo(true);
- }
-
- @Test
- public void isSliceableCorrectKey_returnsTrue() {
- final WakeScreenGesturePreferenceController controller =
- new WakeScreenGesturePreferenceController(mContext, "gesture_wake_screen");
- assertThat(controller.isSliceable()).isTrue();
- }
-
- @Test
- public void isSliceableIncorrectKey_returnsFalse() {
- final WakeScreenGesturePreferenceController controller =
- new WakeScreenGesturePreferenceController(mContext, "bad_key");
- assertThat(controller.isSliceable()).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/gestures/WakeScreenGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/WakeScreenGestureSettingsTest.java
deleted file mode 100644
index f1d2fcc..0000000
--- a/tests/robotests/src/com/android/settings/gestures/WakeScreenGestureSettingsTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.gestures;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.provider.SearchIndexableResource;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class WakeScreenGestureSettingsTest {
-
- private WakeScreenGestureSettings mSettings;
-
- @Before
- public void setUp() {
- mSettings = new WakeScreenGestureSettings();
- }
-
- @Test
- public void testSearchIndexProvider_shouldIndexResource() {
- final List<SearchIndexableResource> indexRes =
- WakeScreenGestureSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(
- RuntimeEnvironment.application, true /* enabled */);
-
- assertThat(indexRes).isNotNull();
- assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId());
- }
-}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
index 2db7f3f..0683bd8 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
@@ -57,7 +57,7 @@
final Intent intent = new Intent();
mImpl.sendBroadcast(intent);
- verify(mContext, never()).sendBroadcastAsUser(intent, UserHandle.ALL);
+ verify(mContext, never()).sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
@Test
@@ -66,7 +66,7 @@
final Intent intent = new Intent();
mImpl.sendBroadcast(intent);
- verify(mContext).sendBroadcastAsUser(intent, UserHandle.ALL);
+ verify(mContext).sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
index 8b04ef3..40e6939 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
@@ -22,7 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -87,6 +87,19 @@
}
@Test
+ public void isCardEligibleToDisplay_invalidRankingScore_returnFalse() {
+ final ContextualCard card = new ContextualCard.Builder()
+ .setName("test_card")
+ .setCardType(ContextualCard.CardType.SLICE)
+ .setSliceUri(CustomSliceRegistry.FLASHLIGHT_SLICE_URI)
+ .setRankingScore(-1)
+ .build();
+
+ assertThat(mEligibleCardChecker.isCardEligibleToDisplay(card))
+ .isFalse();
+ }
+
+ @Test
public void isCardEligibleToDisplay_nullSlice_returnFalse() {
doReturn(null).when(mEligibleCardChecker).bindSlice(Uri.parse(TEST_SLICE_URI));
@@ -108,7 +121,7 @@
public void getDisplayableCards_twoEligibleCards_shouldShowAll() {
final List<ContextualCard> cards = getContextualCardList().stream().limit(2)
.collect(Collectors.toList());
- doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(cards);
@@ -118,7 +131,7 @@
@Test
public void getDisplayableCards_fiveEligibleCardsNoLarge_shouldShowDefaultCardCount() {
final List<ContextualCard> fiveCards = getContextualCardListWithNoLargeCard();
- doReturn(fiveCards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(fiveCards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(
fiveCards);
@@ -136,7 +149,7 @@
.setSliceUri(Uri.parse(
"content://com.android.settings.test.slices/action/gesture_pick_up"))
.build());
- doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(cards);
@@ -147,7 +160,7 @@
public void getDisplayableCards_threeEligibleCardsTwoLarge_shouldShowTwoCards() {
final List<ContextualCard> threeCards = getContextualCardList().stream().limit(3)
.collect(Collectors.toList());
- doReturn(threeCards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(threeCards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(
threeCards);
@@ -167,25 +180,26 @@
public void getDisplayableCards_refreshCardUri_shouldLogContextualCardDisplay() {
mContextualCardLoader.mNotifyUri = CardContentProvider.REFRESH_CARD_URI;
- mContextualCardLoader.getDisplayableCards(new ArrayList<ContextualCard>());
+ mContextualCardLoader.getDisplayableCards(new ArrayList<>());
verify(mFakeFeatureFactory.mContextualCardFeatureProvider).logContextualCardDisplay(
- any(List.class), any(List.class));
+ anyList(), anyList());
}
@Test
public void getDisplayableCards_deleteCardUri_shouldNotLogContextualCardDisplay() {
mContextualCardLoader.mNotifyUri = CardContentProvider.DELETE_CARD_URI;
- mContextualCardLoader.getDisplayableCards(new ArrayList<ContextualCard>());
+ mContextualCardLoader.getDisplayableCards(new ArrayList<>());
verify(mFakeFeatureFactory.mContextualCardFeatureProvider, never())
- .logContextualCardDisplay(any(List.class), any(List.class));
+ .logContextualCardDisplay(anyList(), anyList());
}
private ContextualCard getContextualCard(String sliceUri) {
return new ContextualCard.Builder()
.setName("test_card")
+ .setRankingScore(0.5)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(Uri.parse(sliceUri))
.build();
@@ -215,11 +229,6 @@
.setSliceUri(Uri.parse(
"content://com.android.settings.test.slices/action/gesture_pick_up"))
.build());
- cards.add(new ContextualCard.Builder()
- .setName("test_battery")
- .setCardType(ContextualCard.CardType.SLICE)
- .setSliceUri(CustomSliceRegistry.BATTERY_INFO_SLICE_URI)
- .build());
return cards;
}
@@ -248,11 +257,6 @@
.setSliceUri(Uri.parse(
"content://com.android.settings.test.slices/action/gesture_pick_up"))
.build());
- cards.add(new ContextualCard.Builder()
- .setName("test_battery")
- .setCardType(ContextualCard.CardType.SLICE)
- .setSliceUri(CustomSliceRegistry.BATTERY_INFO_SLICE_URI)
- .build());
return cards;
}
}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
index eb9a461..8087716 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
@@ -460,7 +460,7 @@
cards.add(new ContextualCard.Builder()
.setName("test_battery")
.setCardType(ContextualCard.CardType.SLICE)
- .setSliceUri(CustomSliceRegistry.BATTERY_INFO_SLICE_URI)
+ .setSliceUri(CustomSliceRegistry.BATTERY_FIX_SLICE_URI)
.setViewType(VIEW_TYPE_FULL_WIDTH)
.build());
return cards;
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionControllerTest.java
new file mode 100644
index 0000000..8c24735
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionControllerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.conditional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class GrayscaleConditionControllerTest {
+
+ @Mock
+ private ConditionManager mConditionManager;
+
+ private ColorDisplayManager mColorDisplayManager;
+ private Context mContext;
+ private GrayscaleConditionController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mColorDisplayManager = spy(mContext.getSystemService(ColorDisplayManager.class));
+ doReturn(mColorDisplayManager).when(mContext).getSystemService(ColorDisplayManager.class);
+ mController = new GrayscaleConditionController(mContext, mConditionManager);
+ }
+
+ @Test
+ public void isDisplayable_noIntent_shouldReturnFalse() {
+ assertThat(mController.isDisplayable()).isFalse();
+ }
+
+ @Test
+ @Config(qualifiers = "mcc999")
+ public void isDisplayable_validIntentAndGrayscaleOn_shouldReturnTrue() {
+ doReturn(true).when(mColorDisplayManager).isSaturationActivated();
+
+ assertThat(mController.isDisplayable()).isTrue();
+ }
+
+ @Test
+ @Config(qualifiers = "mcc999")
+ public void isDisplayable_validIntentAndGrayscaleOff_shouldReturnFalse() {
+ doReturn(false).when(mColorDisplayManager).isSaturationActivated();
+
+ assertThat(mController.isDisplayable()).isFalse();
+ }
+
+ @Test
+ public void onActionClick_shouldRefreshCondition() {
+ mController.onActionClick();
+
+ verify(mConditionManager).onConditionChanged();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSliceTest.java
deleted file mode 100644
index ff276d6..0000000
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSliceTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.deviceinfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-
-import android.content.Context;
-
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.slice.Slice;
-import androidx.slice.SliceMetadata;
-import androidx.slice.SliceProvider;
-import androidx.slice.core.SliceAction;
-import androidx.slice.widget.SliceLiveData;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class BatteryInfoSliceTest {
-
- private Context mContext;
- private BatteryInfoSlice mBatteryInfoSlice;
-
- @Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
-
- // Set-up specs for SliceMetadata.
- SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
-
- mBatteryInfoSlice = spy(new BatteryInfoSlice(mContext));
- }
-
- @Test
- public void getSlice_shouldBeCorrectSliceContent() {
- doNothing().when(mBatteryInfoSlice).loadBatteryInfo();
- doReturn("10%").when(mBatteryInfoSlice).getBatteryPercentString();
- doReturn("test").when(mBatteryInfoSlice).getSummary();
-
- final Slice slice = mBatteryInfoSlice.getSlice();
-
- final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
- assertThat(metadata.getTitle()).isEqualTo(
- mContext.getString(R.string.power_usage_summary_title));
-
- final SliceAction primaryAction = metadata.getPrimaryAction();
- final IconCompat expectedIcon = IconCompat.createWithResource(mContext,
- R.drawable.ic_settings_battery);
- assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedIcon.toString());
- }
-}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSliceTest.java
new file mode 100644
index 0000000..f2b87be
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSliceTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.content.Context;
+import android.net.Uri;
+
+import com.android.settings.R;
+import com.android.settings.slices.CustomSliceRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ContextualNotificationChannelSliceTest {
+
+ private Context mContext;
+ private ContextualNotificationChannelSlice mNotificationChannelSlice;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mNotificationChannelSlice = new ContextualNotificationChannelSlice(mContext);
+ }
+
+ @Test
+ public void getUri_shouldBeContextualNotificationChannelSliceUri() {
+ final Uri uri = mNotificationChannelSlice.getUri();
+
+ assertThat(uri).isEqualTo(CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI);
+ }
+
+ @Test
+ public void getSubTitle_shouldBeRecentlyInstalledApp() {
+ final CharSequence subTitle = mNotificationChannelSlice.getSubTitle("com.test.package", 0);
+
+ assertThat(subTitle).isEqualTo(mContext.getText(R.string.recently_installed_app));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java
new file mode 100644
index 0000000..ab3f4de
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.media;
+
+import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceMetadata;
+import androidx.slice.SliceProvider;
+import androidx.slice.core.SliceAction;
+import androidx.slice.widget.SliceLiveData;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.media.MediaOutputSliceConstants;
+
+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 java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class MediaOutputIndicatorSliceTest {
+
+ private static final String TEST_DEVICE_NAME = "test_device_name";
+ private static final int TEST_DEVICE_1_ICON =
+ com.android.internal.R.drawable.ic_bt_headphones_a2dp;
+
+ @Mock
+ private LocalMediaManager mLocalMediaManager;
+
+ private final List<MediaDevice> mDevices = new ArrayList<>();
+
+ private Context mContext;
+ private MediaOutputIndicatorSlice mMediaOutputIndicatorSlice;
+ private MediaOutputIndicatorWorker mMediaOutputIndicatorWorker;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+
+ // Set-up specs for SliceMetadata.
+ SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+
+ mMediaOutputIndicatorSlice = new MediaOutputIndicatorSlice(mContext);
+ mMediaOutputIndicatorWorker = spy(new MediaOutputIndicatorWorker(
+ mContext, MEDIA_OUTPUT_INDICATOR_SLICE_URI));
+ mMediaOutputIndicatorSlice.mWorker = mMediaOutputIndicatorWorker;
+ }
+
+ @Test
+ public void getSlice_invisible_returnNull() {
+ when(mMediaOutputIndicatorWorker.isVisible()).thenReturn(false);
+
+ assertThat(mMediaOutputIndicatorSlice.getSlice()).isNull();
+ }
+
+ @Test
+ public void getSlice_withActiveDevice_checkContent() {
+ when(mMediaOutputIndicatorWorker.isVisible()).thenReturn(true);
+ when(mMediaOutputIndicatorWorker.findActiveDeviceName()).thenReturn(TEST_DEVICE_NAME);
+ final Slice mediaSlice = mMediaOutputIndicatorSlice.getSlice();
+ final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
+ // Verify slice title and subtitle
+ assertThat(metadata.getTitle()).isEqualTo(mContext.getText(R.string.media_output_title));
+ assertThat(metadata.getSubtitle()).isEqualTo(TEST_DEVICE_NAME);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java
new file mode 100644
index 0000000..4a5662e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorWorkerTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.settings.R;
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothEventManager;
+import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+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.shadows.ShadowBluetoothDevice;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class,
+ ShadowBluetoothDevice.class})
+public class MediaOutputIndicatorWorkerTest {
+
+ private static final String TEST_A2DP_DEVICE_NAME = "Test_A2DP_BT_Device_NAME";
+ private static final String TEST_HAP_DEVICE_NAME = "Test_HAP_BT_Device_NAME";
+ private static final String TEST_A2DP_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
+ private static final String TEST_HAP_DEVICE_ADDRESS = "00:B2:B2:B2:B2:B2";
+ private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
+
+ @Mock
+ private A2dpProfile mA2dpProfile;
+ @Mock
+ private HearingAidProfile mHearingAidProfile;
+ @Mock
+ private LocalBluetoothManager mLocalManager;
+ @Mock
+ private BluetoothEventManager mBluetoothEventManager;
+ @Mock
+ private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
+
+ private BluetoothAdapter mBluetoothAdapter;
+ private BluetoothDevice mA2dpDevice;
+ private BluetoothDevice mHapDevice;
+ private BluetoothManager mBluetoothManager;
+ private Context mContext;
+ private List<BluetoothDevice> mDevicesList;
+ private LocalBluetoothManager mLocalBluetoothManager;
+ private MediaOutputIndicatorWorker mMediaDeviceUpdateWorker;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ ShadowBluetoothUtils.sLocalBluetoothManager = mLocalManager;
+ mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+ when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
+ when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+ when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+ mBluetoothManager = new BluetoothManager(mContext);
+ mBluetoothAdapter = mBluetoothManager.getAdapter();
+
+ // Setup A2dp device
+ mA2dpDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_A2DP_DEVICE_ADDRESS));
+ when(mA2dpDevice.getName()).thenReturn(TEST_A2DP_DEVICE_NAME);
+ when(mA2dpDevice.isConnected()).thenReturn(true);
+ // Setup HearingAid device
+ mHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_HAP_DEVICE_ADDRESS));
+ when(mHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME);
+ when(mHapDevice.isConnected()).thenReturn(true);
+
+ mMediaDeviceUpdateWorker = new MediaOutputIndicatorWorker(mContext, URI);
+ mDevicesList = new ArrayList<>();
+ }
+
+ @Test
+ public void isVisible_noConnectableDevice_returnFalse() {
+ mDevicesList.clear();
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mDevicesList);
+
+ assertThat(mMediaDeviceUpdateWorker.isVisible()).isFalse();
+ }
+
+ @Test
+ public void isVisible_withConnectableA2dpDevice_returnTrue() {
+ mDevicesList.clear();
+ mDevicesList.add(mA2dpDevice);
+ when(mHearingAidProfile.getConnectableDevices()).thenReturn(mDevicesList);
+
+ assertThat(mMediaDeviceUpdateWorker.isVisible()).isTrue();
+ }
+
+ @Test
+ public void isVisible_withConnectableHADevice_returnTrue() {
+ mDevicesList.clear();
+ mDevicesList.add(mHapDevice);
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mDevicesList);
+
+ assertThat(mMediaDeviceUpdateWorker.isVisible()).isTrue();
+ }
+
+ @Test
+ public void findActiveDeviceName_A2dpDeviceActive_verifyName() {
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mA2dpDevice);
+
+ assertThat(mMediaDeviceUpdateWorker.findActiveDeviceName())
+ .isEqualTo(mA2dpDevice.getAliasName());
+ }
+
+ @Test
+ public void findActiveDeviceName_HADeviceActive_verifyName() {
+ mDevicesList.add(mHapDevice);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mDevicesList);
+
+ assertThat(mMediaDeviceUpdateWorker.findActiveDeviceName())
+ .isEqualTo(mHapDevice.getAliasName());
+ }
+
+ @Test
+ public void findActiveDeviceName_noActiveDevice_verifyDefaultName() {
+ when(mA2dpProfile.getActiveDevice()).thenReturn(null);
+ mDevicesList.clear();
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mDevicesList);
+
+ assertThat(mMediaDeviceUpdateWorker.findActiveDeviceName())
+ .isEqualTo(mContext.getText(R.string.media_output_default_summary));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
index daaba90..d26a458 100644
--- a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
@@ -21,21 +21,15 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.util.IconDrawableFactory;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
@@ -43,6 +37,7 @@
import androidx.slice.core.SliceAction;
import androidx.slice.widget.SliceLiveData;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
@@ -53,70 +48,71 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class MediaOutputSliceTest {
private static final String TEST_PACKAGE_NAME = "com.fake.android.music";
- private static final String TEST_LABEL = "Test app";
private static final String TEST_DEVICE_1_ID = "test_device_1_id";
+ private static final String TEST_DEVICE_1_NAME = "test_device_1_name";
+ private static final int TEST_DEVICE_1_ICON =
+ com.android.internal.R.drawable.ic_bt_headphones_a2dp;
@Mock
- private PackageManager mPackageManager;
- @Mock
- private ApplicationInfo mApplicationInfo;
- @Mock
- private ApplicationInfo mApplicationInfo2;
- @Mock
private LocalMediaManager mLocalMediaManager;
- @Mock
- private IconDrawableFactory mIconDrawableFactory;
- @Mock
- private Drawable mTestDrawable;
private final List<MediaDevice> mDevices = new ArrayList<>();
private Context mContext;
private MediaOutputSlice mMediaOutputSlice;
private MediaDeviceUpdateWorker mMediaDeviceUpdateWorker;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
- when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt()))
- .thenReturn(mApplicationInfo);
- when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_NAME), anyInt(), anyInt()))
- .thenReturn(mApplicationInfo2);
- when(mApplicationInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
- when(mIconDrawableFactory.getBadgedIcon(mApplicationInfo2, UserHandle.myUserId()))
- .thenReturn(mTestDrawable);
- when(mTestDrawable.getIntrinsicWidth()).thenReturn(100);
- when(mTestDrawable.getIntrinsicHeight()).thenReturn(100);
-
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+ // Setup BluetoothAdapter
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mShadowBluetoothAdapter.setEnabled(true);
mMediaOutputSlice = new MediaOutputSlice(mContext);
mMediaDeviceUpdateWorker = new MediaDeviceUpdateWorker(mContext, MEDIA_OUTPUT_SLICE_URI);
mMediaDeviceUpdateWorker.setPackageName(TEST_PACKAGE_NAME);
mMediaDeviceUpdateWorker.onDeviceListUpdate(mDevices);
mMediaDeviceUpdateWorker.mLocalMediaManager = mLocalMediaManager;
- mMediaOutputSlice.init(TEST_PACKAGE_NAME, mMediaDeviceUpdateWorker, mIconDrawableFactory);
+ mMediaOutputSlice.init(TEST_PACKAGE_NAME, mMediaDeviceUpdateWorker);
}
@Test
- public void getSlice_shouldHaveAppTitle() {
+ public void getSlice_workerIsNull_shouldNotCrash() {
+ mMediaOutputSlice.init(TEST_PACKAGE_NAME, null);
+
+ mMediaOutputSlice.getSlice();
+ }
+
+ @Test
+ public void getSlice_shouldHaveActiveDeviceName() {
+ mDevices.clear();
+ final MediaDevice device = mock(MediaDevice.class);
+ when(device.getName()).thenReturn(TEST_DEVICE_1_NAME);
+ when(device.getIcon()).thenReturn(TEST_DEVICE_1_ICON);
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(device);
+
final Slice mediaSlice = mMediaOutputSlice.getSlice();
final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
final SliceAction primaryAction = metadata.getPrimaryAction();
- assertThat(primaryAction.getTitle().toString()).isEqualTo(TEST_LABEL);
+ assertThat(primaryAction.getTitle().toString()).isEqualTo(TEST_DEVICE_1_NAME);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java
index 1325650..10264ab 100644
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -31,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
+import android.telephony.euicc.EuiccManager;
import org.junit.After;
import org.junit.Before;
@@ -51,6 +53,9 @@
@RunWith(RobolectricTestRunner.class)
public class MobileNetworkListControllerTest {
@Mock
+ EuiccManager mEuiccManager;
+
+ @Mock
private Lifecycle mLifecycle;
@Mock
@@ -58,12 +63,17 @@
private Context mContext;
private MobileNetworkListController mController;
+ private Preference mAddMorePreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(Robolectric.setupActivity(Activity.class));
+ when(mContext.getSystemService(EuiccManager.class)).thenReturn(mEuiccManager);
when(mPreferenceScreen.getContext()).thenReturn(mContext);
+ mAddMorePreference = new Preference(mContext);
+ when(mPreferenceScreen.findPreference(MobileNetworkListController.KEY_ADD_MORE)).thenReturn(
+ mAddMorePreference);
mController = new MobileNetworkListController(mContext, mLifecycle);
}
@@ -79,6 +89,22 @@
}
@Test
+ public void displayPreference_eSimNotSupported_addMoreLinkNotVisible() {
+ when(mEuiccManager.isEnabled()).thenReturn(false);
+ mController.displayPreference(mPreferenceScreen);
+ mController.onResume();
+ assertThat(mAddMorePreference.isVisible()).isFalse();
+ }
+
+ @Test
+ public void displayPreference_eSimSupported_addMoreLinkIsVisible() {
+ when(mEuiccManager.isEnabled()).thenReturn(true);
+ mController.displayPreference(mPreferenceScreen);
+ mController.onResume();
+ assertThat(mAddMorePreference.isVisible()).isTrue();
+ }
+
+ @Test
public void displayPreference_twoSubscriptions_correctlySetup() {
final SubscriptionInfo sub1 = createMockSubscription(1, "sub1");
final SubscriptionInfo sub2 = createMockSubscription(2, "sub2");
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java
index ba152b9..3404ca2 100644
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java
@@ -16,13 +16,9 @@
package com.android.settings.network;
-import static android.telephony.TelephonyManager.MultiSimVariants.DSDS;
-import static android.telephony.TelephonyManager.MultiSimVariants.UNKNOWN;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
@@ -37,7 +33,6 @@
import android.net.ConnectivityManager;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
-import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
@@ -64,8 +59,6 @@
@Mock
private Lifecycle mLifecycle;
@Mock
- private TelephonyManager mTelephonyManager;
- @Mock
private EuiccManager mEuiccManager;
@Mock
private PreferenceScreen mPreferenceScreen;
@@ -78,9 +71,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(Robolectric.setupActivity(Activity.class));
- when(mContext.getSystemService(eq(TelephonyManager.class))).thenReturn(mTelephonyManager);
when(mContext.getSystemService(EuiccManager.class)).thenReturn(mEuiccManager);
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(UNKNOWN);
when(mEuiccManager.isEnabled()).thenReturn(true);
mController = new MobileNetworkSummaryController(mContext, mLifecycle);
@@ -97,7 +88,7 @@
@Test
public void isAvailable_wifiOnlyMode_notAvailable() {
- ConnectivityManager cm = mock(ConnectivityManager.class);
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
when(cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(cm);
assertThat(mController.isAvailable()).isFalse();
@@ -212,24 +203,7 @@
}
@Test
- public void addButton_noSubscriptionsSingleSimMode_noAddClickListener() {
- mController.displayPreference(mPreferenceScreen);
- mController.onResume();
- verify(mPreference, never()).setOnAddClickListener(notNull());
- }
-
- @Test
- public void addButton_oneSubscriptionSingleSimMode_noAddClickListener() {
- final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
- SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
- mController.displayPreference(mPreferenceScreen);
- mController.onResume();
- verify(mPreference, never()).setOnAddClickListener(notNull());
- }
-
- @Test
- public void addButton_noSubscriptionsMultiSimModeNoEuiccMgr_noAddClickListener() {
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
+ public void addButton_noSubscriptionsNoEuiccMgr_noAddClickListener() {
when(mEuiccManager.isEnabled()).thenReturn(false);
mController.displayPreference(mPreferenceScreen);
mController.onResume();
@@ -237,41 +211,43 @@
}
@Test
- public void addButton_noSubscriptionsMultiSimMode_hasAddClickListenerAndPrefDisabled() {
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
- mController.displayPreference(mPreferenceScreen);
- mController.onResume();
- assertThat(mPreference.isEnabled()).isFalse();
- verify(mPreference, never()).setOnAddClickListener(isNull());
- verify(mPreference).setOnAddClickListener(notNull());
- }
-
- @Test
- public void addButton_oneSubscriptionMultiSimMode_hasAddClickListener() {
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
+ public void addButton_oneSubscriptionNoEuiccMgr_noAddClickListener() {
+ when(mEuiccManager.isEnabled()).thenReturn(false);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.displayPreference(mPreferenceScreen);
mController.onResume();
- verify(mPreference, never()).setOnAddClickListener(isNull());
+ verify(mPreference, never()).setOnAddClickListener(notNull());
+ }
+
+ @Test
+ public void addButton_noSubscriptions_noAddClickListener() {
+ mController.displayPreference(mPreferenceScreen);
+ mController.onResume();
+ verify(mPreference, never()).setOnAddClickListener(notNull());
+ }
+
+ @Test
+ public void addButton_oneSubscription_hasAddClickListener() {
+ final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
+ mController.displayPreference(mPreferenceScreen);
+ mController.onResume();
verify(mPreference).setOnAddClickListener(notNull());
}
@Test
- public void addButton_twoSubscriptionsMultiSimMode_hasAddClickListener() {
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
+ public void addButton_twoSubscriptions_hasAddClickListener() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
mController.displayPreference(mPreferenceScreen);
mController.onResume();
- verify(mPreference, never()).setOnAddClickListener(isNull());
verify(mPreference).setOnAddClickListener(notNull());
}
@Test
public void addButton_oneSubscriptionAirplaneModeTurnedOn_addButtonGetsDisabled() {
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.displayPreference(mPreferenceScreen);
@@ -280,14 +256,13 @@
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
mController.onAirplaneModeChanged(true);
- ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+ final ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(captor.capture());
assertThat(captor.getValue()).isFalse();
}
@Test
public void onResume_oneSubscriptionAirplaneMode_isDisabled() {
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
@@ -296,7 +271,7 @@
assertThat(mPreference.isEnabled()).isFalse();
- ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+ final ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(captor.capture());
assertThat(captor.getValue()).isFalse();
}
@@ -318,7 +293,6 @@
@Test
public void onAirplaneModeChanged_oneSubscriptionAirplaneModeGetsTurnedOff_isEnabled() {
- when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
@@ -332,7 +306,7 @@
assertThat(mPreference.isEnabled()).isTrue();
- ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+ final ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(eq(false));
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(captor.capture());
assertThat(captor.getValue()).isTrue();
diff --git a/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java
index 2135331..e04262f 100644
--- a/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java
@@ -16,6 +16,8 @@
package com.android.settings.network;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -24,8 +26,10 @@
import android.os.UserManager;
import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
+import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,7 +43,7 @@
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowRestrictedLockUtilsInternal.class)
+@Config(shadows = {ShadowRestrictedLockUtilsInternal.class, ShadowUtils.class})
public class TopLevelNetworkEntryPreferenceControllerTest {
@Mock
@@ -70,6 +74,17 @@
mTetherPreferenceController);
}
+ @After
+ public void tearDown() {
+ ShadowUtils.reset();
+ }
+
+ @Test
+ public void getAvailabilityStatus_demoUser_unsupported() {
+ ShadowUtils.setIsDemoUser(true);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
@Test
public void getSummary_hasMobileAndHotspot_shouldReturnMobileSummary() {
when(mWifiPreferenceController.isAvailable()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
index 072c835..a9882d9 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
@@ -22,7 +22,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -43,7 +42,6 @@
import com.android.ims.ImsManager;
import com.android.settings.R;
-import com.android.settings.slices.CustomSliceManager;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SliceBroadcastReceiver;
@@ -85,10 +83,6 @@
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
- CustomSliceManager manager = new CustomSliceManager(mContext);
- when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class)))
- .thenReturn(manager);
-
//setup for SettingsSliceProvider tests
mProvider = spy(new SettingsSliceProvider());
doReturn(mContext).when(mProvider).getContext();
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
index 31daa37..e488d0c 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
@@ -184,6 +184,6 @@
final PendingIntent pendingIntent = primaryAction.getAction();
final Intent actionIntent = pendingIntent.getIntent();
- assertThat(actionIntent.getAction()).isNull();
+ assertThat(actionIntent).isNull();
}
}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
index 55a4224..011bca5 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
@@ -30,6 +30,8 @@
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import androidx.fragment.app.FragmentActivity;
+
import com.android.settings.core.FeatureFlags;
import com.android.settings.datausage.DataUsageSummaryPreferenceController;
import com.android.settings.development.featureflags.FeatureFlagPersistent;
@@ -48,8 +50,6 @@
import java.util.List;
-import androidx.fragment.app.FragmentActivity;
-
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowEntityHeaderController.class)
public class MobileNetworkSettingsTest {
@@ -86,6 +86,7 @@
@Test
public void onAttach_noV2Flag_noCrash() {
+ FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, false);
mFragment.onAttach(mContext);
}
@@ -97,6 +98,7 @@
@Test
public void createPreferenceControllers_noV2Flag_noDataUsageSummaryController() {
+ FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, false);
final List<AbstractPreferenceController> controllers =
mFragment.createPreferenceControllers(mContext);
assertThat(controllers.stream().filter(
diff --git a/tests/robotests/src/com/android/settings/notification/AssistantCapabilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/AssistantCapabilityPreferenceControllerTest.java
new file mode 100644
index 0000000..182ce8a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/AssistantCapabilityPreferenceControllerTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.notification;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+import static com.android.settings.notification.AssistantCapabilityPreferenceController.PRIORITIZER_KEY;
+import static com.android.settings.notification.AssistantCapabilityPreferenceController.SMART_KEY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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 android.content.ComponentName;
+import android.content.Context;
+import android.service.notification.Adjustment;
+
+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 java.util.ArrayList;
+import java.util.List;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class AssistantCapabilityPreferenceControllerTest {
+
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private PreferenceScreen mScreen;
+
+ private Context mContext;
+ private AssistantCapabilityPreferenceController mPrioritizerController;
+ private AssistantCapabilityPreferenceController mChipController;
+ private Preference mPrioritizerPreference;
+ private Preference mChipPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mPrioritizerController = new AssistantCapabilityPreferenceController(
+ mContext, PRIORITIZER_KEY);
+ mPrioritizerController.setBackend(mBackend);
+ mPrioritizerPreference = new Preference(mContext);
+ mPrioritizerPreference.setKey(mPrioritizerController.getPreferenceKey());
+ when(mScreen.findPreference(
+ mPrioritizerController.getPreferenceKey())).thenReturn(mPrioritizerPreference);
+ mChipController = new AssistantCapabilityPreferenceController(mContext, SMART_KEY);
+ mChipController.setBackend(mBackend);
+ mChipPreference = new Preference(mContext);
+ mChipPreference.setKey(mChipController.getPreferenceKey());
+ when(mScreen.findPreference(
+ mChipController.getPreferenceKey())).thenReturn(mChipPreference);
+ }
+
+ @Test
+ public void getAvailabilityStatus_NAS() {
+ when(mBackend.getAllowedNotificationAssistant()).thenReturn(mock(ComponentName.class));
+ assertThat(mPrioritizerController.getAvailabilityStatus())
+ .isEqualTo(AVAILABLE);
+ assertThat(mChipController.getAvailabilityStatus())
+ .isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_noNAS() {
+ when(mBackend.getAllowedNotificationAssistant()).thenReturn(null);
+ assertThat(mPrioritizerController.getAvailabilityStatus())
+ .isEqualTo(DISABLED_DEPENDENT_SETTING);
+ assertThat(mChipController.getAvailabilityStatus())
+ .isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void isChecked_prioritizerSettingIsOff_false() {
+ List<String> capabilities = new ArrayList<>();
+ capabilities.add(Adjustment.KEY_USER_SENTIMENT);
+ when(mBackend.getAssistantCapabilities(anyString())).thenReturn(capabilities);
+ assertThat(mPrioritizerController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_prioritizerSettingIsOn_true() {
+ List<String> capabilities = new ArrayList<>();
+ capabilities.add(Adjustment.KEY_IMPORTANCE);
+ when(mBackend.getAssistantCapabilities(anyString())).thenReturn(capabilities);
+ assertThat(mPrioritizerController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void isChecked_chipSettingIsOff_false() {
+ List<String> capabilities = new ArrayList<>();
+ capabilities.add(Adjustment.KEY_IMPORTANCE);
+ when(mBackend.getAssistantCapabilities(anyString())).thenReturn(capabilities);
+ assertThat(mChipController.isChecked()).isFalse();
+
+ capabilities = new ArrayList<>();
+ capabilities.add(Adjustment.KEY_CONTEXTUAL_ACTIONS);
+ when(mBackend.getAssistantCapabilities(anyString())).thenReturn(capabilities);
+ assertThat(mChipController.isChecked()).isFalse();
+
+ capabilities = new ArrayList<>();
+ capabilities.add(Adjustment.KEY_TEXT_REPLIES);
+ when(mBackend.getAssistantCapabilities(anyString())).thenReturn(capabilities);
+ assertThat(mChipController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_chipSettingIsOn_true() {
+ List<String> capabilities = new ArrayList<>();
+ capabilities.add(Adjustment.KEY_TEXT_REPLIES);
+ capabilities.add(Adjustment.KEY_CONTEXTUAL_ACTIONS);
+ when(mBackend.getAssistantCapabilities(anyString())).thenReturn(capabilities);
+ assertThat(mChipController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void onPreferenceChange_prioritizerOn() {
+ mPrioritizerController.onPreferenceChange(mPrioritizerPreference, true);
+ verify(mBackend).allowAssistantCapability(Adjustment.KEY_IMPORTANCE, true);
+ }
+
+ @Test
+ public void onPreferenceChange_prioritizerOff() {
+ mPrioritizerController.onPreferenceChange(mPrioritizerPreference, false);
+ verify(mBackend).allowAssistantCapability(Adjustment.KEY_IMPORTANCE, false);
+ }
+
+ @Test
+ public void onPreferenceChange_chipsOn() {
+ mChipController.onPreferenceChange(mChipPreference, true);
+ verify(mBackend).allowAssistantCapability(Adjustment.KEY_CONTEXTUAL_ACTIONS, true);
+ verify(mBackend).allowAssistantCapability(Adjustment.KEY_TEXT_REPLIES, true);
+ }
+
+ @Test
+ public void onPreferenceChange_chipsOff() {
+ mChipController.onPreferenceChange(mChipPreference, false);
+ verify(mBackend).allowAssistantCapability(Adjustment.KEY_CONTEXTUAL_ACTIONS, false);
+ verify(mBackend).allowAssistantCapability(Adjustment.KEY_TEXT_REPLIES, false);
+ }
+}
+
diff --git a/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
index 4f6944a..bdbf40a 100644
--- a/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
@@ -111,6 +111,26 @@
}
@Test
+ public void testIsAvailable_notIfChannelNonDefault() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_ifChannelDefault() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
public void testIsAvailable_notIfGroupNotBlockable() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.systemApp = true;
diff --git a/tests/robotests/src/com/android/settings/notification/HighImportancePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/HighImportancePreferenceControllerTest.java
new file mode 100644
index 0000000..6e6dad4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/HighImportancePreferenceControllerTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+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.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowApplication;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class HighImportancePreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationSettingsBase.ImportanceListener mImportanceListener;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private HighImportancePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = RuntimeEnvironment.application;
+ mController = spy(new HighImportancePreferenceController(
+ mContext, mImportanceListener, mBackend));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_ifAppBlocked() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelBlocked() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notForDefaultChannel() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(mContext, null);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(mContext, null);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_high() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
+ mController.updateState(pref);
+
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_default() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
+ mController.updateState(pref);
+
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void onPreferenceChange() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext, null);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, false);
+
+ assertEquals(IMPORTANCE_DEFAULT, channel.getImportance());
+ verify(mImportanceListener, times(1)).onImportanceChanged();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java
index 99d3376..c180ace 100644
--- a/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java
@@ -27,8 +27,11 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
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.app.Notification;
@@ -36,12 +39,10 @@
import android.app.NotificationManager;
import android.content.Context;
import android.os.UserManager;
-import android.text.TextUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settings.RestrictedListPreference;
import com.android.settingslib.RestrictedLockUtils;
import org.junit.Before;
@@ -95,20 +96,20 @@
}
@Test
- public void testIsAvailable_notIfAppBlocked() {
+ public void testIsAvailable_ifAppBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.banned = true;
mController.onResume(appRow, mock(NotificationChannel.class), null, null);
- assertFalse(mController.isAvailable());
+ assertTrue(mController.isAvailable());
}
@Test
- public void testIsAvailable_notIfChannelBlocked() {
+ public void testIsAvailable_evenIfChannelBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
mController.onResume(appRow, channel, null, null);
- assertFalse(mController.isAvailable());
+ assertTrue(mController.isAvailable());
}
@Test
@@ -137,11 +138,10 @@
mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
RestrictedLockUtils.EnforcedAdmin.class));
- Preference pref = new RestrictedListPreference(mContext, null);
+ Preference pref = new ImportancePreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
- assertFalse(TextUtils.isEmpty(pref.getSummary()));
}
@Test
@@ -154,11 +154,10 @@
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
- Preference pref = new RestrictedListPreference(mContext, null);
+ Preference pref = new ImportancePreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
- assertFalse(TextUtils.isEmpty(pref.getSummary()));
}
@Test
@@ -167,11 +166,12 @@
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
- Preference pref = new RestrictedListPreference(mContext, null);
+ ImportancePreference pref = mock(ImportancePreference.class);
mController.updateState(pref);
- assertTrue(pref.isEnabled());
- assertFalse(TextUtils.isEmpty(pref.getSummary()));
+ verify(pref, times(1)).setConfigurable(anyBoolean());
+ verify(pref, times(1)).setBlockable(anyBoolean());
+ verify(pref, times(1)).setImportance(IMPORTANCE_HIGH);
}
@Test
@@ -181,13 +181,12 @@
channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
- RestrictedListPreference pref = new RestrictedListPreference(mContext, null);
+ ImportancePreference pref = new ImportancePreference(mContext, null);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
- pref.setValue(String.valueOf(IMPORTANCE_HIGH));
- mController.onPreferenceChange(pref, pref.getValue());
+ mController.onPreferenceChange(pref, IMPORTANCE_HIGH);
assertEquals(IMPORTANCE_HIGH, channel.getImportance());
assertNotNull(channel.getSound());
@@ -200,13 +199,12 @@
channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
- RestrictedListPreference pref = new RestrictedListPreference(mContext, null);
+ ImportancePreference pref = new ImportancePreference(mContext, null);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
- pref.setValue(String.valueOf(IMPORTANCE_LOW));
- mController.onPreferenceChange(pref, pref.getValue());
+ mController.onPreferenceChange(pref, IMPORTANCE_LOW);
assertEquals(IMPORTANCE_LOW, channel.getImportance());
assertNull(channel.getSound());
diff --git a/tests/robotests/src/com/android/settings/notification/ImportancePreferenceTest.java b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceTest.java
new file mode 100644
index 0000000..eebfbd1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+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.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+
+import com.android.settings.R;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+@RunWith(RobolectricTestRunner.class)
+public class ImportancePreferenceTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ }
+
+ private GradientDrawable getBackground(ImageButton button) {
+ return (GradientDrawable) ((LayerDrawable) button.getDrawable())
+ .findDrawableByLayerId(R.id.back);
+ }
+
+ @Test
+ public void createNewPreference_shouldSetLayout() {
+ final ImportancePreference preference = new ImportancePreference(mContext);
+ assertThat(preference.getLayoutResource()).isEqualTo(
+ R.layout.notif_importance_preference);
+ }
+
+ @Test
+ public void onBindViewHolder_hideBlockNonBlockable() {
+ final ImportancePreference preference = new ImportancePreference(mContext);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+ inflater.inflate(R.layout.notif_importance_preference, null));
+
+ preference.setBlockable(false);
+ preference.setConfigurable(true);
+ preference.setImportance(IMPORTANCE_DEFAULT);
+ preference.onBindViewHolder(holder);
+
+ assertThat(holder.itemView.findViewById(R.id.block).getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_hideNonSelectedNonConfigurable() {
+ final ImportancePreference preference = new ImportancePreference(mContext);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+ inflater.inflate(R.layout.notif_importance_preference, null));
+
+ preference.setBlockable(true);
+ preference.setConfigurable(false);
+ preference.setImportance(IMPORTANCE_DEFAULT);
+ preference.onBindViewHolder(holder);
+
+ assertThat(holder.itemView.findViewById(R.id.block).getVisibility()).isEqualTo(View.GONE);
+ assertThat(holder.itemView.findViewById(R.id.silence).getVisibility()).isEqualTo(View.GONE);
+ assertThat(holder.itemView.findViewById(R.id.alert).getVisibility())
+ .isEqualTo(View.VISIBLE);
+
+ // other button
+ preference.setImportance(IMPORTANCE_LOW);
+ holder = PreferenceViewHolder.createInstanceForTests(
+ inflater.inflate(R.layout.notif_importance_preference, null));
+ preference.onBindViewHolder(holder);
+
+ assertThat(holder.itemView.findViewById(R.id.block).getVisibility()).isEqualTo(View.GONE);
+ assertThat(holder.itemView.findViewById(R.id.silence).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(holder.itemView.findViewById(R.id.alert).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_selectButton() {
+ final ImportancePreference preference = new ImportancePreference(mContext);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+ inflater.inflate(R.layout.notif_importance_preference, null));
+
+ preference.setBlockable(true);
+ preference.setConfigurable(true);
+ preference.setImportance(IMPORTANCE_DEFAULT);
+
+ ImageButton blockButton = (ImageButton) holder.findViewById(R.id.block_icon);
+ ImageButton silenceButton = (ImageButton) holder.findViewById(R.id.silence_icon);
+ ImageButton alertButton = (ImageButton) holder.findViewById(R.id.alert_icon);
+
+ preference.onBindViewHolder(holder);
+
+ // selected has full color background. others are transparent
+ assertThat(getBackground(alertButton).getColor().getColors()[0]).isNotEqualTo(
+ Color.TRANSPARENT);
+ assertThat(getBackground(silenceButton).getColor().getColors()[0]).isEqualTo(
+ Color.TRANSPARENT);
+ assertThat(getBackground(blockButton).getColor().getColors()[0]).isEqualTo(
+ Color.TRANSPARENT);
+ }
+
+ @Test
+ public void onClick_changesUICallsListener() {
+ final ImportancePreference preference = spy(new ImportancePreference(mContext));
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+ inflater.inflate(R.layout.notif_importance_preference, null));
+
+ preference.setBlockable(true);
+ preference.setConfigurable(true);
+ preference.setImportance(IMPORTANCE_DEFAULT);
+ preference.onBindViewHolder(holder);
+
+ ImageButton blockButton = (ImageButton) holder.findViewById(R.id.block_icon);
+ ImageButton silenceButton = (ImageButton) holder.findViewById(R.id.silence_icon);
+ ImageButton alertButton = (ImageButton) holder.findViewById(R.id.alert_icon);
+
+ silenceButton.callOnClick();
+
+ // selected has full color background. others are transparent
+ assertThat(getBackground(silenceButton).getColor().getColors()[0]).isNotEqualTo(
+ Color.TRANSPARENT);
+ assertThat(getBackground(alertButton).getColor().getColors()[0]).isEqualTo(
+ Color.TRANSPARENT);
+ assertThat(getBackground(blockButton).getColor().getColors()[0]).isEqualTo(
+ Color.TRANSPARENT);
+
+ verify(preference, times(1)).callChangeListener(IMPORTANCE_LOW);
+ }
+
+ @Test
+ public void onBindViewHolder_allButtonsVisible() {
+ final ImportancePreference preference = new ImportancePreference(mContext);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+ inflater.inflate(R.layout.notif_importance_preference, null));
+
+ preference.setBlockable(true);
+ preference.setConfigurable(true);
+ preference.onBindViewHolder(holder);
+
+ assertThat(holder.itemView.findViewById(R.id.block).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(holder.itemView.findViewById(R.id.silence).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(holder.itemView.findViewById(R.id.alert).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/MinImportancePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/MinImportancePreferenceControllerTest.java
new file mode 100644
index 0000000..28058a4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/MinImportancePreferenceControllerTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+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.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowApplication;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class MinImportancePreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationSettingsBase.ImportanceListener mImportanceListener;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private MinImportancePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = RuntimeEnvironment.application;
+ mController = spy(new MinImportancePreferenceController(
+ mContext, mImportanceListener, mBackend));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_ifAppBlocked() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelBlocked() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notForDefaultChannel() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(mContext, null);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(mContext, null);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_min() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_MIN);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
+ mController.updateState(pref);
+
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_low() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
+ mController.updateState(pref);
+
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void onPreferenceChange() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext, null);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, true);
+
+ assertEquals(IMPORTANCE_MIN, channel.getImportance());
+ verify(mImportanceListener, times(1)).onImportanceChanged();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java
index 1ad9378..2368af5 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java
@@ -219,6 +219,20 @@
}
@Test
+ public void testIsConfigurable_appLevel() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = "something";
+ appRow.lockedImportance = true;
+
+ mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isChannelConfigurable());
+
+ appRow.lockedImportance = false;
+ mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+ assertTrue(mController.isChannelConfigurable());
+ }
+
+ @Test
public void testIsChannelBlockable_nonSystemAppsBlockable() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.systemApp = false;
diff --git a/tests/robotests/src/com/android/settings/notification/RemoteVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/RemoteVolumePreferenceControllerTest.java
index 1bf2fd8..1e68de5 100644
--- a/tests/robotests/src/com/android/settings/notification/RemoteVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/RemoteVolumePreferenceControllerTest.java
@@ -23,7 +23,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.media.session.ControllerLink;
+import android.media.session.ISessionController;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
@@ -51,9 +51,9 @@
@Mock
private MediaController mMediaController;
@Mock
- private ControllerLink.ControllerStub mStub;
+ private ISessionController mStub;
@Mock
- private ControllerLink.ControllerStub mStub2;
+ private ISessionController mStub2;
private MediaSession.Token mToken;
private MediaSession.Token mToken2;
private RemoteVolumePreferenceController mController;
@@ -71,8 +71,8 @@
mActiveSessions.add(mMediaController);
when(mMediaSessionManager.getActiveSessions(null)).thenReturn(
mActiveSessions);
- mToken = new MediaSession.Token(new ControllerLink(mStub));
- mToken2 = new MediaSession.Token(new ControllerLink(mStub2));
+ mToken = new MediaSession.Token(mStub);
+ mToken2 = new MediaSession.Token(mStub2);
mController = new RemoteVolumePreferenceController(mContext);
mPlaybackInfo = new MediaController.PlaybackInfo(
diff --git a/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java
deleted file mode 100644
index c2a6f4f..0000000
--- a/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.settings.notification;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.testutils.FakeFeatureFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class ZenAccessSettingsTest {
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Context mContext;
-
- private FakeFeatureFactory mFeatureFactory;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mFeatureFactory = FakeFeatureFactory.setupForTest();
- }
-
- @Test
- public void logSpecialPermissionChange() {
- ZenAccessSettings.logSpecialPermissionChange(true, "app", mContext);
- verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
- eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_ALLOW),
- eq("app"));
-
- ZenAccessSettings.logSpecialPermissionChange(false, "app", mContext);
- verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
- eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_DENY),
- eq("app"));
- }
-}
diff --git a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
index a51b7b0..1d5c3c2 100644
--- a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
+++ b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
@@ -76,15 +76,16 @@
}
@Test
- public void startMediaOutputSlice_withoutPackageName_bundleShouldNotHaveValue() {
+ public void startMediaOutputSlice_withoutPackageName_bundleShouldHaveValue() {
final Intent intent = new Intent()
.setAction("com.android.settings.panel.action.MEDIA_OUTPUT");
final SettingsPanelActivity activity =
Robolectric.buildActivity(SettingsPanelActivity.class, intent).create().get();
- assertThat(activity.mBundle.containsKey(KEY_MEDIA_PACKAGE_NAME)).isFalse();
- assertThat(activity.mBundle.containsKey(KEY_PANEL_TYPE_ARGUMENT)).isFalse();
+ assertThat(activity.mBundle.containsKey(KEY_MEDIA_PACKAGE_NAME)).isTrue();
+ assertThat(activity.mBundle.getString(KEY_PANEL_TYPE_ARGUMENT))
+ .isEqualTo("com.android.settings.panel.action.MEDIA_OUTPUT");
}
@Test
diff --git a/tests/robotests/src/com/android/settings/panel/VolumePanelTest.java b/tests/robotests/src/com/android/settings/panel/VolumePanelTest.java
index 4665dc9..11de7b3 100644
--- a/tests/robotests/src/com/android/settings/panel/VolumePanelTest.java
+++ b/tests/robotests/src/com/android/settings/panel/VolumePanelTest.java
@@ -48,6 +48,7 @@
CustomSliceRegistry.VOLUME_REMOTE_MEDIA_URI,
CustomSliceRegistry.VOLUME_CALL_URI,
CustomSliceRegistry.VOLUME_MEDIA_URI,
+ CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI,
CustomSliceRegistry.VOLUME_RINGER_URI,
CustomSliceRegistry.VOLUME_ALARM_URI);
}
diff --git a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
index 48f9fb4..bed09cb 100644
--- a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
@@ -16,7 +16,7 @@
package com.android.settings.password;
-import static android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
@@ -139,7 +139,7 @@
public void testLaunchChooseLock_setNewPasswordExtraWithPermission() {
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
ShadowPasswordUtils.setCallingAppPackageName(PKG_NAME);
- ShadowPasswordUtils.addGrantedPermission(REQUEST_SCREEN_LOCK_COMPLEXITY);
+ ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
@@ -169,7 +169,7 @@
public void testLaunchChooseLock_setNewPasswordExtraInvalidValue() {
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
ShadowPasswordUtils.setCallingAppPackageName(PKG_NAME);
- ShadowPasswordUtils.addGrantedPermission(REQUEST_SCREEN_LOCK_COMPLEXITY);
+ ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
@@ -197,7 +197,7 @@
public void testLaunchChooseLock_setNewPasswordExtraNoneComplexity() {
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
ShadowPasswordUtils.setCallingAppPackageName(PKG_NAME);
- ShadowPasswordUtils.addGrantedPermission(REQUEST_SCREEN_LOCK_COMPLEXITY);
+ ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
@@ -225,7 +225,7 @@
public void testLaunchChooseLock_setNewPasswordWithoutExtra() {
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
ShadowPasswordUtils.setCallingAppPackageName(PKG_NAME);
- ShadowPasswordUtils.addGrantedPermission(REQUEST_SCREEN_LOCK_COMPLEXITY);
+ ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
@@ -252,7 +252,7 @@
public void testLaunchChooseLock_setNewParentProfilePasswordExtraWithPermission() {
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
ShadowPasswordUtils.setCallingAppPackageName(PKG_NAME);
- ShadowPasswordUtils.addGrantedPermission(REQUEST_SCREEN_LOCK_COMPLEXITY);
+ ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
@@ -280,7 +280,7 @@
public void testLaunchChooseLock_setNewParentProfilePasswordWithoutExtra() {
ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
ShadowPasswordUtils.setCallingAppPackageName(PKG_NAME);
- ShadowPasswordUtils.addGrantedPermission(REQUEST_SCREEN_LOCK_COMPLEXITY);
+ ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
index d35b38a..cd79f58 100644
--- a/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
@@ -16,7 +16,7 @@
package com.android.settings.password;
-import static android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
@@ -68,7 +68,7 @@
@Test
@Config(shadows = {ShadowPasswordUtils.class})
public void setupChooseLockGenericPasswordComplexityExtraWithPermission() {
- ShadowPasswordUtils.addGrantedPermission(REQUEST_SCREEN_LOCK_COMPLEXITY);
+ ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
diff --git a/tests/robotests/src/com/android/settings/privacy/EnableContentCapturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/privacy/EnableContentCapturePreferenceControllerTest.java
index a379858..179063d 100644
--- a/tests/robotests/src/com/android/settings/privacy/EnableContentCapturePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/privacy/EnableContentCapturePreferenceControllerTest.java
@@ -51,7 +51,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mController = new EnableContentCapturePreferenceController(mContext);
+ mController = new EnableContentCapturePreferenceController(mContext, "THE_KEY_TO_SUCCESS");
mPreference = new Preference(mContext);
mPreference.setKey(mController.getPreferenceKey());
}
diff --git a/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java
index 482eaeb..80f3900 100644
--- a/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java
@@ -80,10 +80,10 @@
}
@Test
- public void onViewCreated_shouldCallStyleActionBar() {
+ public void onViewCreated_shouldSetActionBarShadowAnimation() {
mFragment.onViewCreated(new View(mContext), new Bundle());
- verify(mFragment).styleActionBar();
+ assertThat(mFragment.getActivity().getActionBar().getElevation()).isEqualTo(0.f);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index a693f34..005ffbe 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -133,9 +133,7 @@
mProvider = spy(new SettingsSliceProvider());
ShadowStrictMode.reset();
mProvider.mSliceWeakDataCache = new HashMap<>();
- mProvider.mSliceDataCache = new HashMap<>();
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
- mProvider.mCustomSliceManager = new CustomSliceManager(mContext);
when(mProvider.getContext()).thenReturn(mContext);
SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
@@ -199,30 +197,6 @@
}
@Test
- public void testLoadSlice_doesNotCacheWithoutPin() {
- insertSpecialCase(KEY);
- final Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
-
- mProvider.loadSlice(uri);
- SliceData data = mProvider.mSliceDataCache.get(uri);
-
- assertThat(data).isNull();
- }
-
- @Test
- public void testLoadSlice_cachesWithPin() {
- insertSpecialCase(KEY);
- final Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
- when(mManager.getPinnedSlices()).thenReturn(Arrays.asList(uri));
-
- mProvider.loadSlice(uri);
- SliceData data = mProvider.mSliceDataCache.get(uri);
-
- assertThat(data.getKey()).isEqualTo(KEY);
- assertThat(data.getTitle()).isEqualTo(TITLE);
- }
-
- @Test
public void testLoadSlice_cachedEntryRemovedOnBuild() {
SliceData data = getDummyData();
mProvider.mSliceWeakDataCache.put(data.getUri(), data);
@@ -278,18 +252,6 @@
}
@Test
- public void testLoadSlice_cachedEntryRemovedOnUnpin() {
- SliceData data = getDummyData();
- mProvider.mSliceDataCache.put(data.getUri(), data);
- mProvider.onSliceUnpinned(data.getUri());
- insertSpecialCase(data.getKey());
-
- SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
-
- assertThat(cachedData).isNull();
- }
-
- @Test
public void getDescendantUris_fullActionUri_returnsSelf() {
final Uri uri = SliceBuilderUtils.getUri(
SettingsSlicesContract.PATH_SETTING_ACTION + "/key", true);
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
index 74f4ac2..1ea324d 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
@@ -19,14 +19,12 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.app.slice.Slice;
@@ -82,9 +80,6 @@
mSearchFeatureProvider = new SearchFeatureProviderImpl();
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mFakeFeatureFactory.searchFeatureProvider = mSearchFeatureProvider;
- CustomSliceManager manager = new CustomSliceManager(mContext);
- when(mFakeFeatureFactory.slicesFeatureProvider.getCustomSliceManager(any()))
- .thenReturn(manager);
}
@After
diff --git a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
index 0e92c05..96bca07 100644
--- a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
@@ -55,6 +55,7 @@
@After
public void cleanUp() {
DatabaseTestUtils.clearDb(mContext);
+ mDatabase.close();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java b/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java
index 82726df..d483f9e 100644
--- a/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java
@@ -19,8 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.spy;
-
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -39,44 +37,50 @@
@RunWith(RobolectricTestRunner.class)
public class SpecialCaseSliceManagerTest {
- private Context mContext;
+ private final String FAKE_PARAMETER_KEY = "fake_parameter_key";
+ private final String FAKE_PARAMETER_VALUE = "fake_value";
- private CustomSliceManager mCustomSliceManager;
+ private Context mContext;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
- mCustomSliceManager = spy(new CustomSliceManager(mContext));
- mCustomSliceManager.mUriMap.clear();
- mCustomSliceManager.mUriMap.put(FakeSliceable.URI, FakeSliceable.class);
+ CustomSliceRegistry.sUriToSlice.clear();
+ CustomSliceRegistry.sUriToSlice.put(FakeSliceable.URI, FakeSliceable.class);
}
@Test
public void getSliceableFromUri_returnsCorrectObject() {
- final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(
- FakeSliceable.URI);
+ final CustomSliceable sliceable = CustomSliceable.createInstance(
+ mContext, CustomSliceRegistry.getSliceClassByUri(FakeSliceable.URI));
assertThat(sliceable).isInstanceOf(FakeSliceable.class);
}
@Test
- public void getSliceableFromIntentAction_returnsCorrectObject() {
- final CustomSliceable sliceable =
- mCustomSliceManager.getSliceableFromIntentAction(FakeSliceable.URI.toString());
+ public void getSliceableFromUriWithParameter_returnsCorrectObject() {
+ final Uri parameterUri = FakeSliceable.URI
+ .buildUpon()
+ .clearQuery()
+ .appendQueryParameter(FAKE_PARAMETER_KEY, FAKE_PARAMETER_VALUE)
+ .build();
+
+ final CustomSliceable sliceable = CustomSliceable.createInstance(
+ mContext, CustomSliceRegistry.getSliceClassByUri(parameterUri));
assertThat(sliceable).isInstanceOf(FakeSliceable.class);
}
@Test
public void isValidUri_validUri_returnsTrue() {
- final boolean isValidUri = mCustomSliceManager.isValidUri(FakeSliceable.URI);
+ final boolean isValidUri = CustomSliceRegistry.isValidUri(FakeSliceable.URI);
assertThat(isValidUri).isTrue();
}
@Test
public void isValidUri_invalidUri_returnsFalse() {
- final boolean isValidUri = mCustomSliceManager.isValidUri(null);
+ final boolean isValidUri = CustomSliceRegistry.isValidUri(null);
assertThat(isValidUri).isFalse();
}
@@ -84,14 +88,14 @@
@Test
public void isValidAction_validActions_returnsTrue() {
final boolean isValidAction =
- mCustomSliceManager.isValidAction(FakeSliceable.URI.toString());
+ CustomSliceRegistry.isValidAction(FakeSliceable.URI.toString());
assertThat(isValidAction).isTrue();
}
@Test
public void isValidAction_invalidAction_returnsFalse() {
- final boolean isValidAction = mCustomSliceManager.isValidAction("action");
+ final boolean isValidAction = CustomSliceRegistry.isValidAction("action");
assertThat(isValidAction).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index 54a79f4..5b7c863 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -238,63 +238,6 @@
verify(mLocalBluetoothManager).setForegroundActivity(null);
}
- @Test
- public void onPreferenceChange_toThisDevice_shouldSetDefaultSummary() {
- mController.mConnectedDevices.clear();
- mController.mConnectedDevices.add(mBluetoothDevice);
-
- mController.onPreferenceChange(mPreference,
- mContext.getText(R.string.media_output_default_summary));
-
- assertThat(mPreference.getSummary()).isEqualTo(
- mContext.getText(R.string.media_output_default_summary));
- }
-
- /**
- * One Bluetooth devices are available, and select the device.
- * Preference summary should be device name.
- */
- @Test
- public void onPreferenceChange_toBtDevice_shouldSetBtDeviceName() {
- mController.mConnectedDevices.clear();
- mController.mConnectedDevices.add(mBluetoothDevice);
-
- mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1);
-
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
- }
-
- /**
- * More than one Bluetooth devices are available, and select second device.
- * Preference summary should be second device name.
- */
- @Test
- public void onPreferenceChange_toBtDevices_shouldSetSecondBtDeviceName() {
- ShadowBluetoothDevice shadowBluetoothDevice;
- BluetoothDevice secondBluetoothDevice;
- secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
- shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
- shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
- mController.mConnectedDevices.clear();
- mController.mConnectedDevices.add(mBluetoothDevice);
- mController.mConnectedDevices.add(secondBluetoothDevice);
-
- mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_2);
-
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_2);
- }
-
- /**
- * mConnectedDevices is empty.
- * onPreferenceChange should return false.
- */
- @Test
- public void onPreferenceChange_connectedDeviceIsNull_shouldReturnFalse() {
- mController.mConnectedDevices.clear();
-
- assertThat(mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1)).isFalse();
- }
-
/**
* Audio stream output to bluetooth sco headset which is the subset of all sco device.
* isStreamFromOutputDevice should return true.
@@ -416,39 +359,6 @@
}
/**
- * One A2dp device is connected.
- * getConnectedA2dpDevices should add this device to list.
- */
- @Test
- public void getConnectedA2dpDevices_oneConnectedA2dpDevice_shouldAddDeviceToList() {
- mEmptyDevices.clear();
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
-
- mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
-
- assertThat(mEmptyDevices).containsExactly(mBluetoothDevice);
- }
-
- /**
- * More than one A2dp devices are connected.
- * getConnectedA2dpDevices should add all devices to list.
- */
- @Test
- public void getConnectedA2dpDevices_moreThanOneConnectedA2dpDevice_shouldAddDeviceToList() {
- mEmptyDevices.clear();
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
-
- mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
-
- assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mLeftBluetoothHapDevice);
- }
-
- /**
* One hands free profile device is connected.
* getConnectedA2dpDevices should add this device to list.
*/
@@ -488,10 +398,6 @@
}
@Override
- public void setActiveBluetoothDevice(BluetoothDevice device) {
- }
-
- @Override
public BluetoothDevice findActiveDevice() {
return null;
}
diff --git a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
index 4010145..0eada60 100644
--- a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
@@ -57,6 +57,7 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowBluetoothDevice;
@@ -107,7 +108,7 @@
private BluetoothDevice mLeftBluetoothHapDevice;
private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
- private AudioSwitchPreferenceController mController;
+ private HandsFreeProfileOutputPreferenceController mController;
private List<BluetoothDevice> mProfileConnectedDevices;
private List<BluetoothDevice> mHearingAidActiveDevices;
@@ -478,4 +479,61 @@
assertThat(mController.findActiveDevice()).isNull();
}
+
+ /**
+ * One Bluetooth devices are available, and select the device.
+ * Preference summary should be device name.
+ */
+ @Test
+ public void onPreferenceChange_toBtDevice_shouldSetBtDeviceName() {
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+
+ mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ /**
+ * More than one Bluetooth devices are available, and select second device.
+ * Preference summary should be second device name.
+ */
+ @Test
+ public void onPreferenceChange_toBtDevices_shouldSetSecondBtDeviceName() {
+ ShadowBluetoothDevice shadowBluetoothDevice;
+ BluetoothDevice secondBluetoothDevice;
+ secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
+ shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
+ shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+ mController.mConnectedDevices.add(secondBluetoothDevice);
+
+ mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_2);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_2);
+ }
+
+ /**
+ * mConnectedDevices is empty.
+ * onPreferenceChange should return false.
+ */
+ @Test
+ public void onPreferenceChange_connectedDeviceIsNull_shouldReturnFalse() {
+ mController.mConnectedDevices.clear();
+
+ assertThat(mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1)).isFalse();
+ }
+
+ @Test
+ public void onPreferenceChange_toThisDevice_shouldSetDefaultSummary() {
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+
+ mController.onPreferenceChange(mPreference,
+ mContext.getText(R.string.media_output_default_summary));
+
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getText(R.string.media_output_default_summary));
+ }
}
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index 8c7faef..7fcd3d2 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -17,16 +17,15 @@
package com.android.settings.sound;
import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_EARPIECE;
import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.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;
@@ -34,9 +33,10 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
+import android.content.Intent;
import android.media.AudioManager;
-import androidx.preference.ListPreference;
+import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
@@ -49,11 +49,13 @@
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.MediaOutputSliceConstants;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -80,8 +82,6 @@
private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
- private final static long HISYNCID1 = 10;
- private final static long HISYNCID2 = 11;
@Mock
private LocalBluetoothManager mLocalManager;
@@ -98,7 +98,7 @@
private Context mContext;
private PreferenceScreen mScreen;
- private ListPreference mPreference;
+ private Preference mPreference;
private AudioManager mAudioManager;
private ShadowAudioManager mShadowAudioManager;
private BluetoothManager mBluetoothManager;
@@ -108,8 +108,8 @@
private BluetoothDevice mLeftBluetoothHapDevice;
private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
- private AudioSwitchPreferenceController mController;
- private List<BluetoothDevice> mProfileConnectedDevices;
+ private MediaOutputPreferenceController mController;
+ private List<BluetoothDevice> mProfileConnectableDevices;
private List<BluetoothDevice> mHearingAidActiveDevices;
@Before
@@ -149,8 +149,8 @@
mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
mScreen = spy(new PreferenceScreen(mContext, null));
- mPreference = new ListPreference(mContext);
- mProfileConnectedDevices = new ArrayList<>();
+ mPreference = new Preference(mContext);
+ mProfileConnectableDevices = new ArrayList<>();
mHearingAidActiveDevices = new ArrayList<>(2);
when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
@@ -166,61 +166,140 @@
ShadowBluetoothUtils.reset();
}
+
/**
- * In normal mode, bluetooth device with HisyncId.
- * HearingAidProfile should set active device to this device.
+ * A2DP Bluetooth device(s) are not connected nor previously connected
+ * Preference should be invisible
*/
@Test
- public void setActiveBluetoothDevice_btDeviceWithHisyncId_shouldSetBtDeviceActive() {
+ public void updateState_withoutConnectableBtDevice_preferenceInvisible() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ mProfileConnectableDevices.clear();
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ mPreference.setVisible(true);
- mController.setActiveBluetoothDevice(mLeftBluetoothHapDevice);
-
- verify(mHearingAidProfile).setActiveDevice(mLeftBluetoothHapDevice);
- verify(mA2dpProfile, never()).setActiveDevice(mLeftBluetoothHapDevice);
+ assertThat(mPreference.isVisible()).isTrue();
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isFalse();
}
/**
- * In normal mode, bluetooth device without HisyncId.
- * A2dpProfile should set active device to this device.
+ * A2DP Bluetooth device(s) are connectable, no matter active or inactive
+ * Preference should be visible
*/
@Test
- public void setActiveBluetoothDevice_btDeviceWithoutHisyncId_shouldSetBtDeviceActive() {
+ public void updateState_withConnectableBtDevice_preferenceVisible() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mProfileConnectableDevices.clear();
+ mProfileConnectableDevices.add(mBluetoothDevice);
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ assertThat(mPreference.isVisible()).isFalse();
- mController.setActiveBluetoothDevice(mBluetoothDevice);
+ // Without Active Bluetooth Device
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
- verify(mA2dpProfile).setActiveDevice(mBluetoothDevice);
- verify(mHearingAidProfile, never()).setActiveDevice(mBluetoothDevice);
+ // With Active Bluetooth Device
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
}
/**
- * In normal mode, set active device to "this device".
- * A2dpProfile should set to null.
- * HearingAidProfile should set to null.
+ * A2DP Bluetooth device(s) are connectable, but no device is set as activated
+ * Preference summary should be "This device"
*/
@Test
- public void setActiveBluetoothDevice_setNull_shouldSetNullToBothProfiles() {
+ public void updateState_withConnectableBtDevice_withoutActiveBtDevice_setDefaultSummary() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mProfileConnectableDevices.clear();
+ mProfileConnectableDevices.add(mBluetoothDevice);
+ mProfileConnectableDevices.add(mSecondBluetoothDevice);
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(null);
- mController.setActiveBluetoothDevice(null);
-
- verify(mA2dpProfile).setActiveDevice(null);
- verify(mHearingAidProfile).setActiveDevice(null);
+ assertThat(mPreference.getSummary()).isNull();
+ mController.updateState(mPreference);
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getText(R.string.media_output_default_summary));
}
/**
- * During a call
- * A2dpProfile should not set active device.
+ * A2DP Bluetooth device(s) are connected and active
+ * Preference summary should be device's name
*/
@Test
- public void setActiveBluetoothDevice_duringACall_shouldNotSetActiveDeviceToA2dpProfile() {
- mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ public void updateState_withActiveBtDevice_setActivatedDeviceName() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mProfileConnectableDevices.clear();
+ mProfileConnectableDevices.add(mBluetoothDevice);
+ mProfileConnectableDevices.add(mSecondBluetoothDevice);
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
- mController.setActiveBluetoothDevice(mBluetoothDevice);
+ assertThat(mPreference.getSummary()).isNull();
+ mController.updateState(mPreference);
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
- verify(mA2dpProfile, times(0)).setActiveDevice(any(BluetoothDevice.class));
+
+ /**
+ * Hearing Aid device(s) are connectable, no matter active or inactive
+ * Preference should be visible
+ */
+ @Test
+ public void updateState_withConnectableHADevice_preferenceVisible() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectableDevices()).thenReturn(mHearingAidActiveDevices);
+ assertThat(mPreference.isVisible()).isFalse();
+
+ // Without Active Hearing Aid Device
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
+
+ // With Active Hearing Aid Device
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
+ }
+
+ /**
+ * Hearing Aid device(s) are connected and active
+ * Preference summary should be device's name
+ */
+ @Test
+ public void updateState_withActiveHADevice_setActivatedDeviceName() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectableDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mPreference.getSummary()).isNull();
+ mController.updateState(mPreference);
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_HAP_DEVICE_NAME_1);
+
+ }
+
+ @Test
+ public void click_launch_outputSwitcherSlice() {
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ mController.handlePreferenceTreeClick(mPreference);
+ verify(mContext, never()).startActivity(intentCaptor.capture());
+
+ mPreference.setKey(TEST_KEY);
+ mController.handlePreferenceTreeClick(mPreference);
+ verify(mContext).startActivity(intentCaptor.capture());
+ assertThat(intentCaptor.getValue().getAction())
+ .isEqualTo(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT);
}
/**
@@ -254,24 +333,6 @@
}
/**
- * No available A2dp BT devices:
- * Preference should be invisible
- * Preference summary should be "This device"
- */
- @Test
- public void updateState_noAvailableA2dpBtDevices_shouldDisableAndSetDefaultSummary() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- List<BluetoothDevice> emptyDeviceList = new ArrayList<>();
- when(mA2dpProfile.getConnectedDevices()).thenReturn(emptyDeviceList);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isFalse();
- String defaultString = mContext.getString(R.string.media_output_default_summary);
- assertThat(mPreference.getSummary()).isEqualTo(defaultString);
- }
-
- /**
* Media stream is captured by something else (cast device):
* Preference should be invisible
* Preference summary should be "unavailable"
@@ -287,235 +348,6 @@
assertThat(mPreference.getSummary()).isEqualTo(defaultString);
}
- /**
- * One A2DP Bluetooth device is available and active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_oneA2dpBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
- }
-
- /**
- * More than one A2DP Bluetooth devices are available, and second device is active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_moreThanOneA2DpBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- mProfileConnectedDevices.add(mSecondBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(mSecondBluetoothDevice);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_2);
- }
-
- /**
- * A2DP Bluetooth device(s) are available, but wired headset is plugged in and activated
- * Preference should be visible
- * Preference summary should be "This device"
- */
- @Test
- public void updateState_a2dpDevicesAvailableWiredHeadsetIsActivated_shouldSetDefaultSummary() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(null);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(
- mContext.getString(R.string.media_output_default_summary));
- }
-
-
- /**
- * A2DP Bluetooth device(s) are available, but current device speaker is activated
- * Preference should be visible
- * Preference summary should be "This device"
- */
- @Test
- public void updateState_a2dpDevicesAvailableCurrentDeviceActivated_shouldSetDefaultSummary() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(null);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(
- mContext.getString(R.string.media_output_default_summary));
- }
-
- /**
- * One hearing aid profile Bluetooth device is available and active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_oneHapBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
- }
-
- /**
- * More than one hearing aid profile Bluetooth devices are available, and second
- * device is active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
- }
-
- /**
- * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
- * profile devices with same HisyncId are active. Both of HAP device are active,
- * "left" side HAP device is added first.
- * Preference should be visible
- * Preference summary should be the activated device name
- * ConnectedDevice should not contain second HAP device with same HisyncId
- */
- @Test
- public void updateState_hapBtDeviceWithSameId_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- //with same HisyncId, first one will remain in UI.
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
- assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isTrue();
- assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isFalse();
- }
-
- /**
- * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
- * profile devices with same HisyncId. Both of HAP device are active,
- * "right" side HAP device is added first.
- * Preference should be visible
- * Preference summary should be the activated device name
- * ConnectedDevice should not contain second HAP device with same HisyncId
- */
- @Test
- public void updateState_hapBtDeviceWithSameIdButDifferentOrder_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- //with same HisyncId, first one will remain in UI.
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
- assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isTrue();
- assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isFalse();
- }
-
- /**
- * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
- * profile devices with different HisyncId. One of HAP device is active.
- * Preference should be visible
- * Preference summary should be the activated device name
- * ConnectedDevice should contain both HAP device with different HisyncId
- */
- @Test
- public void updateState_hapBtDeviceWithDifferentId_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(null);
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
- assertThat(mController.mConnectedDevices).containsExactly(mBluetoothDevice,
- mLeftBluetoothHapDevice, mRightBluetoothHapDevice);
- }
-
@Test
public void findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice() {
when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDisclaimerItemFactory.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDisclaimerItemFactory.java
new file mode 100644
index 0000000..c50d4f1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDisclaimerItemFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.testutils.shadow;
+
+import android.content.Context;
+
+import com.android.settings.wifi.calling.DisclaimerItemFactory;
+import com.android.settings.wifi.calling.DisclaimerItem;
+
+import java.util.List;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(DisclaimerItemFactory.class)
+public final class ShadowDisclaimerItemFactory {
+ private static List<DisclaimerItem> sMockDisclaimerItemList;
+
+ public static void setDisclaimerItemList(List<DisclaimerItem> list) {
+ sMockDisclaimerItemList = list;
+ }
+
+ @Implementation
+ public static List<DisclaimerItem> create(Context context, int subId) {
+ return sMockDisclaimerItemList;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
index 8325777..78fb23f 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
@@ -19,15 +19,19 @@
import android.app.NotificationManager;
import android.net.Uri;
import android.service.notification.ZenModeConfig;
+import android.util.ArraySet;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.util.Set;
+
@Implements(NotificationManager.class)
public class ShadowNotificationManager {
private int mZenMode;
private ZenModeConfig mZenModeConfig;
+ private Set<String> mNotificationPolicyGrantedPackages = new ArraySet<>();
@Implementation
protected void setZenMode(int mode, Uri conditionId, String reason) {
@@ -40,6 +44,11 @@
}
@Implementation
+ protected boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
+ return mNotificationPolicyGrantedPackages.contains(pkg);
+ }
+
+ @Implementation
public ZenModeConfig getZenModeConfig() {
return mZenModeConfig;
}
@@ -47,4 +56,8 @@
public void setZenModeConfig(ZenModeConfig config) {
mZenModeConfig = config;
}
+
+ public void setNotificationPolicyAccessGrantedForPackage(String pkg) {
+ mNotificationPolicyGrantedPackages.add(pkg);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/widget/AdaptiveHomepageIconTest.java b/tests/robotests/src/com/android/settings/widget/AdaptiveIconTest.java
similarity index 80%
rename from tests/robotests/src/com/android/settings/widget/AdaptiveHomepageIconTest.java
rename to tests/robotests/src/com/android/settings/widget/AdaptiveIconTest.java
index 05a9cfb..1be3332 100644
--- a/tests/robotests/src/com/android/settings/widget/AdaptiveHomepageIconTest.java
+++ b/tests/robotests/src/com/android/settings/widget/AdaptiveIconTest.java
@@ -48,7 +48,7 @@
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
-public class AdaptiveHomepageIconTest {
+public class AdaptiveIconTest {
private Context mContext;
private ActivityInfo mActivityInfo;
@@ -64,8 +64,8 @@
@Test
public void createIcon_shouldSetBackgroundAndInset() {
- final AdaptiveHomepageIcon icon =
- new AdaptiveHomepageIcon(mContext, new ColorDrawable(Color.BLACK));
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
assertThat(icon.getNumberOfLayers()).isEqualTo(2);
assertThat(icon.getDrawable(0)).isInstanceOf(AdaptiveIconShapeDrawable.class);
@@ -73,8 +73,8 @@
@Test
public void setBackgroundColor_shouldUpdateColorFilter() {
- final AdaptiveHomepageIcon icon =
- spy(new AdaptiveHomepageIcon(mContext, new ColorDrawable(Color.BLACK)));
+ final AdaptiveIcon icon =
+ spy(new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)));
final ShapeDrawable background = mock(ShapeDrawable.class);
when(icon.getDrawable(0)).thenReturn(background);
@@ -89,8 +89,8 @@
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
doReturn(Icon.createWithResource(mContext, R.drawable.ic_settings_accent))
.when(tile).getIcon(mContext);
- final AdaptiveHomepageIcon icon =
- new AdaptiveHomepageIcon(mContext, new ColorDrawable(Color.BLACK));
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
icon.setBackgroundColor(mContext, tile);
assertThat(icon.mBackgroundColor).isEqualTo(0xff0000);
@@ -101,8 +101,8 @@
final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
doReturn(Icon.createWithResource(mContext, R.drawable.ic_settings_accent))
.when(tile).getIcon(mContext);
- final AdaptiveHomepageIcon icon =
- new AdaptiveHomepageIcon(mContext, new ColorDrawable(Color.BLACK));
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
icon.setBackgroundColor(mContext, tile);
@@ -118,11 +118,24 @@
doReturn(Icon.createWithResource(mContext, R.drawable.ic_settings_accent))
.when(tile).getIcon(mContext);
- final AdaptiveHomepageIcon icon =
- new AdaptiveHomepageIcon(mContext, new ColorDrawable(Color.BLACK));
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
icon.setBackgroundColor(mContext, tile);
assertThat(icon.mBackgroundColor)
.isEqualTo(mContext.getColor(R.color.material_blue_500));
}
+
+ @Test
+ public void getConstantState_returnCorrectState() {
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(Color.YELLOW);
+
+ final AdaptiveIcon.AdaptiveConstantState state =
+ (AdaptiveIcon.AdaptiveConstantState) icon.getConstantState();
+
+ assertThat(state.color).isEqualTo(Color.YELLOW);
+ assertThat(state.context).isEqualTo(mContext);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/wifi/ButtonPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/ButtonPreferenceTest.java
deleted file mode 100644
index 7f0598d..0000000
--- a/tests/robotests/src/com/android/settings/wifi/ButtonPreferenceTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.wifi;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.view.View;
-import android.widget.ImageButton;
-
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class ButtonPreferenceTest {
-
- private Context mContext;
- private View mRootView;
- private ButtonPreference mPref;
- private PreferenceViewHolder mHolder;
- private boolean mClicked;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mContext = RuntimeEnvironment.application;
- mPref = new ButtonPreference(mContext);
- mRootView = View.inflate(mContext, R.layout.wifi_button_preference_widget, /* parent */
- null);
- mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
- }
-
- @Test
- public void initButton_noIcon_shouldInvisible() {
- mPref.initButton(mHolder);
- assertThat(mRootView.findViewById(R.id.button_icon).getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void initButton_withIcon_shouldVisible() {
- mPref.setButtonIcon(R.drawable.ic_qrcode_24dp);
- mPref.initButton(mHolder);
- assertThat(mRootView.findViewById(R.id.button_icon).getVisibility()).isEqualTo(
- View.VISIBLE);
- }
-
- @Test
- public void initButton_whenClick_shouldCallback() {
- mClicked = false;
- mPref.setButtonIcon(R.drawable.ic_qrcode_24dp);
- mPref.setButtonOnClickListener((View v) -> {
- mClicked = true;
- });
- mPref.initButton(mHolder);
- ImageButton button = (ImageButton) mRootView.findViewById(R.id.button_icon);
- button.performClick();
- assertThat(mClicked).isTrue();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java
index 21b68f2..4202143 100644
--- a/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/NetworkRequestDialogFragmentTest.java
@@ -106,12 +106,12 @@
}
@Test
- public void clickPositiveButton_shouldCloseTheDialog() {
+ public void clickNegativeButton_shouldCloseTheDialog() {
networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null);
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(alertDialog.isShowing()).isTrue();
- Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE);
assertThat(positiveButton).isNotNull();
positiveButton.performClick();
@@ -186,26 +186,6 @@
}
@Test
- public void updateAccessPointList_onUserSelectionConnectFailure_shouldCallAbortDialog() {
- FakeNetworkRequestDialogFragment fakeFragment = new FakeNetworkRequestDialogFragment();
- FakeNetworkRequestDialogFragment spyFakeFragment = spy(fakeFragment);
- List<AccessPoint> accessPointList = createAccessPointList();
- when(spyFakeFragment.getAccessPointList()).thenReturn(accessPointList);
- spyFakeFragment.show(mActivity.getSupportFragmentManager(), null);
-
- AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
- assertThat(alertDialog.isShowing()).isTrue();
-
- // Test if config would update list.
- WifiConfiguration config = new WifiConfiguration();
- config.SSID = "Test AP 3";
- fakeFragment.onUserSelectionConnectFailure(config);
-
- assertThat(fakeFragment.bCalledStopAndPop).isTrue();
- assertThat(fakeFragment.errorType).isEqualTo(ERROR_DIALOG_TYPE.ABORT);
- }
-
- @Test
public void onUserSelectionCallbackRegistration_onClick_shouldCallSelect() {
// Assert.
final int indexClickItem = 3;
@@ -267,19 +247,27 @@
Bundle bundle = new Bundle();
bundle.putString(KEY_SSID, "Test AP 1");
- bundle.putInt(KEY_SECURITY, 1);
+ bundle.putInt(KEY_SECURITY, 1 /* WEP */);
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 2");
- bundle.putInt(KEY_SECURITY, 1);
+ bundle.putInt(KEY_SECURITY, 1 /* WEP */);
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 3");
- bundle.putInt(KEY_SECURITY, 2);
+ bundle.putInt(KEY_SECURITY, 1 /* WEP */);
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 4");
- bundle.putInt(KEY_SECURITY, 0);
+ bundle.putInt(KEY_SECURITY, 0 /* NONE */);
+ accessPointList.add(new AccessPoint(mContext, bundle));
+
+ bundle.putString(KEY_SSID, "Test AP 5");
+ bundle.putInt(KEY_SECURITY, 1 /* WEP */);
+ accessPointList.add(new AccessPoint(mContext, bundle));
+
+ bundle.putString(KEY_SSID, "Test AP 6");
+ bundle.putInt(KEY_SECURITY, 1 /* WEP */);
accessPointList.add(new AccessPoint(mContext, bundle));
return accessPointList;
@@ -300,9 +288,13 @@
networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), /* tag */ null);
final AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+
+ List<AccessPoint> accessPointList = createAccessPointList();
+ when(mWifiTracker.getAccessPoints()).thenReturn(accessPointList);
+
final String SSID_AP = "Test AP ";
final List<ScanResult> scanResults = new ArrayList<>();
- for (int i = 0; i < 6 ; i ++) {
+ for (int i = 0; i < 7 ; i ++) {
ScanResult scanResult = new ScanResult();
scanResult.SSID = SSID_AP + i;
scanResult.capabilities = "WEP";
@@ -338,4 +330,21 @@
// Check
assertThat(button.getVisibility()).isEqualTo(View.GONE);
}
+
+ @Test
+ public void cancelDialog_callsReject() {
+ // Assert
+ networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), /* tag */ null);
+ final AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ final NetworkRequestUserSelectionCallback selectionCallback = mock(
+ NetworkRequestUserSelectionCallback.class);
+ networkRequestDialogFragment.onUserSelectionCallbackRegistration(selectionCallback);
+
+ // Action
+ final Button button = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+ button.performClick();
+
+ // Check
+ verify(selectionCallback, times(1)).reject();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java
index 22ccd3c..c811b0c 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java
@@ -69,6 +69,7 @@
mWifiSettings = spy(new WifiSettings());
doReturn(mContext).when(mWifiSettings).getContext();
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+ mWifiSettings.mAddWifiNetworkPreference = new AddWifiNetworkPreference(mContext);
mWifiSettings.mSavedNetworksPreference = new Preference(mContext);
mWifiSettings.mConfigureWifiSettingsPreference = new Preference(mContext);
mWifiSettings.mWifiTracker = mWifiTracker;
@@ -151,4 +152,11 @@
assertThat(mWifiSettings.mConfigureWifiSettingsPreference.getSummary()).isEqualTo(
mContext.getString(R.string.wifi_configure_settings_preference_summary_wakeup_off));
}
-}
\ No newline at end of file
+
+ @Test
+ public void checkAddWifiNetworkPrefernce_preferenceVisible() {
+ assertThat(mWifiSettings.mAddWifiNetworkPreference.isVisible()).isTrue();
+ assertThat(mWifiSettings.mAddWifiNetworkPreference.getTitle()).isEqualTo(
+ mContext.getString(R.string.wifi_add_network));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/DisclaimerItemListAdapterTest.java b/tests/robotests/src/com/android/settings/wifi/calling/DisclaimerItemListAdapterTest.java
new file mode 100644
index 0000000..4cfc9ba
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/calling/DisclaimerItemListAdapterTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
+ .DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_TITLE;
+import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
+ .DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_DESCRIPTION;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class DisclaimerItemListAdapterTest {
+
+ private static final int ITEM_POSITION = 0;
+ private static final int DISCLAIMER_TITLE_STRING_ID = 0;
+ private static final int DISCLAIMER_MESSAGE_STRING_ID = 1;
+
+ @Mock
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private TextView mTestView;
+ @Mock
+ private TextView mDescView;
+ @Mock
+ private View mView;
+ @Mock
+ private ViewGroup mViewGroup;
+ @Mock
+ private Context mContext;
+
+ private MockDisclaimerItem mDisclaimerItem;
+ private DisclaimerItemListAdapter mDisclaimerItemListAdapter;
+ private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mDisclaimerItem = spy(new MockDisclaimerItem(mContext, 0 /* subId */));
+ mDisclaimerItemList.add(mDisclaimerItem);
+
+ when(mLayoutInflater.inflate(anyInt(), anyObject(), anyBoolean())).thenReturn(mView);
+ when(mViewGroup.getContext()).thenReturn(mContext);
+ when(mViewGroup.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(
+ mLayoutInflater);
+ when(mView.findViewById(ID_DISCLAIMER_ITEM_TITLE)).thenReturn(mTestView);
+ when(mView.findViewById(ID_DISCLAIMER_ITEM_DESCRIPTION)).thenReturn(mDescView);
+ }
+
+ @Test
+ public void onBindViewHolder_haveItem_shouldSetText() {
+ final DisclaimerItemListAdapter.DisclaimerItemViewHolder viewHolder =
+ new DisclaimerItemListAdapter.DisclaimerItemViewHolder(mView);
+
+ mDisclaimerItemListAdapter = new DisclaimerItemListAdapter(mDisclaimerItemList);
+ mDisclaimerItemListAdapter.onCreateViewHolder(mViewGroup, 0 /* viewType */);
+ mDisclaimerItemListAdapter.onBindViewHolder(viewHolder, ITEM_POSITION);
+
+ // Check the text is set when the DisclaimerItem exists.
+ verify(viewHolder.titleView).setText(DISCLAIMER_TITLE_STRING_ID);
+ verify(viewHolder.descriptionView).setText(DISCLAIMER_MESSAGE_STRING_ID);
+ }
+
+ private class MockDisclaimerItem extends DisclaimerItem {
+ MockDisclaimerItem(Context context, int subId) {
+ super(context, subId);
+ }
+
+ @Override
+ protected int getTitleId() {
+ return DISCLAIMER_TITLE_STRING_ID;
+ }
+
+ @Override
+ protected int getMessageId() {
+ return DISCLAIMER_MESSAGE_STRING_ID;
+ }
+
+ @Override
+ protected String getName() {
+ return "MockDisclaimerItem";
+ }
+
+ @Override
+ protected String getPrefKey() {
+ return "mock_pref_key";
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java
new file mode 100644
index 0000000..307c0ac
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Parcelable;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ListWithEntrySummaryPreferenceTest {
+
+ private Context mContext;
+ private ListWithEntrySummaryPreference mPreference;
+
+ private CharSequence[] mDefaultEntries =
+ {"default_entry1", "default_entry2", "default_entry3"};
+ private CharSequence[] mDefaultEntryValues = {"0", "1", "2"};
+ private CharSequence[] mDefaultEntrySummaries =
+ {"default_summary1", "default_summary2", "default_summary3"};
+
+ private CharSequence[] mCustomEntries = {"custom_entry1", "custom_entry2"};
+ private CharSequence[] mCustomEntryValues = {"0", "1"};
+ private CharSequence[] mCustomEntrySummaries = {"custom_summary1", "custom_summary2"};
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mContext.setTheme(R.style.Theme_Settings_Home);
+ mPreference = new ListWithEntrySummaryPreference(mContext, null);
+ mPreference.setEntries(mDefaultEntries);
+ mPreference.setEntryValues(mDefaultEntryValues);
+ mPreference.setEntrySummaries(mDefaultEntrySummaries);
+ }
+
+ @Test
+ public void initialize_defaultEntries_shouldDisplayDefalutEntries() {
+ AlertDialog dialog = showDialog(mPreference);
+ ListAdapter adapter = dialog.getListView().getAdapter();
+
+ int len = mDefaultEntries.length;
+ assertThat(adapter.getCount()).isEqualTo(len);
+ for (int i = 0; i < len; i++) {
+ TextView title = adapter.getView(i, null, null).findViewById(R.id.title);
+ TextView summary = adapter.getView(i, null, null).findViewById(R.id.summary);
+ assertThat(title.getText()).isEqualTo(mDefaultEntries[i]);
+ assertThat(summary.getText()).isEqualTo(mDefaultEntrySummaries[i]);
+ }
+ }
+
+ @Test
+ public void setEntries_customEntries_shouldUpdateEntries() {
+ mPreference.setEntries(mCustomEntries);
+ mPreference.setEntryValues(mCustomEntryValues);
+ mPreference.setEntrySummaries(mCustomEntrySummaries);
+
+ AlertDialog dialog = showDialog(mPreference);
+ ListAdapter adapter = dialog.getListView().getAdapter();
+
+ int len = mCustomEntries.length;
+ assertThat(adapter.getCount()).isEqualTo(len);
+ for (int i = 0; i < len; i++) {
+ TextView title = adapter.getView(i, null, null).findViewById(R.id.title);
+ TextView summary = adapter.getView(i, null, null).findViewById(R.id.summary);
+ assertThat(title.getText()).isEqualTo(mCustomEntries[i]);
+ assertThat(summary.getText()).isEqualTo(mCustomEntrySummaries[i]);
+ }
+ }
+
+ @Test
+ public void onSaveAndRestoreInstanceState_resumePreference_shouldNotChangeEntries() {
+ setEntries_customEntries_shouldUpdateEntries();
+
+ final Parcelable parcelable = mPreference.onSaveInstanceState();
+ ListWithEntrySummaryPreference preference
+ = new ListWithEntrySummaryPreference(mContext, null);
+ preference.setEntries(mDefaultEntries);
+ preference.setEntryValues(mDefaultEntryValues);
+ preference.setEntrySummaries(mDefaultEntrySummaries);
+ preference.onRestoreInstanceState(parcelable);
+
+ AlertDialog dialog = showDialog(preference);
+ ListAdapter adapter = dialog.getListView().getAdapter();
+
+ int len = mCustomEntries.length;
+ assertThat(adapter.getCount()).isEqualTo(len);
+ for (int i = 0; i < len; i++) {
+ TextView title = adapter.getView(i, null, null).findViewById(R.id.title);
+ TextView summary = adapter.getView(i, null, null).findViewById(R.id.summary);
+ assertThat(title.getText()).isEqualTo(mCustomEntries[i]);
+ assertThat(summary.getText()).isEqualTo(mCustomEntrySummaries[i]);
+ }
+ }
+
+ private AlertDialog showDialog(ListWithEntrySummaryPreference preference) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ preference.onPrepareDialogBuilder(builder, null);
+ return builder.show();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingDisclaimerFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingDisclaimerFragmentTest.java
new file mode 100644
index 0000000..6c221e7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingDisclaimerFragmentTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.calling;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDisclaimerItemFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowDisclaimerItemFactory.class)
+public class WifiCallingDisclaimerFragmentTest {
+
+ @Mock
+ private Activity mActivity;
+ @Mock
+ private DisclaimerItem mDisclaimerItem;
+ @Mock
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private View mView;
+ @Mock
+ private ViewGroup mViewGroup;
+ @Mock
+ private Button mAgreeButton;
+ @Mock
+ private Button mDisagreeButton;
+ @Mock
+ private RecyclerView mRecyclerView;
+
+ @Captor
+ ArgumentCaptor<OnClickListener> mOnClickListenerCaptor;
+ @Captor
+ ArgumentCaptor<OnScrollListener> mOnScrollListenerCaptor;
+
+ private WifiCallingDisclaimerFragment mFragment;
+ private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<>();
+ private List<DisclaimerItem> mEmptyDisclaimerItemList = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mActivity = Robolectric.setupActivity(Activity.class);
+ mFragment = spy(new WifiCallingDisclaimerFragment());
+
+ doReturn(mActivity).when(mFragment).getActivity();
+
+ when(mLayoutInflater.inflate(anyInt(), anyObject(), anyBoolean())).thenReturn(mView);
+ when(mView.findViewById(R.id.agree_button)).thenReturn(mAgreeButton);
+ when(mView.findViewById(R.id.disagree_button)).thenReturn(mDisagreeButton);
+ when(mView.findViewById(R.id.disclaimer_item_list)).thenReturn(mRecyclerView);
+ when(mView.getId()).thenReturn(R.id.agree_button);
+
+ mOnScrollListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class);
+ doNothing().when(mRecyclerView).addOnScrollListener(mOnScrollListenerCaptor.capture());
+ mOnClickListenerCaptor = ArgumentCaptor.forClass(OnClickListener.class);
+ doNothing().when(mAgreeButton).setOnClickListener(mOnClickListenerCaptor.capture());
+
+ mDisclaimerItemList.add(mDisclaimerItem);
+ }
+
+ @Test
+ public void onCreate_notHaveItem_shouldFinishFragment() {
+ ShadowDisclaimerItemFactory.setDisclaimerItemList(mEmptyDisclaimerItemList);
+
+ mFragment.onCreate(null /* savedInstanceState */);
+
+ // Check the fragment is finished when the DisclaimerItemList is empty.
+ verify(mFragment).finish(Activity.RESULT_OK);
+ }
+
+ @Test
+ public void onCreate_haveItem_shouldNotFinishFragment() {
+ ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
+
+ mFragment.onCreate(null /* savedInstanceState */);
+
+ // Check the fragment is not finished when the DisclaimerItemList is not empty.
+ verify(mFragment, never()).finish(Activity.RESULT_OK);
+ }
+
+ @Test
+ public void onScrolled_canNotScroll_shouldEnableAgreeButton() {
+ ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
+
+ when(mRecyclerView.canScrollVertically(1)).thenReturn(false);
+
+ mFragment.onCreate(null /* savedInstanceState */);
+ mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
+
+ mOnScrollListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 0);
+
+ // Check the agreeButton is enabled when the view is scrolled to the bottom end.
+ verify(mAgreeButton).setEnabled(true);
+ // Check the OnScrollListener is removed when the view is scrolled to the bottom end.
+ verify(mRecyclerView).removeOnScrollListener(any());
+ }
+
+ @Test
+ public void onScrolled_canScroll_shouldNotEnableAgreeButton() {
+ ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
+
+ when(mRecyclerView.canScrollVertically(1)).thenReturn(true);
+
+ mFragment.onCreate(null /* savedInstanceState */);
+ mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
+
+ mOnScrollListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 0);
+
+ // Check the agreeButton is not enabled when the view is not scrolled to the bottom end.
+ verify(mAgreeButton, never()).setEnabled(anyBoolean());
+ // Check the OnScrollListener is not removed when the view is not scrolled to
+ // the bottom end.
+ verify(mRecyclerView, never()).removeOnScrollListener(any());
+ }
+
+ @Test
+ public void onClick_agreeButton_shouldFinishFragment() {
+ ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
+
+ mFragment.onCreate(null /* savedInstanceState */);
+ mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
+
+ mOnClickListenerCaptor.getValue().onClick(mAgreeButton);
+
+ // Check the onAgreed callback is called when "CONTINUE" button is clicked.
+ verify(mDisclaimerItem).onAgreed();
+ // Check the WFC disclaimer fragment is finished when "CONTINUE" button is clicked.
+ verify(mFragment).finish(Activity.RESULT_OK);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
index 39de254..ae88231 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
@@ -16,11 +16,14 @@
package com.android.settings.wifi.calling;
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -31,6 +34,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Activity;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -41,20 +46,22 @@
import android.view.View;
import android.widget.TextView;
-import androidx.preference.ListPreference;
+import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.ims.ImsConfig;
-import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.widget.SwitchBar;
import com.android.settings.widget.ToggleSwitch;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -65,6 +72,8 @@
public class WifiCallingSettingsForSubTest {
private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
+ private static final String TEST_EMERGENCY_ADDRESS_CARRIER_APP =
+ "com.android.settings/.wifi.calling.TestEmergencyAddressCarrierApp";
private TestFragment mFragment;
private Context mContext;
@@ -72,6 +81,7 @@
private final PersistableBundle mBundle = new PersistableBundle();
@Mock private static CarrierConfigManager sCarrierConfigManager;
+ @Mock private CarrierConfigManager mMockConfigManager;
@Mock private ImsManager mImsManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private PreferenceScreen mPreferenceScreen;
@@ -80,11 +90,12 @@
@Mock private ToggleSwitch mToggleSwitch;
@Mock private View mView;
@Mock private ImsConfig mImsConfig;
- @Mock private ListPreference mButtonWfcMode;
- @Mock private ListPreference mButtonWfcRoamingMode;
+ @Mock private ListWithEntrySummaryPreference mButtonWfcMode;
+ @Mock private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
+ @Mock private Preference mUpdateAddress;
@Before
- public void setUp() throws NoSuchFieldException, ImsException {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
@@ -100,7 +111,7 @@
final Bundle bundle = new Bundle();
when(mFragment.getArguments()).thenReturn(bundle);
doNothing().when(mFragment).addPreferencesFromResource(anyInt());
- doReturn(mock(ListPreference.class)).when(mFragment).findPreference(any());
+ doReturn(mock(ListWithEntrySummaryPreference.class)).when(mFragment).findPreference(any());
doReturn(mButtonWfcMode).when(mFragment).findPreference(BUTTON_WFC_MODE);
doReturn(mButtonWfcRoamingMode).when(mFragment).findPreference(BUTTON_WFC_ROAMING_MODE);
doNothing().when(mFragment).finish();
@@ -123,6 +134,11 @@
doReturn(mBundle).when(sCarrierConfigManager).getConfigForSubId(anyInt());
setDefaultCarrierConfigValues();
+ doReturn(sCarrierConfigManager).when(mActivity).getSystemService(
+ CarrierConfigManager.class);
+ doReturn(mContext.getResources()).when(mFragment).getResourcesForSubId();
+ doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt());
+
mFragment.onAttach(mContext);
mFragment.onCreate(null);
mFragment.onActivityCreated(null);
@@ -133,6 +149,9 @@
CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false);
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, true);
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, true);
+ mBundle.putString(
+ CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING,
+ TEST_EMERGENCY_ADDRESS_CARRIER_APP);
}
@Test
@@ -141,7 +160,7 @@
}
@Test
- public void onResume_provisioningAllowed_shouldNotFinish() throws ImsException {
+ public void onResume_provisioningAllowed_shouldNotFinish() {
// Call onResume while provisioning is allowed.
mFragment.onResume();
@@ -160,7 +179,7 @@
}
@Test
- public void onResumeOnPause_provisioningCallbackRegistration() throws ImsException {
+ public void onResumeOnPause_provisioningCallbackRegistration() throws Exception {
// Verify that provisioning callback is registered after call to onResume().
mFragment.onResume();
verify(mImsConfig).addConfigCallback(any(ProvisioningManager.Callback.class));
@@ -261,6 +280,61 @@
eq(true));
}
+ @Test
+ public void onSwitchChanged_enableSetting_shouldLaunchWfcDisclaimerFragment() {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ mFragment.onSwitchChanged(null, true);
+
+ // Check the WFC disclaimer fragment is launched.
+ verify(mFragment).startActivityForResult(intentCaptor.capture(),
+ eq(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_DISCLAIMER));
+ Intent intent = intentCaptor.getValue();
+ assertThat(intent.getStringExtra(EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(WifiCallingDisclaimerFragment.class.getName());
+ }
+
+ @Test
+ public void onActivityResult_finishWfcDisclaimerFragment_shouldLaunchCarrierActivity() {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ // Emulate the WfcDisclaimerActivity finish.
+ mFragment.onActivityResult(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_DISCLAIMER,
+ Activity.RESULT_OK, null);
+
+ // Check the WFC emergency address activity is launched.
+ verify(mFragment).startActivityForResult(intentCaptor.capture(),
+ eq(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_EMERGENCY_ADDRESS));
+ Intent intent = intentCaptor.getValue();
+ assertEquals(intent.getComponent(), ComponentName.unflattenFromString(
+ TEST_EMERGENCY_ADDRESS_CARRIER_APP));
+ }
+
+ @Test
+ public void onActivityResult_finishCarrierActivity_shouldShowWfcPreference() {
+ ReflectionHelpers.setField(mFragment, "mButtonWfcMode", mButtonWfcMode);
+ ReflectionHelpers.setField(mFragment, "mButtonWfcRoamingMode", mButtonWfcRoamingMode);
+ ReflectionHelpers.setField(mFragment, "mUpdateAddress", mUpdateAddress);
+
+ mFragment.onActivityResult(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_EMERGENCY_ADDRESS,
+ Activity.RESULT_OK, null);
+
+ // Check the WFC preferences is added.
+ verify(mPreferenceScreen).addPreference(mButtonWfcMode);
+ verify(mPreferenceScreen).addPreference(mButtonWfcRoamingMode);
+ verify(mPreferenceScreen).addPreference(mUpdateAddress);
+ // Check the WFC enable request.
+ verify(mImsManager).setWfcSetting(true);
+ }
+
+ @Test
+ public void onSwitchChanged_disableSetting_shouldNotLaunchWfcDisclaimerFragment() {
+ mFragment.onSwitchChanged(null, false);
+
+ // Check the WFC disclaimer fragment is not launched.
+ verify(mFragment, never()).startActivityForResult(any(Intent.class), anyInt());
+ }
+
protected class TestFragment extends WifiCallingSettingsForSub {
@Override
protected Object getSystemService(final String name) {
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
index f9109ce..644e5e8 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
@@ -22,7 +22,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -48,7 +47,6 @@
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
import com.android.settings.R;
-import com.android.settings.slices.CustomSliceManager;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SliceBroadcastReceiver;
@@ -99,9 +97,6 @@
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
- CustomSliceManager manager = new CustomSliceManager(mContext);
- when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class)))
- .thenReturn(manager);
mWfcSliceHelper = new FakeWifiCallingSliceHelper(mContext);
diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
index e640dee..574d0d6 100644
--- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
@@ -649,6 +649,19 @@
nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
updateNetworkCapabilities(nc);
inOrder.verify(mockHeaderController).setSummary(summary);
+
+ // UI will be refreshed when device connects to a partial connectivity network.
+ summary = "Limited connection";
+ when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mockHeaderController).setSummary(summary);
+
+ // Although UI will be refreshed when network become validated. The Settings should
+ // continue to display "Limited connection" if network still provides partial connectivity.
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mockHeaderController).setSummary(summary);
}
@Test
@@ -758,7 +771,9 @@
}
@Test
- public void forgetNetwork_Passpoint() {
+ public void forgetNetwork_v1_Passpoint() {
+ FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, false);
+
mockWifiConfig.networkId = 5;
when(mockWifiConfig.isPasspoint()).thenReturn(true);
@@ -776,9 +791,9 @@
mockWifiConfig.networkId = 5;
when(mockWifiConfig.isPasspoint()).thenReturn(true);
+ spyController.displayPreference(mockScreen);
FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, true);
- spyController.displayPreference(mockScreen);
mForgetClickListener.getValue().onClick(null);
verify(mockWifiManager, times(0)).removePasspointConfiguration(mockWifiConfig.FQDN);
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java
index 6ede989..746456e 100644
--- a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java
@@ -82,6 +82,8 @@
@Test
public void onForget_isPasspointConfig_shouldRefreshAPList() {
+ FeatureFlagPersistent.setEnabled(RuntimeEnvironment.application,
+ FeatureFlags.NETWORK_INTERNET_V2, false);
when(mAccessPoint.isPasspointConfig()).thenReturn(true);
ReflectionHelpers.setField(mSettings, "mSelectedAccessPoint", mAccessPoint);
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
index d681afe..520d988 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
@@ -36,6 +36,8 @@
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;
@@ -52,11 +54,15 @@
private ContentResolver mResolver;
private WifiManager mWifiManager;
private ContextualWifiSlice mWifiSlice;
+ private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.application);
mResolver = mock(ContentResolver.class);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mFeatureFactory.slicesFeatureProvider = new SlicesFeatureProviderImpl();
+ mFeatureFactory.slicesFeatureProvider.newUiSession();
doReturn(mResolver).when(mContext).getContentResolver();
mWifiManager = mContext.getSystemService(WifiManager.class);
@@ -65,10 +71,28 @@
mWifiManager.setWifiEnabled(true);
mWifiSlice = new ContextualWifiSlice(mContext);
+ mWifiSlice.sPreviouslyDisplayed = false;
}
@Test
public void getWifiSlice_hasActiveConnection_shouldReturnNull() {
+ mWifiSlice.sPreviouslyDisplayed = false;
+ final WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "123";
+ mWifiManager.connect(config, null /* listener */);
+
+ final Slice wifiSlice = mWifiSlice.getSlice();
+
+ assertThat(wifiSlice).isNull();
+ }
+
+ @Test
+ public void getWifiSlice_newSession_hasActiveConnection_shouldReturnNull() {
+ // Session: use a non-active value
+ // previous displayed: yes
+ mWifiSlice.sPreviouslyDisplayed = true;
+ mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+
final WifiConfiguration config = new WifiConfiguration();
config.SSID = "123";
mWifiManager.connect(config, null /* listener */);
@@ -80,7 +104,8 @@
@Test
public void getWifiSlice_previousDisplayed_hasActiveConnection_shouldHaveTitleAndToggle() {
- mWifiSlice.mPreviouslyDisplayed = true;
+ mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+ mWifiSlice.sPreviouslyDisplayed = true;
final WifiConfiguration config = new WifiConfiguration();
config.SSID = "123";
mWifiManager.connect(config, null /* listener */);
@@ -101,7 +126,8 @@
@Test
public void getWifiSlice_contextualWifiSlice_shouldReturnContextualWifiSliceUri() {
- mWifiSlice.mPreviouslyDisplayed = true;
+ mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+ mWifiSlice.sPreviouslyDisplayed = true;
final Slice wifiSlice = mWifiSlice.getSlice();
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
index 01feb8e..75a9e11 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
@@ -34,8 +34,10 @@
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
import android.net.Uri;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -61,6 +63,7 @@
import org.robolectric.annotation.Implements;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@@ -253,11 +256,42 @@
verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
}
- @Test
- public void onConnectedChanged_shouldNotifyChange() {
- mWifiScanWorker.onConnectedChanged();
+ private AccessPoint createAccessPoint(String name, State state) {
+ final NetworkInfo info = mock(NetworkInfo.class);
+ doReturn(state).when(info).getState();
- verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
+ final Bundle savedState = new Bundle();
+ savedState.putString("key_ssid", name);
+ savedState.putParcelable("key_networkinfo", info);
+ return new AccessPoint(mContext, savedState);
+ }
+
+ @Test
+ public void SliceAccessPoint_sameState_shouldBeTheSame() {
+ final AccessPoint ap1 = createAccessPoint(AP1_NAME, State.CONNECTED);
+ final AccessPoint ap2 = createAccessPoint(AP1_NAME, State.CONNECTED);
+
+ assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2)))
+ .isTrue();
+ }
+
+ @Test
+ public void SliceAccessPoint_differentState_shouldBeDifferent() {
+ final AccessPoint ap1 = createAccessPoint(AP1_NAME, State.CONNECTING);
+ final AccessPoint ap2 = createAccessPoint(AP1_NAME, State.CONNECTED);
+
+ assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2)))
+ .isFalse();
+ }
+ @Test
+ public void SliceAccessPoint_differentLength_shouldBeDifferent() {
+ final AccessPoint ap1 = createAccessPoint(AP1_NAME, State.CONNECTED);
+ final AccessPoint ap2 = createAccessPoint(AP1_NAME, State.CONNECTED);
+ final List<AccessPoint> list = new ArrayList<>();
+ list.add(ap1);
+ list.add(ap2);
+
+ assertThat(mWifiScanWorker.areListsTheSame(list, Arrays.asList(ap1))).isFalse();
}
@Implements(SliceBackgroundWorker.class)
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
index d8cd878a..06716db 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
@@ -112,36 +112,6 @@
}
@Test
- public void testReceiver_turnOnAirplaneMode_clearPreferenceSummary() {
- final ContentResolver cr = mock(ContentResolver.class);
- when(mContext.getContentResolver()).thenReturn(cr);
- Settings.Global.putInt(cr, Settings.Global.AIRPLANE_MODE_ON, 1);
- mController.displayPreference(mScreen);
- final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
- final Intent broadcast = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-
- receiver.onReceive(RuntimeEnvironment.application, broadcast);
-
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- "Unavailable because airplane mode is turned on");
- }
-
- @Test
- public void testReceiver_turnOffAirplaneMode_displayOffSummary() {
- final ContentResolver cr = mock(ContentResolver.class);
- when(mContext.getContentResolver()).thenReturn(cr);
- Settings.Global.putInt(cr, Settings.Global.AIRPLANE_MODE_ON, 0);
- mController.displayPreference(mScreen);
- final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
- final Intent broadcast = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-
- receiver.onReceive(RuntimeEnvironment.application, broadcast);
-
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- "Not sharing internet or content with other devices");
- }
-
- @Test
public void testHandleWifiApStateChanged_stateEnabling_showEnablingSummary() {
mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0 /* reason */);
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
index cb58778..3d15197 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiManager;
import androidx.preference.PreferenceScreen;
@@ -56,12 +57,12 @@
private PreferenceScreen mScreen;
private WifiTetherSSIDPreferenceController mController;
- private ValidatedEditTextPreference mPreference;
+ private WifiTetherSsidPreference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mPreference = new ValidatedEditTextPreference(RuntimeEnvironment.application);
+ mPreference = new WifiTetherSsidPreference(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
@@ -121,4 +122,28 @@
assertThat(mController.getSSID()).isEqualTo(config.SSID);
assertThat(mPreference.getSummary()).isEqualTo(config.SSID);
}
+
+ @Test
+ public void displayPreference_wifiApDisabled_shouldHideQrCodeIcon() {
+ when(mWifiManager.isWifiApEnabled()).thenReturn(false);
+ final WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "test_1234";
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ when(mWifiManager.getWifiApConfiguration()).thenReturn(config);
+
+ mController.displayPreference(mScreen);
+ assertThat(mController.isQrCodeButtonAvailable()).isEqualTo(false);
+ }
+
+ @Test
+ public void displayPreference_wifiApEnabled_shouldShowQrCodeIcon() {
+ when(mWifiManager.isWifiApEnabled()).thenReturn(true);
+ final WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "test_1234";
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ when(mWifiManager.getWifiApConfiguration()).thenReturn(config);
+
+ mController.displayPreference(mScreen);
+ assertThat(mController.isQrCodeButtonAvailable()).isEqualTo(true);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java
index 1a5a228..30358da 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java
@@ -73,17 +73,7 @@
mController = new WifiTetherSwitchBarController(mContext,
new SwitchBarController(mSwitchBar));
}
-
- @Test
- public void constructor_airplaneModeOn_switchBarDisabled() {
- Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 1);
-
- new WifiTetherSwitchBarController(mContext, new SwitchBarController(mSwitchBar));
-
- assertThat(mSwitchBar.isEnabled()).isFalse();
- }
-
+
@Test
public void startTether_fail_resetSwitchBar() {
when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(false);
diff --git a/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
index 447e2b4..653e55e 100644
--- a/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
+++ b/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
@@ -86,6 +86,7 @@
private ContextualCard getContextualCard(Uri sliceUri) {
return new ContextualCard.Builder()
.setName("test_card")
+ .setRankingScore(0.5f)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(sliceUri)
.build();
diff --git a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
index 405ed4a..38211b3 100644
--- a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
+++ b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
@@ -147,7 +147,7 @@
@Test
public void rotateScreen_shouldGetCorrectWifiNetworkConfig() {
final WifiNetworkConfig wifiNetworkConfig = new WifiNetworkConfig("WPA", "WifiSsid",
- "password", /* hiddenSsid */ false, /* networkId */ 0);
+ "password", /* hiddenSsid */ false, /* networkId */ 0, /* isHotspot */ true);
final Intent intent = new Intent(Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI);
intent.setData(Uri.parse(VALID_WIFI_DPP_QR_CODE));
@@ -173,5 +173,6 @@
assertThat(restoredWifiNetworkConfig.getPreSharedKey()).isEqualTo("password");
assertThat(restoredWifiNetworkConfig.getHiddenSsid()).isFalse();
assertThat(restoredWifiNetworkConfig.getNetworkId()).isEqualTo(0);
+ assertThat(restoredWifiNetworkConfig.isHotspot()).isTrue();
}
}