Merge "[Network Connection] Implement new UI changing"
diff --git a/Android.mk b/Android.mk
index e385b34..283575d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -49,6 +49,7 @@
settings-contextual-card-protos-lite \
contextualcards \
settings-logtags \
+ statsdprotolite \
zxing-core-1.7
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7625e78..5b78681 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -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">
@@ -3124,6 +3137,18 @@
<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>
<!-- This is the longest AndroidManifest.xml ever. -->
</application>
</manifest>
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index e9f8d58..7074481 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>
@@ -1081,7 +969,7 @@
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,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/colors.xml"
- line="138"
+ line="141"
column="5"/>
</issue>
@@ -1653,12 +1589,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 +1605,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 +1621,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 +1637,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 +1653,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 +1669,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 +1685,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 +1701,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 +1717,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 +1733,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 +1749,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 +1765,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 +1781,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 +1797,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 +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_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 +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_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 +1845,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 +2453,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 +2473,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rAU/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2473,7 +2489,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rCA/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2489,7 +2505,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rGB/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2505,7 +2521,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rIN/strings.xml"
- line="2534"
+ line="2581"
column="64"/>
</issue>
@@ -2521,7 +2537,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/strings.xml"
- line="5885"
+ line="6026"
column="36"/>
</issue>
@@ -2537,7 +2553,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="174"
+ line="163"
column="41"/>
</issue>
@@ -2553,7 +2569,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="388"
+ line="380"
column="44"/>
</issue>
@@ -2569,7 +2585,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="394"
+ line="386"
column="44"/>
</issue>
@@ -2585,7 +2601,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="395"
+ line="387"
column="44"/>
</issue>
@@ -2601,7 +2617,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="430"
+ line="423"
column="34"/>
</issue>
@@ -2729,7 +2745,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="166"
+ line="169"
column="45"/>
</issue>
@@ -2745,7 +2761,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="167"
+ line="170"
column="49"/>
</issue>
@@ -2761,7 +2777,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="175"
+ line="178"
column="45"/>
</issue>
@@ -2777,7 +2793,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="176"
+ line="179"
column="49"/>
</issue>
@@ -2793,7 +2809,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="192"
+ line="195"
column="39"/>
</issue>
@@ -2809,7 +2825,7 @@
errorLine2=" ^">
<location
file="res/values/themes.xml"
- line="193"
+ line="196"
column="40"/>
</issue>
@@ -2825,7 +2841,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/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/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/homepage_condition_footer.xml b/res/layout/homepage_condition_footer.xml
index cc84f52..56687fe 100644
--- a/res/layout/homepage_condition_footer.xml
+++ b/res/layout/homepage_condition_footer.xml
@@ -29,7 +29,6 @@
android:id="@+id/collapse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/ic_expand_less"
- android:tint="?android:attr/colorAccent"/>
+ android:src="@drawable/ic_expand_less"/>
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/homepage_condition_header.xml b/res/layout/homepage_condition_header.xml
index 5c1b181..5460be9 100644
--- a/res/layout/homepage_condition_header.xml
+++ b/res/layout/homepage_condition_header.xml
@@ -45,8 +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:tint="?android:attr/colorAccent"/>
+ 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..b2badf5 100644
--- a/res/layout/homepage_slice_tile.xml
+++ b/res/layout/homepage_slice_tile.xml
@@ -32,6 +32,7 @@
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/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/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_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/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 0999d20..4b004e3 100644
--- a/res/layout/wifi_button_preference_widget.xml
+++ b/res/layout/wifi_button_preference_widget.xml
@@ -22,5 +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:background="?android:attr/selectableItemBackground"/>
diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml
index 333296c..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_dpp_scan_qr_code"/>
- </RelativeLayout>
+ </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_dpp_scan_qr_code"/>
- </RelativeLayout>
+ </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/values/colors.xml b/res/values/colors.xml
index 7b55a2b..0afd288 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>
@@ -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>
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 a36d8ab..ef6fc4f 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>
@@ -389,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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 165ee0b..631d912 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3022,14 +3022,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] -->
@@ -3581,10 +3581,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 -->
@@ -3633,12 +3633,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 -->
@@ -3754,13 +3748,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
@@ -3771,8 +3777,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-->
@@ -4515,8 +4525,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>
@@ -7771,7 +7781,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>
@@ -7780,7 +7790,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>
@@ -7920,6 +7939,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>
@@ -9306,6 +9328,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>
@@ -9867,8 +9895,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>
@@ -10684,6 +10710,9 @@
<!-- Title for the Volume dialog (settings panel) with all volume streams[CHAR LIMIT=50] -->
<string name="volume_connectivity_panel_title">Volume</string>
+ <!-- Subtitle explaining that mobile data cannot be used while airplane mode is on [CHAR LIMIT=50] -->
+ <string name="mobile_data_ap_mode_disabled">Unavailable during airplane mode</string>
+
<!-- UI debug setting: force desktop mode [CHAR LIMIT=50] -->
<string name="force_desktop_mode">Force desktop mode</string>
<!-- UI debug setting: force desktop mode summary [CHAR LIMIT=NONE] -->
@@ -10734,12 +10763,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] -->
@@ -10749,12 +10778,23 @@
</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">You may lose access to any remaining time or data. Check with your provider before removing.</string>
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_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/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/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/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/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..495d35c 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.
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..ead0d6a 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -96,6 +96,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 +160,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/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/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/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/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..b836e55 100644
--- a/src/com/android/settings/backup/BackupSettingsHelper.java
+++ b/src/com/android/settings/backup/BackupSettingsHelper.java
@@ -209,7 +209,7 @@
}
/** Checks if backup service is enabled for this user. */
- private boolean isBackupServiceActive() {
+ public boolean isBackupServiceActive() {
boolean backupOkay;
try {
backupOkay = mBackupManager.isBackupServiceActive(UserHandle.myUserId());
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/biometrics/face/FaceEnrollAccessibilityToggle.java b/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityToggle.java
index 49f4f7f..2db654b 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityToggle.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityToggle.java
@@ -58,9 +58,14 @@
a.recycle();
}
mSwitch = findViewById(R.id.toggle);
+ mSwitch.setChecked(false);
}
public boolean isChecked() {
return mSwitch.isChecked();
}
+
+ public void setChecked(boolean checked) {
+ mSwitch.setChecked(checked);
+ }
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index 5f2b675..c7966f2 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -53,6 +53,7 @@
final LinearLayout accessibilityLayout = findViewById(R.id.accessibility_layout);
final Button accessibilityButton = findViewById(R.id.accessibility_button);
accessibilityButton.setOnClickListener(view -> {
+ mSwitchDiversity.setChecked(true);
accessibilityButton.setVisibility(View.INVISIBLE);
accessibilityLayout.setVisibility(View.VISIBLE);
});
@@ -177,7 +178,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/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..d522918 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;
@@ -135,6 +136,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 +210,7 @@
UserSettings.class.getName(),
NotificationAccessSettings.class.getName(),
ZenAccessSettings.class.getName(),
+ ZenAccessDetails.class.getName(),
ZenModeAutomationSettings.class.getName(),
PrintSettingsFragment.class.getName(),
PrintJobSettingsFragment.class.getName(),
@@ -260,6 +263,7 @@
ConnectedDeviceDashboardFragment.class.getName(),
UsbDetailsFragment.class.getName(),
AppAndNotificationDashboardFragment.class.getName(),
+ WifiCallingDisclaimerFragment.class.getName(),
AccountDashboardFragment.class.getName(),
EnterprisePrivacySettings.class.getName(),
WebViewAppPicker.class.getName(),
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/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/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/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..1f5f7dd 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -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) {
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index c829015..7df322d 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,6 +39,7 @@
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;
@@ -109,13 +111,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 +197,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 +208,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;
}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
index e598e4c..72ddb50 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java
@@ -19,16 +19,19 @@
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 {
@@ -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
diff --git a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
index fe68d02..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,7 +50,32 @@
@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
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/location/RecentLocationAccessPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
index 44d600e..a76d381 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_group.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/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 232986c..e76bc88 100644
--- a/src/com/android/settings/media/MediaOutputSlice.java
+++ b/src/com/android/settings/media/MediaOutputSlice.java
@@ -76,6 +76,11 @@
return null;
}
+ if (getWorker() == null) {
+ Log.d(TAG, "getSlice() Can not get worker through uri!");
+ return null;
+ }
+
final List<MediaDevice> devices = getMediaDevices();
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
@@ -115,13 +120,15 @@
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;
}
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/telephony/MobileDataSlice.java b/src/com/android/settings/network/telephony/MobileDataSlice.java
index 6efd8c3..5a5d6a2 100644
--- a/src/com/android/settings/network/telephony/MobileDataSlice.java
+++ b/src/com/android/settings/network/telephony/MobileDataSlice.java
@@ -74,8 +74,27 @@
final IconCompat icon = IconCompat.createWithResource(mContext,
R.drawable.ic_network_cell);
final String title = mContext.getText(R.string.mobile_data_settings_title).toString();
- final CharSequence summary = getSummary();
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
+
+ // Return a Slice without the mobile data toggle when airplane mode is on.
+ if (isAirplaneModeEnabled()) {
+ final CharSequence summary = mContext.getText(R.string.mobile_data_ap_mode_disabled);
+ // Intent does nothing, but we have to pass an intent to the Row.
+ final PendingIntent intent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
+ new Intent(), 0 /* flags */);
+ final SliceAction deadAction =
+ SliceAction.create(intent, icon, ListBuilder.ICON_IMAGE, title);
+ final ListBuilder listBuilder = new ListBuilder(mContext, getUri(),
+ ListBuilder.INFINITY)
+ .setAccentColor(color)
+ .addRow(new ListBuilder.RowBuilder()
+ .setTitle(title)
+ .setSubtitle(summary)
+ .setPrimaryAction(deadAction));
+ return listBuilder.build();
+ }
+
+ final CharSequence summary = getSummary();
final PendingIntent toggleAction = getBroadcastIntent(mContext);
final PendingIntent primaryAction = getPrimaryAction();
final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon,
@@ -101,11 +120,6 @@
@Override
public void onNotifyChange(Intent intent) {
- // Don't make a change if we are in Airplane Mode.
- if (isAirplaneModeEnabled()) {
- return;
- }
-
final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
isMobileDataEnabled());
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/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/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/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/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/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/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/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/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..6b1ead7 100644
--- a/src/com/android/settings/slices/CustomSliceRegistry.java
+++ b/src/com/android/settings/slices/CustomSliceRegistry.java
@@ -24,11 +24,31 @@
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.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 +73,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.
*/
@@ -287,4 +299,61 @@
.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_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..3187d10 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -118,9 +118,6 @@
private static final KeyValueListParser KEY_VALUE_LIST_PARSER = new KeyValueListParser(',');
@VisibleForTesting
- CustomSliceManager mCustomSliceManager;
-
- @VisibleForTesting
SlicesDatabaseAccessor mSlicesDatabaseAccessor;
@VisibleForTesting
@@ -140,15 +137,15 @@
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);
@@ -194,10 +191,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)) {
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/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 5169d7a..0000000
--- a/src/com/android/settings/wifi/ButtonPreference.java
+++ /dev/null
@@ -1,135 +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;
- private String mContentDescription;
-
- // 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;
- mContentDescription = 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);
- mImageButton.setContentDescription(mContentDescription);
- }
- setButtonVisibility();
- }
-
- private void setButtonVisibility() {
- if(mImageButton != null) {
- mImageButton.setVisibility(mButtonIcon == null ? View.GONE : View.VISIBLE);
- }
- }
-
- /**
- * Sets the drawable to be displayed in button.
- */
- public void setButtonIcon(@DrawableRes int iconResId) {
- if (iconResId == 0) {
- return;
- }
-
- try {
- mButtonIcon = getContext().getDrawable(iconResId);
- notifyChanged();
- } catch (Resources.NotFoundException exception) {
- Log.e(TAG, "Resource does not exist: " + iconResId);
- }
- }
-
- /**
- * Register a callback to be invoked when button is clicked.
- */
- public void setButtonOnClickListener(View.OnClickListener listener) {
- if (listener != mClickListener) {
- mClickListener = listener;
- notifyChanged();
- }
- }
-
- /**
- * A content description briefly describes the button and is primarily used for accessibility
- * support to determine how a button should be presented to the user.
- */
- public void setButtonContentDescription(String contentDescription) {
- if (contentDescription != mContentDescription) {
- mContentDescription = contentDescription;
- notifyChanged();
- }
- }
-}
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/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index a8fc34d..585cf71 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -179,7 +179,8 @@
private PreferenceCategory mConnectedAccessPointPreferenceCategory;
private PreferenceCategory mAccessPointsPreferenceCategory;
- private ButtonPreference mAddPreference;
+ @VisibleForTesting
+ AddWifiNetworkPreference mAddWifiNetworkPreference;
@VisibleForTesting
Preference mConfigureWifiSettingsPreference;
@VisibleForTesting
@@ -237,20 +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));
- });
- mAddPreference.setButtonContentDescription(getString(R.string.wifi_dpp_scan_qr_code));
+ mAddWifiNetworkPreference = new AddWifiNetworkPreference(getPrefContext());
mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE);
-
mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());
}
@@ -590,7 +579,7 @@
showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
break;
}
- } else if (preference == mAddPreference) {
+ } else if (preference == mAddWifiNetworkPreference) {
onAddNetworkPressed();
} else {
return super.onPreferenceTreeClick(preference);
@@ -809,8 +798,8 @@
}
}
removeCachedPrefs(mAccessPointsPreferenceCategory);
- mAddPreference.setOrder(index);
- mAccessPointsPreferenceCategory.addPreference(mAddPreference);
+ mAddWifiNetworkPreference.setOrder(index);
+ mAccessPointsPreferenceCategory.addPreference(mAddWifiNetworkPreference);
setAdditionalSettingsSummaries();
if (!hasAvailableAccessPoints) {
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/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 4255f3c..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";
@@ -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,14 +264,13 @@
mTelephonyManager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
.createForSubscriptionId(mSubId);
- mButtonWfcMode = (ListWithEntrySummaryPreference) findPreference(BUTTON_WFC_MODE);
+ mButtonWfcMode = findPreference(BUTTON_WFC_MODE);
mButtonWfcMode.setOnPreferenceChangeListener(this);
- mButtonWfcRoamingMode = (ListWithEntrySummaryPreference) 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();
@@ -423,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();
}
/*
@@ -483,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;
}
}
@@ -575,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 89565df..5588977 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -66,6 +66,7 @@
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;
@@ -75,6 +76,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;
@@ -136,7 +139,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;
@@ -176,7 +181,7 @@
// fall through
case WifiManager.NETWORK_STATE_CHANGED_ACTION:
case WifiManager.RSSI_CHANGED_ACTION:
- updateLiveNetworkInfo();
+ updateNetworkInfo();
break;
}
}
@@ -206,14 +211,16 @@
@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.
+ // Don't do this on every NetworkCapabilities change because update accesspoint notify
+ // changed for accesspoint changed on the main 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();
+ mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ refreshEntityHeader();
}
mNetworkCapabilities = nc;
+ refreshButtons();
updateIpLayerInfo();
}
}
@@ -226,6 +233,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,
@@ -270,6 +300,17 @@
mLifecycle = lifecycle;
lifecycle.addObserver(this);
+
+ if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) {
+ mWifiTracker = WifiTrackerFactory.create(
+ mFragment.getActivity(),
+ mWifiListener,
+ mLifecycle,
+ true /*includeSaved*/,
+ true /*includeScans*/);
+ } else {
+ mWifiTracker = null;
+ }
}
@Override
@@ -360,7 +401,7 @@
mNetwork = mWifiManager.getCurrentNetwork();
mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
- updateLiveNetworkInfo();
+ updateNetworkInfo();
mContext.registerReceiver(mReceiver, mFilter);
mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
mHandler);
@@ -377,72 +418,73 @@
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());
-
- refreshEntityHeader();
-
- 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
- final int frequency = mWifiInfo.getFrequency();
- String band = null;
- if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
- && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
- band = mContext.getResources().getString(R.string.wifi_band_24ghz);
- } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
- && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
- band = mContext.getResources().getString(R.string.wifi_band_5ghz);
- } else {
- Log.e(TAG, "Unexpected frequency " + frequency);
- }
- mFrequencyPref.setSummary(band);
-
+ 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() {
@@ -452,14 +494,16 @@
mFragment.getActivity().finish();
}
- private void refreshNetworkState() {
- mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
- refreshEntityHeader();
- }
-
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;
}
@@ -477,6 +521,84 @@
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
+ && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
+ band = mContext.getResources().getString(R.string.wifi_band_24ghz);
+ } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
+ && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
+ band = mContext.getResources().getString(R.string.wifi_band_5ghz);
+ } else {
+ Log.e(TAG, "Unexpected frequency " + frequency);
+ }
+ mFrequencyPref.setSummary(band);
+ mFrequencyPref.setVisible(true);
+ }
+
+ private void refreshTxSpeed() {
+ if (mWifiInfo == null) {
+ mTxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps();
+ mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0);
+ mTxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps()));
+ }
+
+ private void refreshRxSpeed() {
+ if (mWifiInfo == null) {
+ mRxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ 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) {
@@ -488,13 +610,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);
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 7edd227..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,30 +123,15 @@
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()));
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/WifiNetworkListFragment.java b/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java
index 4688d76..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
@@ -256,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;
}
@@ -290,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/savedaccesspoints/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java
index ea858f3..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())
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..1c44204 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -32,6 +32,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -355,7 +356,6 @@
@Override
public void onConnectedChanged() {
- notifySliceChange();
}
@Override
@@ -370,10 +370,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/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/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/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/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/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/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/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 e66774e..40e6939 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
@@ -229,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;
}
@@ -262,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/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 da0d85b..d26a458 100644
--- a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
@@ -94,6 +94,13 @@
}
@Test
+ 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);
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/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 c497cf8..e488d0c 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
@@ -162,4 +163,27 @@
assertThat(isMobileDataEnabled).isEqualTo(seed);
}
+
+ @Test
+ public void airplaneModeEnabled_slicePrimaryActionIsEmpty() {
+ doReturn(true).when(mMobileDataSlice).isAirplaneModeEnabled();
+ doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+ final Slice mobileData = mMobileDataSlice.getSlice();
+
+ final SliceMetadata metadata = SliceMetadata.from(mContext, mobileData);
+ assertThat(metadata.getTitle())
+ .isEqualTo(mContext.getString(R.string.mobile_data_settings_title));
+
+ assertThat(metadata.getSubtitle())
+ .isEqualTo(mContext.getString(R.string.mobile_data_ap_mode_disabled));
+
+ final List<SliceAction> toggles = metadata.getToggles();
+ assertThat(toggles).hasSize(0);
+
+ final SliceAction primaryAction = metadata.getPrimaryAction();
+ final PendingIntent pendingIntent = primaryAction.getAction();
+ final Intent actionIntent = pendingIntent.getIntent();
+
+ assertThat(actionIntent).isNull();
+ }
}
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/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/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index a693f34..23025b2 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -135,7 +135,6 @@
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();
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/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/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/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
index cc2622f..307c0ac 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java
@@ -33,8 +33,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import java.io.IOException;
-
@RunWith(RobolectricTestRunner.class)
public class ListWithEntrySummaryPreferenceTest {
@@ -54,6 +52,7 @@
@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);
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/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)