Snap for 5476769 from a713243db4 to qt-release

Change-Id: I4f3b34b738480149937cf26278fb2c637acf2fd1
This commit is contained in:
android-build-team Robot
2019-04-17 03:13:17 +00:00
97 changed files with 2335 additions and 1100 deletions

View File

@@ -2692,10 +2692,12 @@
android:excludeFromRecents="true"
android:exported="false" />
<activity android:name=".sim.SimDialogActivity"
android:theme="@style/Theme.AlertDialog"
android:label="@string/sim_settings_title"
android:excludeFromRecents="true">
<activity
android:name=".sim.SimDialogActivity"
android:theme="@style/Theme.AlertDialog"
android:label="@string/sim_settings_title"
android:launchMode="singleTop"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
@@ -2907,7 +2909,7 @@
android:launchMode="singleInstance"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.settings.VIEW_ADVANCED_POWER_USAGE_DETAIL" />
<action android:name="android.settings.APP_BATTERY_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
</intent-filter>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2019 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FF000000"
android:pathData="M12,22C17.52,22 22,17.52 22,12 22,6.48 17.52,2 12,2 6.48,2 2,6.48 2,12 2,17.52 6.48,22 12,22ZM12,3.915c3.889,0 8,4.005 8,8.085 0,4.08 -3.927,7.992 -7.928,7.992z"/>
</vector>

View File

@@ -13,17 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FF000000"
android:pathData="M16.67,4H14.5V2h-5v2H7.33C6.6,4 6,4.6 6,5.33V15v5.67C6,21.4 6.6,22 7.33,22h9.33C17.4,22 18,21.4 18,20.67V15V5.33C18,4.6 17.4,4 16.67,4zM16,15v5H8v-5V6h8V15z"/>
<path
android:fillColor="#FF000000"
android:pathData="M15,12l-2,0l0,-2l-2,0l0,2l-2,0l0,2l2,0l0,2l2,0l0,-2l2,0z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_battery_status_bad_24dp"
android:tint="?android:attr/colorAccent" />

View File

@@ -13,19 +13,7 @@
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="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FF000000"
android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V6h1V4H15zM17,19H7V6h10V19z"/>
<path
android:fillColor="#FF000000"
android:pathData="M9,8h2v9h-2z"/>
<path
android:fillColor="#FF000000"
android:pathData="M13,8h2v9h-2z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_delete"
android:tint="?android:attr/colorAccent" />

View File

@@ -13,12 +13,7 @@
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="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M1,9l2,2c4.97,-4.97 13.03,-4.97 18,0l2,-2C16.93,2.93 7.08,2.93 1,9zM9,17l3,3l3,-3C13.35,15.34 10.66,15.34 9,17zM5,13l2,2c2.76,-2.76 7.24,-2.76 10,0l2,-2C15.14,9.14 8.87,9.14 5,13z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_settings_wireless"
android:tint="@android:color/white" />

View File

@@ -14,13 +14,7 @@
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="26dp"
android:height="24dp"
android:viewportWidth="26"
android:viewportHeight="24">
<path
android:fillAlpha="0.3"
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@*android:drawable/ic_wifi_signal_0"
android:tint="?attr/wifi_signal_color" />

View File

@@ -14,16 +14,7 @@
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="26dp"
android:height="24dp"
android:viewportWidth="26"
android:viewportHeight="24">
<path
android:fillAlpha="0.3"
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.1,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.5,6.5L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0z"/>
<path
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.1,22.0l5.5,-6.8c-0.2,-0.2 -2.3,-1.9 -5.5,-1.9s-5.3,1.8 -5.5,1.9L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@*android:drawable/ic_wifi_signal_1"
android:tint="?attr/wifi_signal_color" />

View File

@@ -14,16 +14,7 @@
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="26dp"
android:height="24dp"
android:viewportWidth="26"
android:viewportHeight="24">
<path
android:fillAlpha="0.3"
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
<path
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.0,22.0l7.6,-9.4C20.3,12.4 17.4,10.0 13.0,10.0s-7.3,2.4 -7.6,2.7L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@*android:drawable/ic_wifi_signal_2"
android:tint="?attr/wifi_signal_color" />

View File

@@ -14,16 +14,7 @@
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="26dp"
android:height="24dp"
android:viewportWidth="26"
android:viewportHeight="24">
<path
android:fillAlpha="0.3"
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
<path
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.0,22.0l9.2,-11.4c-0.4,-0.3 -3.9,-3.2 -9.2,-3.2s-8.9,3.0 -9.2,3.2L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@*android:drawable/ic_wifi_signal_3"
android:tint="?attr/wifi_signal_color" />

View File

@@ -14,12 +14,7 @@
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="26dp"
android:height="24dp"
android:viewportWidth="26"
android:viewportHeight="24">
<path
android:fillColor="?attr/wifi_signal_color"
android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
</vector>
<com.android.settings.widget.TintDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@*android:drawable/ic_wifi_signal_4"
android:tint="?attr/wifi_signal_color" />

View File

@@ -15,50 +15,60 @@
limitations under the License
-->
<!-- Note: There is a landscape version of this layout. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/panel_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingBottom="24dp"
android:paddingTop="18dp"
android:textColor="?android:attr/colorPrimary"
android:textSize="20sp"/>
<include layout="@layout/panel_slice_list"/>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/panel_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/settings_panel_background" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp">
android:orientation="vertical">
<Button
android:id="@+id/see_more"
style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="12dp"
android:text="@string/see_more"/>
<TextView
android:id="@+id/panel_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingBottom="24dp"
android:paddingTop="18dp"
android:textColor="?android:attr/colorPrimary"
android:textSize="20sp"/>
<Space
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<include layout="@layout/horizontal_divider"/>
<Button
android:id="@+id/done"
style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginEnd="12dp"
android:text="@string/done"/>
<!-- Note: There is a landscape version of panel_slice_list which supports scrolling. -->
<include layout="@layout/panel_slice_list"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<Button
android:id="@+id/see_more"
style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="12dp"
android:text="@string/see_more"/>
<Space
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<Button
android:id="@+id/done"
style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginEnd="12dp"
android:text="@string/done"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@@ -28,7 +28,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/conversation_background"
android:background="?android:attr/colorBackgroundFloating"
android:paddingTop="@dimen/conversation_message_list_padding"
android:paddingStart="@dimen/conversation_message_list_padding"
android:paddingEnd="@dimen/conversation_message_list_padding"

View File

@@ -25,7 +25,6 @@
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

View File

@@ -15,6 +15,5 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:animateLayoutChanges="true"/>
android:layout_height="wrap_content"
android:layout_width="match_parent"/>

View File

@@ -15,7 +15,6 @@
-->
<resources>
<color name="switchbar_text_color">@android:color/black</color>
<color name="switchbar_switch_track_tint">#82000000</color>
<color name="switchbar_switch_thumb_tint">@android:color/black</color>
<color name="homepage_accessibility_background">#783BE5</color>

View File

@@ -164,6 +164,11 @@
<attr name="textOff" format="reference" />
</declare-styleable>
<declare-styleable name="TintDrawable">
<attr name="android:tint" />
<attr name="android:drawable" />
</declare-styleable>
<attr name="twoStateButtonPreferenceStyle" format="reference" />
<attr name="fingerprint_layout_theme" format="reference" />

View File

@@ -63,7 +63,6 @@
<color name="timestamp_text_incoming">#99ffffff</color>
<color name="message_bubble_incoming">#689f38</color>
<color name="message_bubble_outgoing">#ffffffff</color>
<color name="conversation_background">#eeeeee</color>
<color name="message_icon_background_incoming">#689f38</color>
<color name="message_icon_text_incoming">#ffffffff</color>
<color name="message_icon_background_outgoing">#4285f4</color>
@@ -103,7 +102,7 @@
<color name="contextual_card_background">@*android:color/background_device_default_light</color>
<!-- End of dashboard/homepage icon background colors -->
<color name="switchbar_text_color">@android:color/white</color>
<color name="switchbar_background_color">@*android:color/material_grey_600</color>
<color name="switchbar_switch_track_tint">#BFFFFFFF</color>
<color name="switchbar_switch_thumb_tint">@android:color/white</color>

View File

@@ -2791,7 +2791,7 @@
<!-- Display settings screen, display white balance settings title [CHAR LIMIT=30] -->
<string name="display_white_balance_title">Display white balance</string>
<!-- Display settings screen, setting option name to enable adaptive sleep [CHAR LIMIT=30] -->
<string name="adaptive_sleep_title">Screen aware</string>
<string name="adaptive_sleep_title">Screen attention</string>
<!-- Setting option summary when adaptive sleep is on [CHAR LIMIT=NONE] -->
<string name="adaptive_sleep_summary_on">On / Screen wont turn off if youre looking at it</string>
<!-- Setting option summary when adaptive sleep is off [CHAR LIMIT=NONE] -->
@@ -2799,7 +2799,7 @@
<!-- Description about the feature adaptive sleep [CHAR LIMIT=NONE]-->
<string name="adaptive_sleep_description">Prevents your screen from turning off if youre looking at it.</string>
<!-- Description feature's privacy sensitive details to make sure users understand what feature users, what it saves/sends etc [CHAR LIMIT=NONE]-->
<string name="adaptive_sleep_privacy">Screen aware uses the front camera to see if someone is looking at the screen. It works on device, and images are never stored or sent to Google.</string>
<string name="adaptive_sleep_privacy">Screen attention uses the front camera to see if someone is looking at the screen. It works on device, and images are never stored or sent to Google.</string>
<!-- Night display screen, setting option name to enable night display (renamed "Night Light" with title caps). [CHAR LIMIT=30] -->
@@ -9900,17 +9900,14 @@
<string name="demo_mode">System UI demo mode</string>
<!-- [CHAR LIMIT=60] Name of setting that changes the UI to dark -->
<string name="dark_ui_mode">Theme</string>
<!-- [CHAR LIMIT=60] Name of dev option that changes the color of the UI -->
<string name="dark_ui_mode_title">Choose Theme</string>
<!-- [CHAR_LIMIT=NONE] Summary that is shown in the footer when light mode is selected -->
<string name="dark_ui_settings_light_summary">This setting also applies to apps</string>
<string name="dark_ui_mode">Dark Theme</string>
<!-- [CHAR_LIMIT=NONE] Summary that is shown in the footer when dark mode is selected -->
<string name="dark_ui_settings_dark_summary">Supported apps will also switch to dark theme</string>
<!-- [CHAR_LIMIT=40] Positive button text in dark theme notification -->
<string name="dark_ui_settings_dialog_acknowledge">Got it</string>
<!-- [CHAR LIMIT=60] Name of dev option to enable extra quick settings tiles -->
<string name="quick_settings_developer_tiles">Quick settings developer tiles</string>
@@ -11025,12 +11022,12 @@
<!-- 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>
<!-- Keywords for Content Capture / Smart Suggestions feature [CHAR_LIMIT=none] -->
<string name="keywords_content_capture">content capture, smart suggestions</string>
<!-- Keywords for Content Capture feature [CHAR_LIMIT=none] -->
<string name="keywords_content_capture">content capture</string>
<!-- Title of the 'Content Capture' feature toggle in the Settings -> Privacy screen [CHAR LIMIT=none]-->
<string name="content_capture">Smart Suggestions</string>
<!-- Description of the 'Content Capture / Smart Suggestions' feature toggle in the Settings -> Privacy screen [CHAR LIMIT=NONE]-->
<string name="content_capture_summary">Allow Android to save information seen on your screen or heard in video or audio content. Android makes helpful suggestions based on your device activity.</string>
<string name="content_capture">Content Capture</string>
<!-- Description of the 'Content Capture' feature toggle in the Settings -> Privacy screen [CHAR LIMIT=NONE]-->
<string name="content_capture_summary">Allow apps to send content to the Android system</string>
<!-- Title for the button to initiate a heap dump for the system server. [CHAR LIMIT=NONE] -->
<string name="capture_system_heap_dump_title">Capture system heap dump</string>

View File

@@ -87,10 +87,10 @@
<item name="android:backgroundDimEnabled">false</item>
</style>
<style name="ThemeOverlay.SwitchBar.Settings" parent="@android:style/ThemeOverlay.Material.ActionBar">
<style name="ThemeOverlay.SwitchBar.Settings" parent="@*android:style/ThemeOverlay.DeviceDefault.ActionBar">
<item name="switchBarMarginStart">@dimen/switchbar_subsettings_margin_start</item>
<item name="switchBarMarginEnd">@dimen/switchbar_subsettings_margin_end</item>
<item name="switchBarBackgroundColor">?android:attr/textColorSecondary</item>
<item name="switchBarBackgroundColor">@color/switchbar_background_color</item>
<item name="switchBarBackgroundActivatedColor">?android:attr/colorAccent</item>
<item name="switchBarRestrictionIcon">@*android:drawable/ic_info</item>
</style>
@@ -203,7 +203,7 @@
<!-- Note that Dialog themes do not set list dividers -->
<style name="Theme.BottomDialog" parent="@*android:style/Theme.DeviceDefault.Settings.Dialog">
<item name="android:windowBackground">@drawable/settings_panel_background</item>
<item name="android:windowBackground">@null</item>
<item name="android:dividerHorizontal">@*android:drawable/list_divider_material</item>
<item name="android:windowNoTitle">true</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>

View File

@@ -191,12 +191,14 @@
</style>
<style name="SuwAlertDialogThemeCompat" parent="@style/Theme.AppCompat.Dialog.Alert">
<item name="android:windowSoftInputMode">adjustResize</item>
<!-- copied from Theme.DeviceDefault.Light.Dialog.Alert -->
<item name="colorAccent">@*android:color/accent_device_default_light</item>
<item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
</style>
<style name="SuwAlertDialogThemeCompat.Light" parent="@style/Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowSoftInputMode">adjustResize</item>
<!-- copied from Theme.DeviceDefault.Light.Dialog.Alert -->
<item name="colorAccent">@*android:color/accent_device_default_light</item>
<item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>

View File

@@ -58,11 +58,10 @@
android:title="@string/screen_zoom_title"
settings:searchable="false"/>
<Preference
<SwitchPreference
android:key="dark_ui_mode_accessibility"
android:fragment="com.android.settings.display.DarkUISettings"
android:title="@string/dark_ui_mode"
settings:searchable="false" />
settings:searchable="false"/>
<Preference
android:fragment="com.android.settings.accessibility.MagnificationPreferenceFragment"

View File

@@ -56,11 +56,10 @@
</com.android.settingslib.RestrictedPreference>
<Preference
<SwitchPreference
android:key="dark_ui_mode"
android:fragment="com.android.settings.display.DarkUISettings"
android:title="@string/dark_ui_mode"
settings:searchable="false"
settings:keywords="@string/keywords_dark_ui_mode"
settings:controller="com.android.settings.display.DarkUIPreferenceController"/>
<!-- Cross-listed item, if you change this, also change it in power_usage_summary.xml -->

View File

@@ -103,7 +103,7 @@
settings:useAdminDisabledSummary="true"
settings:keywords="@string/keywords_sounds_and_notifications_interruptions"
settings:allowDividerAbove="true"
settings:controller="com.android.settings.notification.ZenModeSoundSettingsPreferenceController"/>
settings:controller="com.android.settings.notification.ZenModePreferenceController"/>
<Preference
android:key="gesture_prevent_ringing_sound"

View File

@@ -20,7 +20,7 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="zen_mode_settings"
android:title="@string/zen_mode_settings_title"
settings:keywords="@string/keywords_zen_mode_settings">
settings:searchable="false">
<PreferenceCategory
android:key="zen_mode_settings_category_behavior"
@@ -66,11 +66,13 @@
<!-- Turn on DND button -->
<com.android.settingslib.widget.LayoutPreference
android:key="zen_mode_settings_button_container"
android:key="zen_mode_toggle"
android:title="@string/zen_mode_settings_title"
android:selectable="false"
android:layout="@layout/zen_mode_settings_button"
settings:allowDividerAbove="true"
settings:allowDividerBelow="true"/>
settings:allowDividerBelow="true"
settings:keywords="@string/keywords_zen_mode_settings"/>
<!-- Footer that shows if user is put into alarms only or total silence mode by an app -->
<com.android.settingslib.widget.FooterPreference/>

View File

@@ -18,11 +18,13 @@ package com.android.settings;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.display.BrightnessLevelPreferenceController;
import com.android.settings.display.CameraGesturePreferenceController;
import com.android.settings.display.DarkUIPreferenceController;
import com.android.settings.display.LiftToWakePreferenceController;
import com.android.settings.display.NightDisplayPreferenceController;
import com.android.settings.display.NightModePreferenceController;
@@ -62,6 +64,12 @@ public class DisplaySettings extends DashboardFragment {
return R.xml.display_settings;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
use(DarkUIPreferenceController.class).setParentFragment(this);
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getSettingsLifecycle());

View File

@@ -639,12 +639,7 @@ public class SettingsActivity extends SettingsBaseActivity
showDev, isAdmin)
|| somethingChanged;
// For profiles, we want them to be included in the profile select dialog even if
// backup is not activated.
// For other users, enable/disable backup settings depending on whether backup is activated
// for the user.
boolean enableBackupTile = um.isManagedProfile()
|| new BackupSettingsHelper(this).isBackupServiceActive();
boolean enableBackupTile = new BackupSettingsHelper(this).showBackupSettingsForUser();
somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
UserBackupSettingsActivity.class.getName()), enableBackupTile, isAdmin)
|| somethingChanged;

View File

@@ -244,7 +244,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
private SwitchPreference mToggleInversionPreference;
private ColorInversionPreferenceController mInversionPreferenceController;
private AccessibilityHearingAidPreferenceController mHearingAidPreferenceController;
private Preference mDarkUIModePreference;
private SwitchPreference mDarkUIModePreference;
private DarkUIPreferenceController mDarkUIPreferenceController;
private LiveCaptionPreferenceController mLiveCaptionPreferenceController;
@@ -524,8 +524,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
mDarkUIModePreference = findPreference(DARK_UI_MODE_PREFERENCE);
mDarkUIPreferenceController = new DarkUIPreferenceController(getContext(),
DARK_UI_MODE_PREFERENCE);
mDarkUIPreferenceController.setParentFragment(this);
mDarkUIPreferenceController.displayPreference(getPreferenceScreen());
mDarkUIModePreference.setSummary(mDarkUIPreferenceController.getSummary());
}
private void updateAllPreferences() {

View File

@@ -28,6 +28,9 @@ public class BackupInactivePreferenceController extends BasePreferenceController
@Override
public int getAvailabilityStatus() {
if (!new BackupSettingsHelper(mContext).showBackupSettingsForUser()) {
return AVAILABLE_UNSEARCHABLE;
}
if (PrivacySettingsUtils.isInvisibleKey(mContext, PrivacySettingsUtils.BACKUP_INACTIVE)) {
return UNSUPPORTED_ON_DEVICE;
}

View File

@@ -50,6 +50,14 @@ public class BackupSettingsHelper {
mContext = context;
}
public boolean showBackupSettingsForUser() {
// For profiles, we want them to be included in the profile select dialog even if
// backup is not activated.
// For other users, enable/disable backup settings depending on whether backup is activated
// for the user.
return UserManager.get(mContext).isManagedProfile() || isBackupServiceActive();
}
/**
* If there is only one profile, show whether the backup is on or off.
* Otherwise, show nothing.

View File

@@ -98,7 +98,7 @@ public class UserBackupSettingsActivity extends FragmentActivity implements Inde
*/
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
private static final String BACKUP_SEARCH_INDEX_KEY = "backup";
private static final String BACKUP_SEARCH_INDEX_KEY = "Backup";
@Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
@@ -119,6 +119,15 @@ public class UserBackupSettingsActivity extends FragmentActivity implements Inde
return result;
}
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> keys = super.getNonIndexableKeys(context);
if (!new BackupSettingsHelper(context).showBackupSettingsForUser()) {
keys.add(BACKUP_SEARCH_INDEX_KEY);
}
return keys;
}
};
@VisibleForTesting

View File

@@ -44,7 +44,7 @@ public abstract class SliderPreferenceController extends BasePreferenceControlle
}
/**
* @return the value of the Slider's position based on the range: [0, maxSteps).
* @return the value of the Slider's position based on the range: [min, max].
*/
public abstract int getSliderPosition();
@@ -57,9 +57,14 @@ public abstract class SliderPreferenceController extends BasePreferenceControlle
public abstract boolean setSliderPosition(int position);
/**
* @return the number of steps supported by the slider.
* @return the maximum value supported by the slider.
*/
public abstract int getMaxSteps();
public abstract int getMax();
/**
* @return the minimum value supported by the slider.
*/
public abstract int getMin();
@Override
public int getSliceType() {

View File

@@ -99,22 +99,23 @@ public class DataUsageList extends DataUsageBaseFragment {
}
};
private ChartDataUsagePreference mChart;
private TelephonyManager mTelephonyManager;
@VisibleForTesting
NetworkTemplate mTemplate;
@VisibleForTesting
int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@VisibleForTesting
int mNetworkType;
@VisibleForTesting
Spinner mCycleSpinner;
@VisibleForTesting
LoadingViewController mLoadingViewController;
private ChartDataUsagePreference mChart;
private TelephonyManager mTelephonyManager;
private List<NetworkCycleChartData> mCycleData;
private ArrayList<Long> mCycles;
private LoadingViewController mLoadingViewController;
private UidDetailProvider mUidDetailProvider;
private CycleAdapter mCycleAdapter;
private Spinner mCycleSpinner;
private Preference mUsageAmount;
private PreferenceGroup mApps;
private View mHeader;
@@ -158,6 +159,7 @@ public class DataUsageList extends DataUsageBaseFragment {
.launch();
});
mCycleSpinner = mHeader.findViewById(R.id.filter_spinner);
mCycleSpinner.setVisibility(View.GONE);
mCycleAdapter = new CycleAdapter(mCycleSpinner.getContext(), new SpinnerInterface() {
@Override
public void setAdapter(CycleAdapter cycleAdapter) {
@@ -276,7 +278,8 @@ public class DataUsageList extends DataUsageBaseFragment {
* Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
* current {@link #mTemplate}.
*/
private void updatePolicy() {
@VisibleForTesting
void updatePolicy() {
final NetworkPolicy policy = services.mPolicyEditor.getPolicy(mTemplate);
final View configureButton = mHeader.findViewById(R.id.filter_settings);
//SUB SELECT
@@ -486,7 +489,8 @@ public class DataUsageList extends DataUsageBaseFragment {
}
};
private final LoaderCallbacks<List<NetworkCycleChartData>> mNetworkCycleDataCallbacks =
@VisibleForTesting
final LoaderCallbacks<List<NetworkCycleChartData>> mNetworkCycleDataCallbacks =
new LoaderCallbacks<List<NetworkCycleChartData>>() {
@Override
public Loader<List<NetworkCycleChartData>> onCreateLoader(int id, Bundle args) {
@@ -503,6 +507,7 @@ public class DataUsageList extends DataUsageBaseFragment {
mCycleData = data;
// calculate policy cycles based on available data
updatePolicy();
mCycleSpinner.setVisibility(View.VISIBLE);
}
@Override

View File

@@ -15,9 +15,14 @@ package com.android.settings.display;
import static android.provider.Settings.System.ADAPTIVE_SLEEP;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
@@ -27,16 +32,24 @@ public class AdaptiveSleepPreferenceController extends TogglePreferenceControlle
private final String SYSTEM_KEY = ADAPTIVE_SLEEP;
private final int DEFAULT_VALUE = 0;
private final boolean hasSufficientPermissions;
public AdaptiveSleepPreferenceController(Context context, String key) {
super(context, key);
final PackageManager packageManager = mContext.getPackageManager();
final String attentionPackage = packageManager.getAttentionServicePackageName();
hasSufficientPermissions = attentionPackage != null && packageManager.checkPermission(
Manifest.permission.CAMERA, attentionPackage) == PackageManager.PERMISSION_GRANTED;
}
@Override
public boolean isChecked() {
return Settings.System.getInt(mContext.getContentResolver(),
return hasSufficientPermissions && Settings.System.getInt(mContext.getContentResolver(),
SYSTEM_KEY, DEFAULT_VALUE) != DEFAULT_VALUE;
}
@Override
public boolean setChecked(boolean isChecked) {
Settings.System.putInt(mContext.getContentResolver(), SYSTEM_KEY,
@@ -64,4 +77,15 @@ public class AdaptiveSleepPreferenceController extends TogglePreferenceControlle
? R.string.adaptive_sleep_summary_on
: R.string.adaptive_sleep_summary_off);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final Preference preference = screen.findPreference(SYSTEM_KEY);
if (preference != null) {
preference.setEnabled(hasSufficientPermissions);
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.display;
import static com.android.settings.display.DarkUIPreferenceController.DARK_MODE_PREFS;
import static com.android.settings.display.DarkUIPreferenceController.PREF_DARK_MODE_DIALOG_SEEN;
import android.app.Dialog;
import android.app.UiModeManager;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
public class DarkUIInfoDialogFragment extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener{
@Override
public int getMetricsCategory() {
// TODO(b/130251804): Add metrics constant in followup change to avoid merge conflict in
// beta cherrypick
return 0;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Context context = getContext();
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
LayoutInflater inflater = LayoutInflater.from(dialog.getContext());
View titleView = inflater.inflate(R.layout.settings_dialog_title, null);
((ImageView) titleView.findViewById(R.id.settings_icon))
.setImageDrawable(context.getDrawable(R.drawable.dark_theme));
((TextView) titleView.findViewById(R.id.settings_title)).setText(R.string.dark_ui_mode);
dialog.setCustomTitle(titleView)
.setMessage(R.string.dark_ui_settings_dark_summary)
.setPositiveButton(
R.string.dark_ui_settings_dialog_acknowledge,
this);
return dialog.create();
}
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
enableDarkTheme();
super.onDismiss(dialog);
}
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// We have to manually dismiss the dialog because changing night mode causes it to
// recreate itself.
dialogInterface.dismiss();
enableDarkTheme();
}
private void enableDarkTheme() {
final Context context = getContext();
if (context != null) {
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.DARK_MODE_DIALOG_SEEN,
DarkUIPreferenceController.DIALOG_SEEN);
context.getSystemService(UiModeManager.class)
.setNightMode(UiModeManager.MODE_NIGHT_YES);
}
}
}

View File

@@ -18,37 +18,66 @@ package com.android.settings.display;
import android.app.UiModeManager;
import android.content.Context;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.TogglePreferenceController;
public class DarkUIPreferenceController extends BasePreferenceController {
public class DarkUIPreferenceController extends TogglePreferenceController {
public static final String DARK_MODE_PREFS = "dark_mode_prefs";
public static final String PREF_DARK_MODE_DIALOG_SEEN = "dark_mode_dialog_seen";
public static final int DIALOG_SEEN = 1;
private UiModeManager mUiModeManager;
private Context mContext;
private Fragment mFragment;
public DarkUIPreferenceController(Context context, String key) {
super(context, key);
mContext = context;
mUiModeManager = context.getSystemService(UiModeManager.class);
}
@Override
public boolean isChecked() {
return mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES;
}
@Override
public boolean setChecked(boolean isChecked) {
final boolean dialogSeen =
Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.DARK_MODE_DIALOG_SEEN, 0) == DIALOG_SEEN;
if (!dialogSeen && isChecked) {
showDarkModeDialog();
return false;
}
mUiModeManager.setNightMode(isChecked
? UiModeManager.MODE_NIGHT_YES
: UiModeManager.MODE_NIGHT_NO);
return true;
}
private void showDarkModeDialog() {
final DarkUIInfoDialogFragment frag = new DarkUIInfoDialogFragment();
if (mFragment.getFragmentManager() != null) {
frag.show(mFragment.getFragmentManager(), getClass().getName());
}
}
@VisibleForTesting
void setUiModeManager(UiModeManager uiModeManager) {
mUiModeManager = uiModeManager;
}
public void setParentFragment(Fragment fragment) {
mFragment = fragment;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public CharSequence getSummary() {
return DarkUISettingsRadioButtonsController.modeToDescription(
mContext, mUiModeManager.getNightMode());
}
}

View File

@@ -1,145 +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.display;
import android.app.UiModeManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.provider.SearchIndexableResource;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.widget.RadioButtonPickerFragment;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.CandidateInfo;
import com.android.settingslib.widget.FooterPreference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* The screen for selecting the dark theme preference for this device. Automatically updates
* the associated footer view with any needed information.
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class DarkUISettings extends RadioButtonPickerFragment implements Indexable {
private DarkUISettingsRadioButtonsController mController;
private Preference mFooter;
@Override
protected int getPreferenceScreenResId() {
return R.xml.dark_ui_settings;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// TODO(b/128686189): add illustration once it is ready
setIllustration(0, 0);
mFooter = new FooterPreference(context);
mFooter.setIcon(android.R.color.transparent);
mController = new DarkUISettingsRadioButtonsController(context, mFooter);
}
@Override
protected List<? extends CandidateInfo> getCandidates() {
final Context context = getContext();
final List<CandidateInfo> candidates = new ArrayList<>();
candidates.add(new DarkUISettingsCandidateInfo(
DarkUISettingsRadioButtonsController.modeToDescription(
context, UiModeManager.MODE_NIGHT_YES),
/* summary */ null,
DarkUISettingsRadioButtonsController.KEY_DARK,
/* enabled */ true));
candidates.add(new DarkUISettingsCandidateInfo(
DarkUISettingsRadioButtonsController.modeToDescription(
context, UiModeManager.MODE_NIGHT_NO),
/* summary */ null,
DarkUISettingsRadioButtonsController.KEY_LIGHT,
/* enabled */ true));
return candidates;
}
@Override
protected void addStaticPreferences(PreferenceScreen screen) {
screen.addPreference(mFooter);
}
@Override
protected String getDefaultKey() {
return mController.getDefaultKey();
}
@Override
protected boolean setDefaultKey(String key) {
return mController.setDefaultKey(key);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DARK_UI_SETTINGS;
}
static class DarkUISettingsCandidateInfo extends CandidateInfo {
private final CharSequence mLabel;
private final CharSequence mSummary;
private final String mKey;
DarkUISettingsCandidateInfo(CharSequence label, CharSequence summary, String key,
boolean enabled) {
super(enabled);
mLabel = label;
mKey = key;
mSummary = summary;
}
@Override
public CharSequence loadLabel() {
return mLabel;
}
@Override
public Drawable loadIcon() {
return null;
}
@Override
public String getKey() {
return mKey;
}
public CharSequence getSummary() {
return mSummary;
}
}
public static final Indexable.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.dark_ui_settings;
return Arrays.asList(sir);
}
};
}

View File

@@ -1,86 +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.display;
import android.app.UiModeManager;
import android.content.Context;
import androidx.preference.Preference;
import com.android.settings.R;
import androidx.annotation.VisibleForTesting;
public class DarkUISettingsRadioButtonsController {
public static final String KEY_DARK = "key_dark_ui_settings_dark";
public static final String KEY_LIGHT = "key_dark_ui_settings_light";
@VisibleForTesting
UiModeManager mManager;
private Preference mFooter;
public DarkUISettingsRadioButtonsController(Context context, Preference footer) {
mManager = context.getSystemService(UiModeManager.class);
mFooter = footer;
}
public String getDefaultKey() {
final int mode = mManager.getNightMode();
updateFooter();
return mode == UiModeManager.MODE_NIGHT_YES ? KEY_DARK : KEY_LIGHT;
}
public boolean setDefaultKey(String key) {
switch(key) {
case KEY_DARK:
mManager.setNightMode(UiModeManager.MODE_NIGHT_YES);
break;
case KEY_LIGHT:
mManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
break;
default:
throw new IllegalStateException(
"Not a valid key for " + this.getClass().getSimpleName() + ": " + key);
}
updateFooter();
return true;
}
public void updateFooter() {
final int mode = mManager.getNightMode();
switch (mode) {
case UiModeManager.MODE_NIGHT_YES:
mFooter.setSummary(R.string.dark_ui_settings_dark_summary);
break;
case UiModeManager.MODE_NIGHT_NO:
case UiModeManager.MODE_NIGHT_AUTO:
default:
mFooter.setSummary(R.string.dark_ui_settings_light_summary);
}
}
public static String modeToDescription(Context context, int mode) {
final String[] values = context.getResources().getStringArray(R.array.dark_ui_mode_entries);
switch (mode) {
case UiModeManager.MODE_NIGHT_YES:
return values[0];
case UiModeManager.MODE_NIGHT_NO:
case UiModeManager.MODE_NIGHT_AUTO:
default:
return values[1];
}
}
}

View File

@@ -55,7 +55,8 @@ public class NightDisplayIntensityPreferenceController extends SliderPreferenceC
super.displayPreference(screen);
final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
preference.setContinuousUpdates(true);
preference.setMax(getMaxSteps());
preference.setMax(getMax());
preference.setMin(getMin());
}
@Override
@@ -75,10 +76,15 @@ public class NightDisplayIntensityPreferenceController extends SliderPreferenceC
}
@Override
public int getMaxSteps() {
public int getMax() {
return convertTemperature(ColorDisplayManager.getMinimumColorTemperature(mContext));
}
@Override
public int getMin() {
return ColorDisplayManager.getMinimumColorTemperature(mContext);
}
/**
* Inverts and range-adjusts a raw value from the SeekBar (i.e. [0, maxTemp-minTemp]), or
* converts an inverted and range-adjusted value to the raw SeekBar value, depending on the

View File

@@ -160,7 +160,7 @@ public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
}
/** Returns the color resid for tinting {@link #getIconId()} or {@link View#NO_ID} if none. */
protected @IdRes int getIconTintColorId() {
public @IdRes int getIconTintColorId() {
return View.NO_ID;
}

View File

@@ -102,8 +102,15 @@ public class PreventRingingSwitchPreferenceController extends AbstractPreference
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
final int preventRingingSetting = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_VIBRATE);
final int newRingingSetting = preventRingingSetting == Settings.Secure.VOLUME_HUSH_OFF
? Settings.Secure.VOLUME_HUSH_VIBRATE
: preventRingingSetting;
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.VOLUME_HUSH_GESTURE, isChecked ? Settings.Secure.VOLUME_HUSH_VIBRATE
Settings.Secure.VOLUME_HUSH_GESTURE, isChecked
? newRingingSetting
: Settings.Secure.VOLUME_HUSH_OFF);
}

View File

@@ -25,6 +25,9 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.ArrayMap;
@@ -39,6 +42,7 @@ import androidx.slice.builders.SliceAction;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
@@ -107,8 +111,12 @@ public class BatteryFixSlice implements CustomSliceable {
if (batteryTip.getState() == BatteryTip.StateType.INVISIBLE) {
continue;
}
final IconCompat icon = IconCompat.createWithResource(mContext,
batteryTip.getIconId());
final Drawable drawable = mContext.getDrawable(batteryTip.getIconId());
drawable.setColorFilter(new PorterDuffColorFilter(
mContext.getResources().getColor(batteryTip.getIconTintColorId()),
PorterDuff.Mode.SRC_IN));
final IconCompat icon = Utils.createIconWithDrawable(drawable);
final SliceAction primaryAction = SliceAction.createDeeplink(getPrimaryAction(),
icon,
ListBuilder.ICON_IMAGE,

View File

@@ -154,16 +154,12 @@ public class NotificationChannelSlice implements CustomSliceable {
// TODO(b/123065955): Review latency of NotificationChannelSlice
final List<PackageInfo> multiChannelPackages = getMultiChannelPackages(
getRecentlyInstalledPackages());
final PackageInfo packageInfo = getMaxSentNotificationsPackage(multiChannelPackages);
// Return a header with IsError flag, if package is not found.
if (packageInfo == null) {
mPackageName = getMaxSentNotificationsPackage(multiChannelPackages);
if (mPackageName == null) {
// Return a header with IsError flag, if package is not found.
return listBuilder.setHeader(getNoSuggestedAppHeader())
.setIsError(true).build();
}
// Save eligible package name and its uid, they will be used in getIntent().
mPackageName = packageInfo.packageName;
mUid = getApplicationUid(mPackageName);
// Add notification channel header.
@@ -177,7 +173,7 @@ public class NotificationChannelSlice implements CustomSliceable {
.setPrimaryAction(getPrimarySliceAction(icon, title, getIntent())));
// Add notification channel rows.
final List<ListBuilder.RowBuilder> rows = getNotificationChannelRows(packageInfo, icon);
final List<ListBuilder.RowBuilder> rows = getNotificationChannelRows(icon);
for (ListBuilder.RowBuilder rowBuilder : rows) {
listBuilder.addRow(rowBuilder);
}
@@ -282,8 +278,7 @@ public class NotificationChannelSlice implements CustomSliceable {
.setPrimaryAction(primarySliceActionForNoSuggestedApp);
}
private List<ListBuilder.RowBuilder> getNotificationChannelRows(PackageInfo packageInfo,
IconCompat icon) {
private List<ListBuilder.RowBuilder> getNotificationChannelRows(IconCompat icon) {
final List<ListBuilder.RowBuilder> notificationChannelRows = new ArrayList<>();
final List<NotificationChannel> displayableChannels = getDisplayableChannels(mAppRow);
@@ -388,14 +383,14 @@ public class NotificationChannelSlice implements CustomSliceable {
.collect(Collectors.toList());
}
private PackageInfo getMaxSentNotificationsPackage(List<PackageInfo> packageInfoList) {
private String getMaxSentNotificationsPackage(List<PackageInfo> packageInfoList) {
if (packageInfoList.isEmpty()) {
return null;
}
// Get the package which has sent at least ~10 notifications and not turn off channels.
int maxSentCount = 0;
PackageInfo maxSentCountPackage = null;
String maxSentCountPackage = null;
for (PackageInfo packageInfo : packageInfoList) {
final NotificationBackend.AppRow appRow = mNotificationBackend.loadAppRow(mContext,
mContext.getPackageManager(), packageInfo);
@@ -408,7 +403,7 @@ public class NotificationChannelSlice implements CustomSliceable {
final int sentCount = appRow.sentByApp.sentCount;
if (sentCount >= MIN_NOTIFICATION_SENT_COUNT && sentCount > maxSentCount) {
maxSentCount = sentCount;
maxSentCountPackage = packageInfo;
maxSentCountPackage = packageInfo.packageName;
mAppRow = appRow;
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.homepage.contextualcards.slices;
import static android.app.slice.Slice.HINT_ERROR;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
@@ -117,6 +119,14 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life
return;
}
if (slice.hasHint(HINT_ERROR)) {
Log.w(TAG, "Slice has HINT_ERROR, skipping rendering. uri=" + slice.getUri());
mSliceLiveDataMap.get(slice.getUri()).removeObservers(mLifecycleOwner);
mContext.getContentResolver().notifyChange(CardContentProvider.REFRESH_CARD_URI,
null);
return;
}
switch (holder.getItemViewType()) {
case VIEW_TYPE_DEFERRED_SETUP:
mDeferredSetupCardHelper.bindView(holder, card, slice);

View File

@@ -21,6 +21,7 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import android.content.Context;
import android.content.Intent;
import android.os.UserManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.euicc.EuiccManager;
@@ -49,6 +50,7 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
private static final String KEY = "mobile_network_list";
private SubscriptionManager mSubscriptionManager;
private UserManager mUserManager;
private SubscriptionsChangeListener mChangeListener;
private AddPreference mPreference;
@@ -70,6 +72,7 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
public MobileNetworkSummaryController(Context context, Lifecycle lifecycle) {
super(context);
mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
mUserManager = context.getSystemService(UserManager.class);
if (lifecycle != null) {
mChangeListener = new SubscriptionsChangeListener(context, this);
lifecycle.addObserver(this);
@@ -137,6 +140,8 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
startAddSimFlow();
return true;
});
} else {
mPreference.setEnabled(false);
}
} else {
// We have one or more existing subscriptions, so we want the plus button if eSIM is
@@ -160,7 +165,7 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
@Override
public boolean isAvailable() {
return !Utils.isWifiOnly(mContext);
return !Utils.isWifiOnly(mContext) && mUserManager.isAdminUser();
}
@Override

View File

@@ -74,4 +74,8 @@ public class AudioHelper {
public int getMaxVolume(int stream) {
return mAudioManager.getStreamMaxVolume(stream);
}
public int getMinVolume(int stream) {
return mAudioManager.getStreamMinVolume(stream);
}
}

View File

@@ -43,7 +43,6 @@ public class RemoteVolumePreferenceController extends VolumeSeekBarPreferenceCon
@VisibleForTesting
static final int REMOTE_VOLUME = 100;
private MediaSessionManager mMediaSessionManager;
private MediaSessions mMediaSessions;
@VisibleForTesting
MediaSession.Token mActiveToken;
@@ -86,28 +85,39 @@ public class RemoteVolumePreferenceController extends VolumeSeekBarPreferenceCon
public RemoteVolumePreferenceController(Context context) {
super(context, KEY_REMOTE_VOLUME);
mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
mMediaSessions = new MediaSessions(context, Looper.getMainLooper(), mCallbacks);
updateToken(getActiveRemoteToken(mContext));
}
@Override
public int getAvailabilityStatus() {
final List<MediaController> controllers = mMediaSessionManager.getActiveSessions(null);
// Always return true to make it indexed in database
return AVAILABLE_UNSEARCHABLE;
}
/**
* Return {@link android.media.session.MediaSession.Token} for active remote token, or
* {@code null} if there is no active remote token.
*/
public static MediaSession.Token getActiveRemoteToken(Context context) {
final MediaSessionManager sessionManager = context.getSystemService(
MediaSessionManager.class);
final List<MediaController> controllers = sessionManager.getActiveSessions(null);
for (MediaController mediaController : controllers) {
final MediaController.PlaybackInfo pi = mediaController.getPlaybackInfo();
if (isRemote(pi)) {
updateToken(mediaController.getSessionToken());
return AVAILABLE;
return mediaController.getSessionToken();
}
}
// No active remote media at this point
return CONDITIONALLY_UNAVAILABLE;
return null;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference.setVisible(mActiveToken != null);
if (mMediaController != null) {
updatePreference(mPreference, mActiveToken, mMediaController.getPlaybackInfo());
}
@@ -150,7 +160,7 @@ public class RemoteVolumePreferenceController extends VolumeSeekBarPreferenceCon
}
@Override
public int getMaxSteps() {
public int getMax() {
if (mPreference != null) {
return mPreference.getMax();
}
@@ -161,6 +171,14 @@ public class RemoteVolumePreferenceController extends VolumeSeekBarPreferenceCon
return playbackInfo != null ? playbackInfo.getMaxVolume() : 0;
}
@Override
public int getMin() {
if (mPreference != null) {
return mPreference.getMin();
}
return 0;
}
@Override
public boolean isSliceable() {
return TextUtils.equals(getPreferenceKey(), KEY_REMOTE_VOLUME);

View File

@@ -80,11 +80,6 @@ public class VolumeSeekBarPreference extends SeekBarPreference {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
@Override
public boolean isSelectable() {
return false;
}
public void setStream(int stream) {
mStream = stream;
setMax(mAudioManager.getStreamMaxVolume(mStream));

View File

@@ -92,13 +92,21 @@ public abstract class VolumeSeekBarPreferenceController extends
}
@Override
public int getMaxSteps() {
public int getMax() {
if (mPreference != null) {
return mPreference.getMax();
}
return mHelper.getMaxVolume(getAudioStream());
}
@Override
public int getMin() {
if (mPreference != null) {
return mPreference.getMin();
}
return mHelper.getMinVolume(getAudioStream());
}
protected abstract int getAudioStream();
protected abstract int getMuteIcon();

View File

@@ -33,11 +33,12 @@ import com.android.settingslib.widget.LayoutPreference;
public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController
implements PreferenceControllerMixin {
public static final String KEY = "zen_mode_toggle";
private static final String TAG = "EnableZenModeButton";
protected static final String KEY = "zen_mode_settings_button_container";
private final FragmentManager mFragment;
private Button mZenButtonOn;
private Button mZenButtonOff;
private FragmentManager mFragment;
public ZenModeButtonPreferenceController(Context context, Lifecycle lifecycle, FragmentManager
fragment) {
@@ -60,13 +61,13 @@ public class ZenModeButtonPreferenceController extends AbstractZenModePreference
super.updateState(preference);
if (null == mZenButtonOn) {
mZenButtonOn = (Button) ((LayoutPreference) preference)
mZenButtonOn = ((LayoutPreference) preference)
.findViewById(R.id.zen_mode_settings_turn_on_button);
updateZenButtonOnClickListener();
}
if (null == mZenButtonOff) {
mZenButtonOff = (Button) ((LayoutPreference) preference)
mZenButtonOff = ((LayoutPreference) preference)
.findViewById(R.id.zen_mode_settings_turn_off_button);
mZenButtonOff.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,

View File

@@ -328,7 +328,6 @@ public class ZenModeSettings extends ZenModeSettingsBase {
public List<String> getNonIndexableKeys(Context context) {
List<String> keys = super.getNonIndexableKeys(context);
keys.add(ZenModeDurationPreferenceController.KEY);
keys.add(ZenModeButtonPreferenceController.KEY);
return keys;
}

View File

@@ -18,8 +18,6 @@ package com.android.settings.notification;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static com.android.settings.notification.ZenModeSoundSettingsPreferenceController.ZEN_MODE_KEY;
import android.annotation.ColorInt;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -47,6 +45,8 @@ public class ZenModeSliceBuilder {
private static final String TAG = "ZenModeSliceBuilder";
private static final String ZEN_MODE_SLICE_KEY = ZenModeButtonPreferenceController.KEY;
/**
* Action notifying a change on the Zen Mode Slice.
*/
@@ -78,7 +78,8 @@ public class ZenModeSliceBuilder {
final PendingIntent primaryAction = getPrimaryAction(context);
final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction,
(IconCompat) null /* icon */, ListBuilder.ICON_IMAGE, title);
final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction, null /* actionTitle */,
final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
null /* actionTitle */,
isZenModeEnabled);
return new ListBuilder(context, CustomSliceRegistry.ZEN_MODE_SLICE_URI,
@@ -110,10 +111,10 @@ public class ZenModeSliceBuilder {
}
public static Intent getIntent(Context context) {
final Uri contentUri = new Uri.Builder().appendPath(ZEN_MODE_KEY).build();
final Uri contentUri = new Uri.Builder().appendPath(ZEN_MODE_SLICE_KEY).build();
final String screenTitle = context.getText(R.string.zen_mode_settings_title).toString();
return SliceBuilderUtils.buildSearchResultPageIntent(context,
ZenModeSettings.class.getName(), ZEN_MODE_KEY, screenTitle,
ZenModeSettings.class.getName(), ZEN_MODE_SLICE_KEY, screenTitle,
SettingsEnums.NOTIFICATION_ZEN_MODE)
.setClassName(context.getPackageName(), SubSettings.class.getName())
.setData(contentUri);

View File

@@ -1,33 +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.notification;
import android.content.Context;
public class ZenModeSoundSettingsPreferenceController extends ZenModePreferenceController {
public static final String ZEN_MODE_KEY = "zen_mode";
public ZenModeSoundSettingsPreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -16,13 +16,20 @@
package com.android.settings.panel;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.TextView;
@@ -30,8 +37,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
import androidx.slice.Slice;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.SliceMetadata;
import androidx.slice.widget.SliceLiveData;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
@@ -40,10 +51,24 @@ import com.android.settings.panel.PanelLoggingContract.PanelClosedKeys;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.google.android.setupdesign.DividerItemDecoration;
import java.util.ArrayList;
import java.util.List;
public class PanelFragment extends Fragment {
private static final String TAG = "PanelFragment";
/**
* Duration of the animation entering or exiting the screen, in milliseconds.
*/
private static final int DURATION_ANIMATE_PANEL_MS = 250;
/**
* Duration of timeout waiting for Slice data to bind, in milliseconds.
*/
private static final int DURATION_SLICE_BINDING_TIMEOUT_MS = 250;
private View mLayoutView;
private TextView mTitleView;
private Button mSeeMoreButton;
private Button mDoneButton;
@@ -53,20 +78,40 @@ public class PanelFragment extends Fragment {
private MetricsFeatureProvider mMetricsProvider;
private String mPanelClosedKey;
private final List<LiveData<Slice>> mSliceLiveData = new ArrayList<>();
@VisibleForTesting
PanelSlicesAdapter mAdapter;
PanelSlicesLoaderCountdownLatch mPanelSlicesLoaderCountdownLatch;
private ViewTreeObserver.OnPreDrawListener mOnPreDrawListener = () -> {
return false;
};
private final ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener =
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
animateIn();
if (mPanelSlices != null) {
mPanelSlices.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
};
private PanelSlicesAdapter mAdapter;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
final View view = inflater.inflate(R.layout.panel_layout, container, false);
mPanelSlices = view.findViewById(R.id.panel_parent_layout);
mSeeMoreButton = view.findViewById(R.id.see_more);
mDoneButton = view.findViewById(R.id.done);
mTitleView = view.findViewById(R.id.panel_title);
mLayoutView = inflater.inflate(R.layout.panel_layout, container, false);
mPanelSlices = mLayoutView.findViewById(R.id.panel_parent_layout);
mSeeMoreButton = mLayoutView.findViewById(R.id.see_more);
mDoneButton = mLayoutView.findViewById(R.id.done);
mTitleView = mLayoutView.findViewById(R.id.panel_title);
final Bundle arguments = getArguments();
final String panelType =
@@ -82,6 +127,24 @@ public class PanelFragment extends Fragment {
.getPanel(activity, panelType, mediaPackageName);
mMetricsProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider();
mPanelSlices.setLayoutManager(new LinearLayoutManager((activity)));
// Add predraw listener to remove the animation and while we wait for Slices to load.
mLayoutView.getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
// Start loading Slices. When finished, the Panel will animate in.
loadAllSlices();
mTitleView.setText(mPanel.getTitle());
mSeeMoreButton.setOnClickListener(getSeeMoreListener());
mDoneButton.setOnClickListener(getCloseListener());
// If getSeeMoreIntent() is null, hide the mSeeMoreButton.
if (mPanel.getSeeMoreIntent() == null) {
mSeeMoreButton.setVisibility(View.GONE);
}
// Log panel opened.
mMetricsProvider.action(
0 /* attribution */,
@@ -90,27 +153,114 @@ public class PanelFragment extends Fragment {
callingPackageName,
0 /* value */);
mAdapter = new PanelSlicesAdapter(this, mPanel);
return mLayoutView;
}
mPanelSlices.setHasFixedSize(true);
mPanelSlices.setLayoutManager(new LinearLayoutManager((activity)));
mPanelSlices.setAdapter(mAdapter);
private void loadAllSlices() {
mSliceLiveData.clear();
final List<Uri> sliceUris = mPanel.getSlices();
mPanelSlicesLoaderCountdownLatch = new PanelSlicesLoaderCountdownLatch(sliceUris.size());
DividerItemDecoration itemDecoration = new DividerItemDecoration(getActivity());
itemDecoration.setDividerCondition(DividerItemDecoration.DIVIDER_CONDITION_BOTH);
mPanelSlices.addItemDecoration(itemDecoration);
for (Uri uri : sliceUris) {
final LiveData<Slice> sliceLiveData = SliceLiveData.fromUri(getActivity(), uri);
mTitleView.setText(mPanel.getTitle());
// Add slice first to make it in order. Will remove it later if there's an error.
mSliceLiveData.add(sliceLiveData);
mSeeMoreButton.setOnClickListener(getSeeMoreListener());
mDoneButton.setOnClickListener(getCloseListener());
sliceLiveData.observe(getViewLifecycleOwner(), slice -> {
// If the Slice has already loaded, do nothing.
if (mPanelSlicesLoaderCountdownLatch.isSliceLoaded(uri)) {
return;
}
//If getSeeMoreIntent() is null, hide the mSeeMoreButton.
if (mPanel.getSeeMoreIntent() == null) {
mSeeMoreButton.setVisibility(View.GONE);
/**
* Watching for the {@link Slice} to load.
* <p>
* If the Slice comes back {@code null} or with the Error attribute, remove the
* Slice data from the list, and mark the Slice as loaded.
* <p>
* If the Slice has come back fully loaded, then mark the Slice as loaded. No
* other actions required since we already have the Slice data in the list.
* <p>
* If the Slice does not match the above condition, we will still want to mark
* it as loaded after 250ms timeout to avoid delay showing up the panel for
* too long. Since we are still having the Slice data in the list, the Slice
* will show up later once it is loaded.
*/
final SliceMetadata metadata = SliceMetadata.from(getActivity(), slice);
if (slice == null || metadata.isErrorSlice()) {
mSliceLiveData.remove(sliceLiveData);
mPanelSlicesLoaderCountdownLatch.markSliceLoaded(uri);
} else if (metadata.getLoadingState() == SliceMetadata.LOADED_ALL) {
mPanelSlicesLoaderCountdownLatch.markSliceLoaded(uri);
} else {
Handler handler = new Handler();
handler.postDelayed(() -> {
mPanelSlicesLoaderCountdownLatch.markSliceLoaded(uri);
loadPanelWhenReady();
}, DURATION_SLICE_BINDING_TIMEOUT_MS);
}
loadPanelWhenReady();
});
}
}
return view;
/**
* When all of the Slices have loaded for the first time, then we can setup the
* {@link RecyclerView}.
* <p>
* When the Recyclerview has been laid out, we can begin the animation with the
* {@link mOnGlobalLayoutListener}, which calls {@link #animateIn()}.
*/
private void loadPanelWhenReady() {
if (mPanelSlicesLoaderCountdownLatch.isPanelReadyToLoad()) {
mAdapter = new PanelSlicesAdapter(
this, mSliceLiveData, mPanel.getMetricsCategory());
mPanelSlices.setAdapter(mAdapter);
mPanelSlices.getViewTreeObserver()
.addOnGlobalLayoutListener(mOnGlobalLayoutListener);
DividerItemDecoration itemDecoration = new DividerItemDecoration(getActivity());
itemDecoration
.setDividerCondition(DividerItemDecoration.DIVIDER_CONDITION_BOTH);
mPanelSlices.addItemDecoration(itemDecoration);
}
}
/**
* Animate a Panel onto the screen.
* <p>
* Takes the entire panel and animates in from behind the navigation bar.
* <p>
* Relies on the Panel being having a fixed height to begin the animation.
*/
private void animateIn() {
final View panelContent = mLayoutView.findViewById(R.id.panel_container);
final AnimatorSet animatorSet = buildAnimatorSet(mLayoutView, panelContent.getHeight(),
0.0f, new DecelerateInterpolator());
final ValueAnimator animator = new ValueAnimator();
animator.setFloatValues(0.0f, 1.0f);
animatorSet.play(animator);
animatorSet.start();
// Remove the predraw listeners on the Panel.
mLayoutView.getViewTreeObserver().removeOnPreDrawListener(mOnPreDrawListener);
}
/**
* Build an {@link AnimatorSet} to bring the Panel, {@param parentView}in our out of the screen,
* based on the positional parameters {@param startY}, {@param endY} and at the rate set by the
* {@param interpolator}.
*/
@NonNull
private static AnimatorSet buildAnimatorSet(@NonNull View parentView, float startY, float endY,
@NonNull Interpolator interpolator) {
final View sheet = parentView.findViewById(R.id.panel_container);
final AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(DURATION_ANIMATE_PANEL_MS);
animatorSet.setInterpolator(interpolator);
animatorSet.playTogether(ObjectAnimator.ofFloat(sheet, View.TRANSLATION_Y, startY, endY));
return animatorSet;
}
@Override

View File

@@ -20,7 +20,6 @@ import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDIC
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,13 +29,13 @@ import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;
import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.Slice;
import androidx.slice.widget.SliceLiveData;
import androidx.slice.widget.SliceView;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.google.android.setupdesign.DividerItemDecoration;
import java.util.ArrayList;
import java.util.List;
/**
@@ -45,14 +44,15 @@ import java.util.List;
public class PanelSlicesAdapter
extends RecyclerView.Adapter<PanelSlicesAdapter.SliceRowViewHolder> {
private final List<Uri> mSliceUris;
private final List<LiveData<Slice>> mSliceLiveData;
private final int mMetricsCategory;
private final PanelFragment mPanelFragment;
private final PanelContent mPanelContent;
public PanelSlicesAdapter(PanelFragment fragment, PanelContent panel) {
public PanelSlicesAdapter(
PanelFragment fragment, List<LiveData<Slice>> sliceLiveData, int metricsCategory) {
mPanelFragment = fragment;
mSliceUris = panel.getSlices();
mPanelContent = panel;
mSliceLiveData = new ArrayList<>(sliceLiveData);
mMetricsCategory = metricsCategory;
}
@NonNull
@@ -62,67 +62,60 @@ public class PanelSlicesAdapter
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.panel_slice_row, viewGroup, false);
return new SliceRowViewHolder(view, mPanelContent);
return new SliceRowViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull SliceRowViewHolder sliceRowViewHolder, int position) {
sliceRowViewHolder.onBind(mPanelFragment, mSliceUris.get(position));
sliceRowViewHolder.onBind(mSliceLiveData.get(position));
}
@Override
public int getItemCount() {
return mSliceUris.size();
return mSliceLiveData.size();
}
@VisibleForTesting
List<Uri> getData() {
return mSliceUris;
List<LiveData<Slice>> getData() {
return mSliceLiveData;
}
/**
* ViewHolder for binding Slices to SliceViews.
*/
public static class SliceRowViewHolder extends RecyclerView.ViewHolder
public class SliceRowViewHolder extends RecyclerView.ViewHolder
implements DividerItemDecoration.DividedViewHolder {
private final PanelContent mPanelContent;
private boolean mDividerAllowedAbove = true;
@VisibleForTesting
LiveData<Slice> sliceLiveData;
@VisibleForTesting
final SliceView sliceView;
public SliceRowViewHolder(View view, PanelContent panelContent) {
public SliceRowViewHolder(View view) {
super(view);
sliceView = view.findViewById(R.id.slice_view);
sliceView.setMode(SliceView.MODE_LARGE);
sliceView.showTitleItems(true);
mPanelContent = panelContent;
}
public void onBind(PanelFragment fragment, Uri sliceUri) {
final Context context = sliceView.getContext();
sliceLiveData = SliceLiveData.fromUri(context, sliceUri);
sliceLiveData.observe(fragment.getViewLifecycleOwner(), sliceView);
public void onBind(LiveData<Slice> sliceLiveData) {
sliceLiveData.observe(mPanelFragment.getViewLifecycleOwner(), sliceView);
// Do not show the divider above media devices switcher slice per request
if (sliceUri.equals(MEDIA_OUTPUT_INDICATOR_SLICE_URI)) {
final Slice slice = sliceLiveData.getValue();
if (slice != null && slice.getUri().equals(MEDIA_OUTPUT_INDICATOR_SLICE_URI)) {
mDividerAllowedAbove = false;
}
// Log Panel interaction
sliceView.setOnSliceActionListener(
((eventInfo, sliceItem) -> {
FeatureFactory.getFactory(context)
FeatureFactory.getFactory(sliceView.getContext())
.getMetricsFeatureProvider()
.action(0 /* attribution */,
SettingsEnums.ACTION_PANEL_INTERACTION,
mPanelContent.getMetricsCategory(),
sliceUri.toString() /* log key */,
mMetricsCategory,
sliceLiveData.toString() /* log key */,
eventInfo.actionType /* value */);
})
);

View File

@@ -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.panel;
import android.net.Uri;
import androidx.slice.Slice;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
* Helper class to isolate the work tracking all of the {@link Slice Slices} being loaded.
* <p>
* Uses a {@link CountDownLatch} and a {@link Set} of Slices to track how many
* Slices have been loaded. A Slice can only be counted as being loaded a single time, even
* when they get updated later.
* <p>
* To use the class, pass the number of expected Slices to load into the constructor. For
* every Slice that loads, call {@link #markSliceLoaded(Uri)} with the corresponding
* {@link Uri}. Then check if all of the Slices have loaded with
* {@link #isPanelReadyToLoad()}, which will return {@code true} the first time after all
* Slices have loaded.
*/
public class PanelSlicesLoaderCountdownLatch {
private final Set<Uri> mLoadedSlices;
private final CountDownLatch mCountDownLatch;
private boolean slicesReadyToLoad = false;
public PanelSlicesLoaderCountdownLatch(int countdownSize) {
mLoadedSlices = new HashSet<>();
mCountDownLatch = new CountDownLatch(countdownSize);
}
/**
* Checks if the {@param sliceUri} has been loaded: if not, then decrement the countdown
* latch, and if so, then do nothing.
*/
public void markSliceLoaded(Uri sliceUri) {
if (mLoadedSlices.contains(sliceUri)) {
return;
}
mLoadedSlices.add(sliceUri);
mCountDownLatch.countDown();
}
/**
* @return {@code true} if the Slice has already been loaded.
*/
public boolean isSliceLoaded(Uri uri) {
return mLoadedSlices.contains(uri);
}
/**
* @return {@code true} when all Slices have loaded, and the Panel has not yet been loaded.
*/
public boolean isPanelReadyToLoad() {
/**
* Use {@link slicesReadyToLoad} to track whether or not the Panel has been loaded. We
* only want to animate the Panel a single time.
*/
if ((mCountDownLatch.getCount() == 0) && !slicesReadyToLoad) {
slicesReadyToLoad = true;
return true;
}
return false;
}
}

View File

@@ -30,6 +30,7 @@ import android.net.Uri;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.notification.RemoteVolumePreferenceController;
import java.util.ArrayList;
import java.util.List;
@@ -54,7 +55,9 @@ public class VolumePanel implements PanelContent {
@Override
public List<Uri> getSlices() {
final List<Uri> uris = new ArrayList<>();
uris.add(VOLUME_REMOTE_MEDIA_URI);
if (RemoteVolumePreferenceController.getActiveRemoteToken(mContext) != null) {
uris.add(VOLUME_REMOTE_MEDIA_URI);
}
uris.add(VOLUME_MEDIA_URI);
uris.add(MEDIA_OUTPUT_INDICATOR_SLICE_URI);
uris.add(VOLUME_CALL_URI);

View File

@@ -0,0 +1,59 @@
/*
* 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.sim;
import android.content.Context;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import java.util.ArrayList;
import java.util.List;
/**
* Specialized version of SimListDialogFragment that fetches a list of SIMs which support calls.
*/
public class CallsSimListDialogFragment extends SimListDialogFragment {
@Override
protected List<SubscriptionInfo> getCurrentSubscriptions() {
final Context context = getContext();
final SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
final TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
final List<PhoneAccountHandle> phoneAccounts =
telecomManager.getCallCapablePhoneAccounts();
final List<SubscriptionInfo> result = new ArrayList<>();
if (phoneAccounts == null) {
return result;
}
for (PhoneAccountHandle handle : phoneAccounts) {
final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(handle);
final int subId = telephonyManager.getSubIdForPhoneAccount(phoneAccount);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
continue;
}
result.add(subscriptionManager.getActiveSubscriptionInfo(subId));
}
return result;
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.sim;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
/**
* Presents a dialog asking the user if they want to update all services to use a given "preferred"
* SIM. Typically this would be used in a case where a device goes from having multiple SIMs down to
* only one.
*/
public class PreferredSimDialogFragment extends SimDialogFragment implements
DialogInterface.OnClickListener {
private static final String TAG = "PreferredSimDialogFrag";
public static PreferredSimDialogFragment newInstance() {
final PreferredSimDialogFragment fragment = new PreferredSimDialogFragment();
final Bundle args = initArguments(SimDialogActivity.PREFERRED_PICK,
R.string.sim_preferred_title);
fragment.setArguments(args);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final AlertDialog dialog = new AlertDialog.Builder(getContext())
.setTitle(getTitleResId())
.setPositiveButton(R.string.yes, this)
.setNegativeButton(R.string.no, null)
.create();
updateDialog(dialog);
return dialog;
}
@Override
public void onClick(DialogInterface dialog, int buttonClicked) {
if (buttonClicked != DialogInterface.BUTTON_POSITIVE) {
return;
}
final SimDialogActivity activity = (SimDialogActivity) getActivity();
final SubscriptionInfo info = getPreferredSubscription();
if (info != null) {
activity.onSubscriptionSelected(getDialogType(), info.getSubscriptionId());
}
}
public SubscriptionInfo getPreferredSubscription() {
final Activity activity = getActivity();
final int slotId = activity.getIntent().getIntExtra(SimDialogActivity.PREFERRED_SIM,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
return getSubscriptionManager().getActiveSubscriptionInfoForSimSlotIndex(slotId);
}
private void updateDialog(AlertDialog dialog) {
final SubscriptionInfo info = getPreferredSubscription();
if (info == null) {
dismiss();
return;
}
final String message =
getContext().getString(R.string.sim_preferred_message, info.getDisplayName());
dialog.setMessage(message);
}
@Override
public void updateDialog() {
updateDialog((AlertDialog) getDialog());
}
@VisibleForTesting
protected SubscriptionManager getSubscriptionManager() {
return getContext().getSystemService(SubscriptionManager.class);
}
}

View File

@@ -16,37 +16,30 @@
package com.android.settings.sim;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.content.Intent;
import android.os.Bundle;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SimDialogActivity extends Activity {
/**
* This activity provides singleton semantics per dialog type for showing various kinds of
* dialogs asking the user to make choices about which SIM to use for various services
* (calls, SMS, and data).
*/
public class SimDialogActivity extends FragmentActivity {
private static String TAG = "SimDialogActivity";
public static String PREFERRED_SIM = "preferred_sim";
@@ -60,276 +53,118 @@ public class SimDialogActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int dialogType = getIntent().getIntExtra(DIALOG_TYPE_KEY, INVALID_PICK);
showOrUpdateDialog();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
showOrUpdateDialog();
}
private void showOrUpdateDialog() {
final int dialogType = getIntent().getIntExtra(DIALOG_TYPE_KEY, INVALID_PICK);
final String tag = Integer.toString(dialogType);
final FragmentManager fragmentManager = getSupportFragmentManager();
SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
if (fragment == null) {
fragment = createFragment(dialogType);
fragment.show(fragmentManager, tag);
} else {
fragment.updateDialog();
}
}
private SimDialogFragment createFragment(int dialogType) {
switch(dialogType) {
case DATA_PICK:
return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
false /* includeAskEveryTime */);
case CALLS_PICK:
return CallsSimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_calls,
true /* includeAskEveryTime */);
case SMS_PICK:
return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
false /* includeAskEveryTime */);
case PREFERRED_PICK:
if (!getIntent().hasExtra(PREFERRED_SIM)) {
throw new IllegalArgumentException("Missing required extra " + PREFERRED_SIM);
}
return PreferredSimDialogFragment.newInstance();
default:
throw new IllegalArgumentException( "Invalid dialog type " + dialogType + " sent.");
}
}
public void onSubscriptionSelected(int dialogType, int subId) {
if (getSupportFragmentManager().findFragmentByTag(Integer.toString(dialogType)) == null) {
Log.w(TAG, "onSubscriptionSelected ignored because stored fragment was null");
return;
}
switch (dialogType) {
case DATA_PICK:
setDefaultDataSubId(subId);
break;
case CALLS_PICK:
setDefaultCallsSubId(subId);
break;
case SMS_PICK:
createDialog(this, dialogType).show();
setDefaultSmsSubId(subId);
break;
case PREFERRED_PICK:
displayPreferredDialog(getIntent().getIntExtra(PREFERRED_SIM, 0));
setPreferredSim(subId);
break;
default:
throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent.");
}
}
private void displayPreferredDialog(final int slotId) {
final Resources res = getResources();
final Context context = getApplicationContext();
final SubscriptionInfo sir = SubscriptionManager.from(context)
.getActiveSubscriptionInfoForSimSlotIndex(slotId);
if (sir != null) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(R.string.sim_preferred_title);
alertDialogBuilder.setMessage(res.getString(
R.string.sim_preferred_message, sir.getDisplayName()));
alertDialogBuilder.setPositiveButton(R.string.yes, new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
final int subId = sir.getSubscriptionId();
PhoneAccountHandle phoneAccountHandle =
subscriptionIdToPhoneAccountHandle(subId);
setDefaultDataSubId(context, subId);
setDefaultSmsSubId(context, subId);
setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
finish();
}
});
alertDialogBuilder.setNegativeButton(R.string.no, new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int id) {
finish();
}
});
alertDialogBuilder.create().show();
} else {
finish();
throw new IllegalArgumentException(
"Invalid dialog type " + dialogType + " sent.");
}
}
private static void setDefaultDataSubId(final Context context, final int subId) {
final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
final TelephonyManager telephonyManager = TelephonyManager.from(context)
.createForSubscriptionId(subId);
public void onFragmentDismissed(SimDialogFragment simDialogFragment) {
final List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments.size() == 1 && fragments.get(0) == simDialogFragment) {
finishAndRemoveTask();
}
}
private void setDefaultDataSubId(final int subId) {
final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
final TelephonyManager telephonyManager = getSystemService(
TelephonyManager.class).createForSubscriptionId(subId);
subscriptionManager.setDefaultDataSubId(subId);
telephonyManager.setDataEnabled(true);
Toast.makeText(context, R.string.data_switch_started, Toast.LENGTH_LONG).show();
Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
}
private static void setDefaultSmsSubId(final Context context, final int subId) {
final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
subscriptionManager.setDefaultSmsSubId(subId);
}
private void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle phoneAccount) {
final TelecomManager telecomManager = TelecomManager.from(this);
private void setDefaultCallsSubId(final int subId) {
final PhoneAccountHandle phoneAccount = subscriptionIdToPhoneAccountHandle(subId);
final TelecomManager telecomManager = getSystemService(TelecomManager.class);
telecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccount);
}
private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
final TelecomManager telecomManager = TelecomManager.from(this);
final TelephonyManager telephonyManager = TelephonyManager.from(this);
final Iterator<PhoneAccountHandle> phoneAccounts =
telecomManager.getCallCapablePhoneAccounts().listIterator();
private void setDefaultSmsSubId(final int subId) {
final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
subscriptionManager.setDefaultSmsSubId(subId);
}
while (phoneAccounts.hasNext()) {
final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
private void setPreferredSim(final int subId) {
setDefaultDataSubId(subId);
setDefaultSmsSubId(subId);
setDefaultCallsSubId(subId);
}
private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
final TelecomManager telecomManager = getSystemService(TelecomManager.class);
final TelephonyManager telephonyManager = getSystemService(TelephonyManager.class);
for (PhoneAccountHandle handle : telecomManager.getCallCapablePhoneAccounts()) {
final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(handle);
if (subId == telephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
return phoneAccountHandle;
return handle;
}
}
return null;
}
public Dialog createDialog(final Context context, final int id) {
final ArrayList<String> list = new ArrayList<String>();
final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
final List<SubscriptionInfo> subInfoList =
subscriptionManager.getActiveSubscriptionInfoList(true);
final int selectableSubInfoLength = subInfoList == null ? 0 : subInfoList.size();
final DialogInterface.OnClickListener selectionListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int value) {
final SubscriptionInfo sir;
switch (id) {
case DATA_PICK:
sir = subInfoList.get(value);
setDefaultDataSubId(context, sir.getSubscriptionId());
break;
case CALLS_PICK:
final TelecomManager telecomManager =
TelecomManager.from(context);
final List<PhoneAccountHandle> phoneAccountsList =
telecomManager.getCallCapablePhoneAccounts();
setUserSelectedOutgoingPhoneAccount(
value < 1 ? null : phoneAccountsList.get(value - 1));
break;
case SMS_PICK:
sir = subInfoList.get(value);
setDefaultSmsSubId(context, sir.getSubscriptionId());
break;
default:
throw new IllegalArgumentException("Invalid dialog type "
+ id + " in SIM dialog.");
}
finish();
}
};
Dialog.OnKeyListener keyListener = new Dialog.OnKeyListener() {
@Override
public boolean onKey(DialogInterface arg0, int keyCode,
KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish();
}
return true;
}
};
ArrayList<SubscriptionInfo> callsSubInfoList = new ArrayList<SubscriptionInfo>();
if (id == CALLS_PICK) {
final TelecomManager telecomManager = TelecomManager.from(context);
final TelephonyManager telephonyManager = TelephonyManager.from(context);
final Iterator<PhoneAccountHandle> phoneAccounts =
telecomManager.getCallCapablePhoneAccounts().listIterator();
list.add(getResources().getString(R.string.sim_calls_ask_first_prefs_title));
callsSubInfoList.add(null);
while (phoneAccounts.hasNext()) {
final PhoneAccount phoneAccount =
telecomManager.getPhoneAccount(phoneAccounts.next());
list.add((String)phoneAccount.getLabel());
int subId = telephonyManager.getSubIdForPhoneAccount(phoneAccount);
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
final SubscriptionInfo sir = SubscriptionManager.from(context)
.getActiveSubscriptionInfo(subId);
callsSubInfoList.add(sir);
} else {
callsSubInfoList.add(null);
}
}
} else {
for (int i = 0; i < selectableSubInfoLength; ++i) {
final SubscriptionInfo sir = subInfoList.get(i);
CharSequence displayName = sir.getDisplayName();
if (displayName == null) {
displayName = "";
}
list.add(displayName.toString());
}
}
String[] arr = list.toArray(new String[0]);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
ListAdapter adapter = new SelectAccountListAdapter(
id == CALLS_PICK ? callsSubInfoList : subInfoList,
builder.getContext(),
R.layout.select_account_list_item,
arr, id);
switch (id) {
case DATA_PICK:
builder.setTitle(R.string.select_sim_for_data);
break;
case CALLS_PICK:
builder.setTitle(R.string.select_sim_for_calls);
break;
case SMS_PICK:
builder.setTitle(R.string.select_sim_for_sms);
break;
default:
throw new IllegalArgumentException("Invalid dialog type "
+ id + " in SIM dialog.");
}
Dialog dialog = builder.setAdapter(adapter, selectionListener).create();
dialog.setOnKeyListener(keyListener);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
finish();
}
});
return dialog;
}
private class SelectAccountListAdapter extends ArrayAdapter<String> {
private Context mContext;
private int mResId;
private int mDialogId;
private final float OPACITY = 0.54f;
private List<SubscriptionInfo> mSubInfoList;
public SelectAccountListAdapter(List<SubscriptionInfo> subInfoList,
Context context, int resource, String[] arr, int dialogId) {
super(context, resource, arr);
mContext = context;
mResId = resource;
mDialogId = dialogId;
mSubInfoList = subInfoList;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView;
final ViewHolder holder;
if (convertView == null) {
// Cache views for faster scrolling
rowView = inflater.inflate(mResId, null);
holder = new ViewHolder();
holder.title = (TextView) rowView.findViewById(R.id.title);
holder.summary = (TextView) rowView.findViewById(R.id.summary);
holder.icon = (ImageView) rowView.findViewById(R.id.icon);
rowView.setTag(holder);
} else {
rowView = convertView;
holder = (ViewHolder) rowView.getTag();
}
final SubscriptionInfo sir = mSubInfoList.get(position);
if (sir == null) {
holder.title.setText(getItem(position));
holder.summary.setText("");
holder.icon.setImageDrawable(getResources()
.getDrawable(R.drawable.ic_live_help));
holder.icon.setAlpha(OPACITY);
} else {
holder.title.setText(sir.getDisplayName());
holder.summary.setText(sir.getNumber());
holder.icon.setImageBitmap(sir.createIconBitmap(mContext));
}
return rowView;
}
private class ViewHolder {
TextView title;
TextView summary;
ImageView icon;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.sim;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
/** Common functionality for showing a dialog in SimDialogActivity. */
public abstract class SimDialogFragment extends DialogFragment {
private static final String TAG = "SimDialogFragment";
private static final String KEY_TITLE_ID = "title_id";
private static final String KEY_DIALOG_TYPE = "dialog_type";
protected static Bundle initArguments(int dialogType, int titleResId) {
final Bundle args = new Bundle();
args.putInt(KEY_DIALOG_TYPE, dialogType);
args.putInt(KEY_TITLE_ID, titleResId);
return args;
}
public int getDialogType() {
return getArguments().getInt(KEY_DIALOG_TYPE);
}
public int getTitleResId() {
return getArguments().getInt(KEY_TITLE_ID);
}
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog);
final SimDialogActivity activity = (SimDialogActivity) getActivity();
if (activity != null && !activity.isFinishing()) {
activity.onFragmentDismissed(this);
}
}
public abstract void updateDialog();
}

View File

@@ -0,0 +1,183 @@
/*
* 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.sim;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* Shows a dialog consisting of a list of SIMs (aka subscriptions), possibly including an additional
* entry indicating "ask me every time".
*/
public class SimListDialogFragment extends SimDialogFragment implements
DialogInterface.OnClickListener {
protected static final String KEY_INCLUDE_ASK_EVERY_TIME = "include_ask_every_time";
protected SelectSubscriptionAdapter mAdapter;
@VisibleForTesting
List<SubscriptionInfo> mSubscriptions;
public static SimListDialogFragment newInstance(int dialogType, int titleResId,
boolean includeAskEveryTime) {
final SimListDialogFragment fragment = new SimListDialogFragment();
final Bundle args = initArguments(dialogType, titleResId);
args.putBoolean(KEY_INCLUDE_ASK_EVERY_TIME, includeAskEveryTime);
fragment.setArguments(args);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
mSubscriptions = new ArrayList<>();
final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(getTitleResId());
mAdapter = new SelectSubscriptionAdapter(builder.getContext(), mSubscriptions);
setAdapter(builder);
final Dialog dialog = builder.create();
updateDialog();
return dialog;
}
@Override
public void onClick(DialogInterface dialog, int selectionIndex) {
if (selectionIndex >= 0 && selectionIndex < mSubscriptions.size()) {
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
final SubscriptionInfo subscription = mSubscriptions.get(selectionIndex);
if (subscription != null) {
subId = subscription.getSubscriptionId();
}
final SimDialogActivity activity = (SimDialogActivity) getActivity();
activity.onSubscriptionSelected(getDialogType(), subId);
}
}
protected List<SubscriptionInfo> getCurrentSubscriptions() {
final SubscriptionManager manager = getContext().getSystemService(
SubscriptionManager.class);
return manager.getActiveSubscriptionInfoList(true);
}
@Override
public void updateDialog() {
List<SubscriptionInfo> currentSubscriptions = getCurrentSubscriptions();
if (currentSubscriptions == null) {
dismiss();
return;
}
if (getArguments().getBoolean(KEY_INCLUDE_ASK_EVERY_TIME)) {
final List<SubscriptionInfo> tmp = new ArrayList<>(currentSubscriptions.size() + 1);
tmp.add(null);
tmp.addAll(currentSubscriptions);
currentSubscriptions = tmp;
}
if (currentSubscriptions.equals(mSubscriptions)) {
return;
}
mSubscriptions.clear();
mSubscriptions.addAll(currentSubscriptions);
mAdapter.notifyDataSetChanged();
}
@VisibleForTesting
void setAdapter(AlertDialog.Builder builder) {
builder.setAdapter(mAdapter, this);
}
private static class SelectSubscriptionAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mInflater;
List<SubscriptionInfo> mSubscriptions;
public SelectSubscriptionAdapter(Context context, List<SubscriptionInfo> subscriptions) {
mSubscriptions = subscriptions;
mContext = context;
}
@Override
public int getCount() {
return mSubscriptions.size();
}
@Override
public SubscriptionInfo getItem(int position) {
return mSubscriptions.get(position);
}
@Override
public long getItemId(int position) {
final SubscriptionInfo info = mSubscriptions.get(position);
if (info == null) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
return info.getSubscriptionId();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
if (mInflater == null) {
mInflater = LayoutInflater.from(parent.getContext());
}
convertView = mInflater.inflate(R.layout.select_account_list_item, parent, false);
}
final SubscriptionInfo sub = getItem(position);
final TextView title = convertView.findViewById(R.id.title);
final TextView summary = convertView.findViewById(R.id.summary);
final ImageView icon = convertView.findViewById(R.id.icon);
if (sub == null) {
title.setText(R.string.sim_calls_ask_first_prefs_title);
summary.setText("");
icon.setImageDrawable(mContext.getDrawable(R.drawable.ic_help));
icon.setImageTintList(
Utils.getColorAttr(mContext, android.R.attr.textColorSecondary));
} else {
title.setText(sub.getDisplayName());
summary.setText(sub.getNumber());
icon.setImageBitmap(sub.createIconBitmap(mContext));
}
return convertView;
}
}
}

View File

@@ -19,8 +19,6 @@ package com.android.settings.slices;
import static android.provider.SettingsSlicesContract.KEY_LOCATION;
import static android.provider.SettingsSlicesContract.KEY_WIFI;
import static com.android.settings.notification.ZenModeSoundSettingsPreferenceController.ZEN_MODE_KEY;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.SettingsSlicesContract;
@@ -43,6 +41,7 @@ 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.notification.ZenModeButtonPreferenceController;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
import com.android.settings.wifi.slice.ContextualWifiSlice;
import com.android.settings.wifi.slice.WifiSlice;
@@ -298,7 +297,7 @@ public class CustomSliceRegistry {
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath(ZEN_MODE_KEY)
.appendPath(ZenModeButtonPreferenceController.KEY)
.build();
/**

View File

@@ -164,11 +164,12 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
}
final SliderPreferenceController sliderController = (SliderPreferenceController) controller;
final int maxSteps = sliderController.getMaxSteps();
if (newPosition < 0 || newPosition > maxSteps) {
final int minValue = sliderController.getMin();
final int maxValue = sliderController.getMax();
if (newPosition < minValue || newPosition > maxValue) {
throw new IllegalArgumentException(
"Invalid position passed to Slider controller. Expected between 0 and "
+ maxSteps + " but found " + newPosition);
"Invalid position passed to Slider controller. Expected between " + minValue
+ " and " + maxValue + " but found " + newPosition);
}
sliderController.setSliderPosition(newPosition);

View File

@@ -326,7 +326,8 @@ public class SliceBuilderUtils {
.setTitle(sliceData.getTitle())
.setSubtitle(subtitleText)
.setPrimaryAction(primaryAction)
.setMax(sliderController.getMaxSteps())
.setMax(sliderController.getMax())
.setMin(sliderController.getMin())
.setValue(sliderController.getSliderPosition())
.setInputAction(actionIntent))
.setKeywords(keywords)

View File

@@ -93,7 +93,7 @@ public class SeekBarPreference extends RestrictedPreference
@Override
public boolean isSelectable() {
return false;
return isDisabledByAdmin();
}
@Override

View File

@@ -0,0 +1,101 @@
/*
* 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.widget;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.drawable.DrawableWrapper;
import android.util.AttributeSet;
import android.util.Log;
import com.android.settings.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
/**
* A Drawable that tints a contained Drawable, overriding the existing tint specified in the
* underlying drawable. This class should only be used in XML.
*
* @attr ref android.R.styleable#DrawableWrapper_drawable
* @attr ref R.styleable#TintDrawable_tint
*/
public class TintDrawable extends DrawableWrapper {
private ColorStateList mTint;
private int[] mThemeAttrs;
/** No-arg constructor used by drawable inflation. */
public TintDrawable() {
super(null);
}
@Override
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.TintDrawable);
super.inflate(r, parser, attrs, theme);
mThemeAttrs = a.extractThemeAttrs();
updateStateFromTypedArray(a);
a.recycle();
applyTint();
}
@Override
public void applyTheme(Theme t) {
super.applyTheme(t);
if (mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.TintDrawable);
updateStateFromTypedArray(a);
a.recycle();
}
// Ensure tint is reapplied after applying the theme to ensure this drawables'
// tint overrides the underlying drawables' tint.
applyTint();
}
@Override
public boolean canApplyTheme() {
return (mThemeAttrs != null && mThemeAttrs.length > 0) || super.canApplyTheme();
}
private void updateStateFromTypedArray(@NonNull TypedArray a) {
if (a.hasValue(R.styleable.TintDrawable_android_drawable)) {
setDrawable(a.getDrawable(R.styleable.TintDrawable_android_drawable));
}
if (a.hasValue(R.styleable.TintDrawable_android_tint)) {
mTint = a.getColorStateList(R.styleable.TintDrawable_android_tint);
}
}
private void applyTint() {
if (getDrawable() != null && mTint != null) {
getDrawable().mutate().setTintList(mTint);
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.wifi;
import android.annotation.StyleRes;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -64,11 +65,21 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase,
public static WifiDialog createModal(Context context, WifiDialogListener listener,
AccessPoint accessPoint, int mode) {
return new WifiDialog(context, listener, accessPoint, mode, 0 /* style */,
mode == WifiConfigUiBase.MODE_VIEW /* hideSubmitButton*/);
mode == WifiConfigUiBase.MODE_VIEW /* hideSubmitButton */);
}
/**
* Creates a WifiDialog with customized style. It displays as a dialog above the current
* view.
*/
public static WifiDialog createModal(Context context, WifiDialogListener listener,
AccessPoint accessPoint, int mode, @StyleRes int style) {
return new WifiDialog(context, listener, accessPoint, mode, style,
mode == WifiConfigUiBase.MODE_VIEW /* hideSubmitButton */);
}
/* package */ WifiDialog(Context context, WifiDialogListener listener, AccessPoint accessPoint,
int mode, int style, boolean hideSubmitButton) {
int mode, @StyleRes int style, boolean hideSubmitButton) {
super(context, style);
mMode = mode;
mListener = listener;

View File

@@ -28,6 +28,7 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settingslib.wifi.AccessPoint;
@@ -74,8 +75,13 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo
accessPoint = new AccessPoint(this, accessPointState);
}
mDialog = WifiDialog.createModal(
this, this, accessPoint, WifiConfigUiBase.MODE_CONNECT);
if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
mDialog = WifiDialog.createModal(this, this, accessPoint,
WifiConfigUiBase.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light);
} else {
mDialog = WifiDialog.createModal(
this, this, accessPoint, WifiConfigUiBase.MODE_CONNECT);
}
mDialog.show();
mDialog.setOnDismissListener(this);
}

View File

@@ -404,9 +404,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
.setButton3Enabled(true)
.setButton4Text(R.string.share)
.setButton4Icon(R.drawable.ic_qrcode_24dp)
.setButton4OnClickListener(view -> shareNetwork())
.setButton4Visible(
WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mContext, mAccessPoint));
.setButton4OnClickListener(view -> shareNetwork());
mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF);
mTxLinkSpeedPref = screen.findPreference(KEY_TX_LINK_SPEED);
@@ -739,14 +737,19 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
mButtonsPref.setButton1Text(
mIsEphemeral ? R.string.wifi_disconnect_button_text : R.string.forget);
mButtonsPref.setButton1Visible(canForgetNetwork());
mButtonsPref.setButton2Visible(canSignIntoNetwork());
mButtonsPref.setButton3Visible(canConnectNetwork());
mButtonsPref.setButton4Visible(canShareNetwork());
mButtonsPref.setVisible(canSignIntoNetwork()
|| canForgetNetwork()
|| canShareNetwork()
|| canConnectNetwork());
boolean canForgetNetwork = canForgetNetwork();
boolean canSignIntoNetwork = canSignIntoNetwork();
boolean canConnectNetwork = canConnectNetwork();
boolean canShareNetwork = canShareNetwork();
mButtonsPref.setButton1Visible(canForgetNetwork);
mButtonsPref.setButton2Visible(canSignIntoNetwork);
mButtonsPref.setButton3Visible(canConnectNetwork);
mButtonsPref.setButton4Visible(canShareNetwork);
mButtonsPref.setVisible(canForgetNetwork
|| canSignIntoNetwork
|| canConnectNetwork
|| canShareNetwork);
}
private boolean canConnectNetwork() {
@@ -843,7 +846,8 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
* Returns whether the user can share the network represented by this preference with QR code.
*/
private boolean canShareNetwork() {
return mAccessPoint.getConfig() != null;
return mAccessPoint.getConfig() != null &&
WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mContext, mAccessPoint);
}
/**

View File

@@ -345,6 +345,9 @@ public class WifiDppUtils {
*/
public static boolean isSupportConfiguratorQrCodeScanner(Context context,
AccessPoint accessPoint) {
if (accessPoint.isPasspoint()) {
return false;
}
return isSupportWifiDpp(context, accessPoint.getSecurity());
}
@@ -356,6 +359,9 @@ public class WifiDppUtils {
*/
public static boolean isSupportConfiguratorQrCodeGenerator(Context context,
AccessPoint accessPoint) {
if (accessPoint.isPasspoint()) {
return false;
}
return isSupportZxing(context, accessPoint.getSecurity());
}

View File

@@ -154,19 +154,6 @@ public class AccessibilitySettingsTest {
assertThat(preference.getSummary()).isEqualTo(mContext.getResources().getString(resId));
}
@Test
public void testDarkUIModePreferenceSummary_shouldUpdateSummary() {
final Preference darkUIModePreference = new Preference(mContext);
final DarkUIPreferenceController mController;
doReturn(darkUIModePreference).when(mSettings).findPreference(
DARK_UI_MODE_PREFERENCE);
mController = new DarkUIPreferenceController(mContext, DARK_UI_MODE_PREFERENCE);
final String darkUIModeDescription = modeToDescription(mUiModeManager.getNightMode());
darkUIModePreference.setSummary(mController.getSummary());
assertThat(darkUIModePreference.getSummary()).isEqualTo(darkUIModeDescription);
}
private String modeToDescription(int mode) {
String[] values = mContext.getResources().getStringArray(R.array.dark_ui_mode_entries);
switch (mode) {

View File

@@ -31,8 +31,10 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import static com.android.settings.backup.UserBackupSettingsActivityTest.ShadowBackupSettingsHelper;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowPrivacySettingsUtils.class})
@Config(shadows = {ShadowPrivacySettingsUtils.class, ShadowBackupSettingsHelper.class})
public class BackupInactivePreferenceControllerTest {
private Context mContext;
private BackupInactivePreferenceController mController;
@@ -48,18 +50,32 @@ public class BackupInactivePreferenceControllerTest {
@After
public void tearDown() {
ShadowPrivacySettingsUtils.reset();
ShadowBackupSettingsHelper.reset();
}
@Test
public void getAvailabilityStatus_isnotInvisibleKey_shouldBeAvailable() {
public void getAvailabilityStatus_isnotInvisibleKey_showBackup_shouldBeAvailable() {
ShadowPrivacySettingsUtils.setIsInvisibleKey(false);
ShadowBackupSettingsHelper.showBackupSettingsForUser = true;
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
}
@Test
public void getAvailabilityStatus_isnotInvisibleKey_dontShowBackup_shouldBeUnsearchable() {
ShadowPrivacySettingsUtils.setIsInvisibleKey(false);
ShadowBackupSettingsHelper.showBackupSettingsForUser = false;
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE_UNSEARCHABLE);
}
@Test
public void getAvailabilityStatus_isInvisibleKey_shouldBeDisabledUnsupported() {
ShadowPrivacySettingsUtils.setIsInvisibleKey(true);
ShadowBackupSettingsHelper.showBackupSettingsForUser = true;
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
}

View File

@@ -29,7 +29,6 @@ import android.app.Application;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -53,8 +52,7 @@ import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {UserBackupSettingsActivityTest.ShadowBackupSettingsHelper.class,
UserBackupSettingsActivityTest.ShadowUserHandle.class})
@Config(shadows = {UserBackupSettingsActivityTest.ShadowBackupSettingsHelper.class})
public class UserBackupSettingsActivityTest {
private ActivityController<UserBackupSettingsActivity> mActivityController;
private UserBackupSettingsActivity mActivity;
@@ -85,7 +83,7 @@ public class UserBackupSettingsActivityTest {
@After
public void resetShadows() {
ShadowUserHandle.reset();
ShadowBackupSettingsHelper.reset();
}
@Test
@@ -125,7 +123,9 @@ public class UserBackupSettingsActivityTest {
}
@Test
public void getNonIndexableKeys_SystemUser() {
public void getNonIndexableKeys_whenShowBackupSettings() {
ShadowBackupSettingsHelper.showBackupSettingsForUser = true;
assertThat(UserBackupSettingsActivity.SEARCH_INDEX_DATA_PROVIDER.getRawDataToIndex(
mApplication, true)).isNotEmpty();
assertThat(UserBackupSettingsActivity.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(
@@ -133,17 +133,24 @@ public class UserBackupSettingsActivityTest {
}
@Test
public void getNonIndexableKeys_NonSystemUser() {
ShadowUserHandle.setUid(1); // Non-SYSTEM user.
public void getNonIndexableKeys_whenDontShowBackupSettings() {
ShadowBackupSettingsHelper.showBackupSettingsForUser = false;
assertThat(UserBackupSettingsActivity.SEARCH_INDEX_DATA_PROVIDER.getRawDataToIndex(
mApplication, true)).isNotEmpty();
assertThat(UserBackupSettingsActivity.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(
mApplication)).isEmpty();
mApplication)).contains("Backup");
}
@Implements(BackupSettingsHelper.class)
public static class ShadowBackupSettingsHelper {
static boolean showBackupSettingsForUser = true;
@Implementation
public boolean showBackupSettingsForUser() {
return showBackupSettingsForUser;
}
@Implementation
protected Intent getIntentForBackupSettings() {
return mIntent;
@@ -153,24 +160,10 @@ public class UserBackupSettingsActivityTest {
protected boolean isBackupProvidedByManufacturer() {
return mIsBackupProvidedByOEM;
}
}
@Implements(UserHandle.class)
public static class ShadowUserHandle {
private static int sUid = 0; // SYSTEM by default
public static void setUid(int uid) {
sUid = uid;
}
@Implementation
protected static int myUserId() {
return sUid;
}
@Resetter
public static void reset() {
sUid = 0;
showBackupSettingsForUser = true;
}
}
}

View File

@@ -41,7 +41,8 @@ public class SettingsSliderPreferenceControllerTest {
"key");
mPreference.setContinuousUpdates(true);
mPreference.setMax(mSliderController.getMaxSteps());
mPreference.setMin(mSliderController.getMin());
mPreference.setMax(mSliderController.getMax());
}
@Test
@@ -89,10 +90,15 @@ public class SettingsSliderPreferenceControllerTest {
}
@Override
public int getMaxSteps() {
public int getMax() {
return MAX_STEPS;
}
@Override
public int getMin() {
return 0;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;

View File

@@ -88,10 +88,15 @@ public class SliderPreferenceControllerTest {
}
@Override
public int getMaxSteps() {
public int getMax() {
return MAX_STEPS;
}
@Override
public int getMin() {
return 0;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;

View File

@@ -18,22 +18,32 @@ package com.android.settings.datausage;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.app.Activity;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Spinner;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.widget.LoadingViewController;
import com.android.settingslib.AppItem;
import com.android.settingslib.NetworkPolicyEditor;
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
@@ -45,15 +55,14 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
@RunWith(RobolectricTestRunner.class)
public class DataUsageListTest {
@@ -61,18 +70,21 @@ public class DataUsageListTest {
private CellDataPreference.DataStateListener mListener;
@Mock
private TemplatePreference.NetworkServices mNetworkServices;
@Mock
private Context mContext;
private Activity mActivity;
private DataUsageList mDataUsageList;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest();
final ActivityController<Activity> mActivityController =
Robolectric.buildActivity(Activity.class);
mActivity = spy(mActivityController.get());
mNetworkServices.mPolicyEditor = mock(NetworkPolicyEditor.class);
mDataUsageList = spy(DataUsageList.class);
doReturn(mContext).when(mDataUsageList).getContext();
doReturn(mActivity).when(mDataUsageList).getContext();
ReflectionHelpers.setField(mDataUsageList, "mDataStateListener", mListener);
ReflectionHelpers.setField(mDataUsageList, "services", mNetworkServices);
}
@@ -86,11 +98,11 @@ public class DataUsageListTest {
mDataUsageList.onResume();
verify(mListener).setListener(true, mDataUsageList.mSubId, mContext);
verify(mListener).setListener(true, mDataUsageList.mSubId, mActivity);
mDataUsageList.onPause();
verify(mListener).setListener(false, mDataUsageList.mSubId, mContext);
verify(mListener).setListener(false, mDataUsageList.mSubId, mActivity);
}
@Test
@@ -140,7 +152,7 @@ public class DataUsageListTest {
final List<NetworkCycleChartData> data = new ArrayList<>();
final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
builder.setStartTime(startTime)
.setEndTime(endTime);
.setEndTime(endTime);
data.add(builder.build());
ReflectionHelpers.setField(mDataUsageList, "mCycleData", data);
final Spinner spinner = mock(Spinner.class);
@@ -150,14 +162,58 @@ public class DataUsageListTest {
mDataUsageList.startAppDataUsage(new AppItem());
verify(mContext).startActivity(intent.capture());
verify(mActivity).startActivity(intent.capture());
final Bundle arguments =
intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(endTime);
final ArrayList<Long> cycles =
(ArrayList) arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES);
(ArrayList) arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES);
assertThat(cycles).hasSize(2);
assertThat(cycles.get(0)).isEqualTo(endTime);
assertThat(cycles.get(1)).isEqualTo(startTime);
}
@Test
public void onViewCreated_shouldHideCycleSpinner() {
final View view = new View(mActivity);
final View header = getHeader();
final Spinner spinner = getSpinner(header);
spinner.setVisibility(View.VISIBLE);
doReturn(header).when(mDataUsageList).setPinnedHeaderView(anyInt());
doReturn(view).when(mDataUsageList).getView();
mDataUsageList.onViewCreated(view, null);
assertThat(spinner.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onLoadFinished_networkCycleDataCallback_shouldShowCycleSpinner() {
final LoadingViewController loadingViewController = mock(LoadingViewController.class);
mDataUsageList.mLoadingViewController = loadingViewController;
final Spinner spinner = getSpinner(getHeader());
spinner.setVisibility(View.INVISIBLE);
mDataUsageList.mCycleSpinner = spinner;
assertThat(spinner.getVisibility()).isEqualTo(View.INVISIBLE);
doNothing().when(mDataUsageList).updatePolicy();
mDataUsageList.mNetworkCycleDataCallbacks.onLoadFinished(null, null);
assertThat(spinner.getVisibility()).isEqualTo(View.VISIBLE);
}
private View getHeader() {
final View rootView = LayoutInflater.from(mActivity)
.inflate(R.layout.preference_list_fragment, null, false);
final FrameLayout pinnedHeader = rootView.findViewById(R.id.pinned_header);
final View header = mActivity.getLayoutInflater()
.inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
return header;
}
private Spinner getSpinner(View header) {
final Spinner spinner = header.findViewById(R.id.filter_spinner);
return spinner;
}
}

View File

@@ -20,15 +20,21 @@ import static android.provider.Settings.System.ADAPTIVE_SLEEP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settingslib.RestrictedPreference;
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;
@@ -42,6 +48,9 @@ public class AdaptiveSleepPreferenceControllerTest {
private AdaptiveSleepPreferenceController mController;
private ContentResolver mContentResolver;
@Mock
private PackageManager mPackageManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -49,6 +58,10 @@ public class AdaptiveSleepPreferenceControllerTest {
mContext = RuntimeEnvironment.application;
mContentResolver = mContext.getContentResolver();
mController = new AdaptiveSleepPreferenceController(mContext, PREFERENCE_KEY);
when(mPackageManager.checkPermission(any(), any())).thenReturn(
PackageManager.PERMISSION_GRANTED);
}
@Test
@@ -114,4 +127,24 @@ public class AdaptiveSleepPreferenceControllerTest {
new AdaptiveSleepPreferenceController(mContext, "any_key");
assertThat(controller.isSliceable()).isTrue();
}
@Test
public void isChecked_returnsFalseWhenNotSufficientPermissions() {
when(mPackageManager.checkPermission(any(), any())).thenReturn(
PackageManager.PERMISSION_DENIED);
mController.setChecked(true);
assertThat(mController.isChecked()).isFalse();
}
@Test
public void isEnabled_returnsFalseWhenNotSufficientPermissions() {
when(mPackageManager.checkPermission(any(), any())).thenReturn(
PackageManager.PERMISSION_DENIED);
mController.setChecked(true);
final RestrictedPreference mPreference = new RestrictedPreference(mContext);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isFalse();
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.display;
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.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class DarkUIInfoDialogFragmentTest {
private DarkUIInfoDialogFragment mFragment;
@Mock
private DialogInterface dialog;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mFragment = spy(new DarkUIInfoDialogFragment());
}
@Test
public void dialogDismissedOnConfirmation() {
doReturn(RuntimeEnvironment.application).when(mFragment).getContext();
SharedPreferences prefs = RuntimeEnvironment.application.getSharedPreferences(
DarkUIPreferenceController.DARK_MODE_PREFS,
Context.MODE_PRIVATE);
assertThat(prefs.getBoolean(DarkUIPreferenceController.PREF_DARK_MODE_DIALOG_SEEN, false))
.isFalse();
mFragment.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
verify(dialog, times(1)).dismiss();
assertThat(prefs.getBoolean(DarkUIPreferenceController.PREF_DARK_MODE_DIALOG_SEEN, false))
.isTrue();
}
}

View File

@@ -1,52 +0,0 @@
package com.android.settings.display;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.app.UiModeManager;
import android.content.Context;
import androidx.preference.Preference;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class DarkUISettingsRadioButtonsControllerTest {
@Mock
private UiModeManager mUiModeManager;
@Mock
private Preference mFooter;
private Context mContext;
private DarkUISettingsRadioButtonsController mController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new DarkUISettingsRadioButtonsController(mContext, mFooter);
mController.mManager = mUiModeManager;
}
@Test
public void footerUpdatesCorrectly() {
doReturn(UiModeManager.MODE_NIGHT_YES).when(mUiModeManager).getNightMode();
mController.updateFooter();
verify(mFooter).setSummary(eq(R.string.dark_ui_settings_dark_summary));
doReturn(UiModeManager.MODE_NIGHT_NO).when(mUiModeManager).getNightMode();
mController.updateFooter();
verify(mFooter).setSummary(eq(R.string.dark_ui_settings_light_summary));
}
public int getCurrentMode() {
final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
return uiModeManager.getNightMode();
}
}

View File

@@ -16,6 +16,10 @@
package com.android.settings.gestures;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -43,6 +47,9 @@ import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class PreventRingingSwitchPreferenceControllerTest {
private static final int UNKNOWN = -1;
private Context mContext;
private Resources mResources;
private PreventRingingSwitchPreferenceController mController;
@@ -76,35 +83,88 @@ public class PreventRingingSwitchPreferenceControllerTest {
}
@Test
public void testOn_updateState_hushOff() {
public void updateState_hushOff_uncheck() {
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE,
Settings.Secure.VOLUME_HUSH_OFF);
VOLUME_HUSH_OFF);
mController.updateState(mPreference);
verify(mController.mSwitch, times(1)).setChecked(false);
}
@Test
public void testOn_updateState_hushVibrate() {
public void updateState_hushVibrate_setChecked() {
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE,
Settings.Secure.VOLUME_HUSH_VIBRATE);
VOLUME_HUSH_VIBRATE);
mController.updateState(mPreference);
verify(mController.mSwitch, times(1)).setChecked(true);
}
@Test
public void testOn_updateState_hushMute() {
public void updateState_hushMute_setChecked() {
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE,
Settings.Secure.VOLUME_HUSH_MUTE);
VOLUME_HUSH_MUTE);
mController.updateState(mPreference);
verify(mController.mSwitch, times(1)).setChecked(true);
}
@Test
public void onSwitchChanged_wasHushOff_checked_returnHushVibrate() {
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE,
VOLUME_HUSH_OFF);
mController.onSwitchChanged(null, true);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.VOLUME_HUSH_GESTURE, UNKNOWN)).isEqualTo(VOLUME_HUSH_VIBRATE);
}
@Test
public void onSwitchChanged_wasHushMute_unchecked_returnHushOff() {
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE,
VOLUME_HUSH_MUTE);
mController.onSwitchChanged(null, false);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.VOLUME_HUSH_GESTURE, UNKNOWN)).isEqualTo(VOLUME_HUSH_OFF);
}
@Test
public void onSwitchChanged_wasHushMute_checked_returnHushMute() {
// this is the case for the page open
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE,
VOLUME_HUSH_MUTE);
mController.onSwitchChanged(null, true);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.VOLUME_HUSH_GESTURE, UNKNOWN)).isEqualTo(VOLUME_HUSH_MUTE);
}
@Test
public void onSwitchChanged_wasHushVibrate_checked_returnHushVibrate() {
// this is the case for the page open
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE,
VOLUME_HUSH_VIBRATE);
mController.onSwitchChanged(null, true);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.VOLUME_HUSH_GESTURE, UNKNOWN)).isEqualTo(VOLUME_HUSH_VIBRATE);
}
@Test
public void testPreferenceClickListenerAttached() {
PreferenceScreen preferenceScreen = mock(PreferenceScreen.class);
LayoutPreference mLayoutPreference = mock(LayoutPreference.class);
when(preferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
mLayoutPreference);
mController.displayPreference(preferenceScreen);
verify(mLayoutPreference, times(1))

View File

@@ -18,7 +18,11 @@ package com.android.settings.homepage.contextualcards.slices;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.Uri;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
@@ -26,11 +30,13 @@ import androidx.slice.SliceProvider;
import androidx.slice.widget.SliceLiveData;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.EarlyWarningTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
import com.android.settings.slices.SliceBackgroundWorker;
import org.junit.After;
import org.junit.Before;
@@ -48,6 +54,10 @@ import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public class BatteryFixSliceTest {
private Context mContext;
@@ -66,13 +76,11 @@ public class BatteryFixSliceTest {
@After
public void tearDown() {
ShadowBatteryTipLoader.reset();
ShadowSliceBackgroundWorker.reset();
ShadowEarlyWarningTip.reset();
}
@Test
@Config(shadows = {
ShadowBatteryStatsHelperLoader.class,
ShadowBatteryTipLoader.class
})
public void updateBatteryTipAvailabilityCache_hasImportantTip_shouldReturnTrue() {
final List<BatteryTip> tips = new ArrayList<>();
tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, ""));
@@ -85,10 +93,6 @@ public class BatteryFixSliceTest {
}
@Test
@Config(shadows = {
ShadowBatteryStatsHelperLoader.class,
ShadowBatteryTipLoader.class
})
public void getSlice_unimportantSlice_shouldSkip() {
final List<BatteryTip> tips = new ArrayList<>();
tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, ""));
@@ -101,6 +105,28 @@ public class BatteryFixSliceTest {
assertThat(SliceMetadata.from(mContext, slice).isErrorSlice()).isTrue();
}
@Test
@Config(shadows = {
BatteryFixSliceTest.ShadowEarlyWarningTip.class,
BatteryFixSliceTest.ShadowSliceBackgroundWorker.class
})
public void getSlice_hasImportantTip_shouldTintIcon() {
final List<BatteryTip> tips = new ArrayList<>();
tips.add(new EarlyWarningTip(BatteryTip.StateType.NEW, false));
// Create fake cache data
ShadowBatteryTipLoader.setBatteryTips(tips);
BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext);
// Create fake background worker data
BatteryFixSlice.BatteryTipWorker batteryTipWorker = mock(
BatteryFixSlice.BatteryTipWorker.class);
when(batteryTipWorker.getResults()).thenReturn(tips);
ShadowSliceBackgroundWorker.setBatteryTipWorkerWorker(batteryTipWorker);
final Slice slice = mSlice.getSlice();
assertThat(ShadowEarlyWarningTip.isIconTintColorIdCalled()).isTrue();
}
@Implements(BatteryStatsHelperLoader.class)
public static class ShadowBatteryStatsHelperLoader {
@@ -129,4 +155,45 @@ public class BatteryFixSliceTest {
sBatteryTips = tips;
}
}
@Implements(SliceBackgroundWorker.class)
public static class ShadowSliceBackgroundWorker {
private static BatteryFixSlice.BatteryTipWorker sBatteryTipWorkerWorker;
@Resetter
public static void reset() {
sBatteryTipWorkerWorker = null;
}
@Implementation
protected static <T extends SliceBackgroundWorker> T getInstance(Uri uri) {
return (T) sBatteryTipWorkerWorker;
}
public static void setBatteryTipWorkerWorker(BatteryFixSlice.BatteryTipWorker worker) {
sBatteryTipWorkerWorker = worker;
}
}
@Implements(EarlyWarningTip.class)
public static class ShadowEarlyWarningTip {
private static boolean mIsGetIconTintColorIdCalled;
@Resetter
public static void reset() {
mIsGetIconTintColorIdCalled = false;
}
@Implementation
protected int getIconTintColorId() {
mIsGetIconTintColorIdCalled = true;
return R.color.battery_bad_color_light;
}
public static boolean isIconTintColorIdCalled() {
return mIsGetIconTintColorIdCalled;
}
}
}

View File

@@ -31,6 +31,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.os.UserManager;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
@@ -65,6 +66,8 @@ public class MobileNetworkSummaryControllerTest {
private EuiccManager mEuiccManager;
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private UserManager mUserManager;
private AddPreference mPreference;
private Context mContext;
@@ -76,6 +79,7 @@ public class MobileNetworkSummaryControllerTest {
mContext = spy(Robolectric.setupActivity(Activity.class));
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(EuiccManager.class)).thenReturn(mEuiccManager);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mTelephonyManager.getNetworkCountryIso()).thenReturn("");
when(mEuiccManager.isEnabled()).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.EUICC_PROVISIONED, 1);
@@ -97,9 +101,22 @@ public class MobileNetworkSummaryControllerTest {
final ConnectivityManager cm = mock(ConnectivityManager.class);
when(cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(cm);
when(mUserManager.isAdminUser()).thenReturn(true);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_secondaryUser_notAvailable() {
final ConnectivityManager cm = mock(ConnectivityManager.class);
when(cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(cm);
when(mUserManager.isAdminUser()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void getSummary_noSubscriptions_correctSummaryAndClickHandler() {
mController.displayPreference(mPreferenceScreen);
@@ -282,6 +299,18 @@ public class MobileNetworkSummaryControllerTest {
assertThat(captor.getValue()).isFalse();
}
@Test
public void onResume_noSubscriptionEsimDisabled_isDisabled() {
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
SubscriptionUtil.setAvailableSubscriptionsForTesting(null);
when(mEuiccManager.isEnabled()).thenReturn(false);
mController.displayPreference(mPreferenceScreen);
mController.onResume();
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void onAirplaneModeChanged_oneSubscriptionAirplaneModeGetsTurnedOn_isDisabled() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);

View File

@@ -121,7 +121,12 @@ public class AdjustVolumeRestrictedPreferenceControllerTest {
}
@Override
public int getMaxSteps() {
public int getMax() {
return 0;
}
@Override
public int getMin() {
return 0;
}
}

View File

@@ -29,10 +29,12 @@ import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
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;
@@ -50,9 +52,9 @@ public class RemoteVolumePreferenceControllerTest {
private MediaSessionManager mMediaSessionManager;
@Mock
private MediaController mMediaController;
@Mock
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ISessionController mStub;
@Mock
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ISessionController mStub2;
private MediaSession.Token mToken;
private MediaSession.Token mToken2;
@@ -78,22 +80,30 @@ public class RemoteVolumePreferenceControllerTest {
mPlaybackInfo = new MediaController.PlaybackInfo(
MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, 0, MAX_POS, CURRENT_POS, null);
when(mMediaController.getPlaybackInfo()).thenReturn(mPlaybackInfo);
when(mMediaController.getSessionToken()).thenReturn(mToken);
}
@Test
public void isAvailable_containRemoteMedia_returnTrue() {
public void getActiveRemoteToken_containRemoteMedia_returnToken() {
when(mMediaController.getPlaybackInfo()).thenReturn(
new MediaController.PlaybackInfo(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
0, 0, 0, null));
assertThat(mController.isAvailable()).isTrue();
assertThat(mController.getActiveRemoteToken(mContext)).isEqualTo(mToken);
}
@Test
public void isAvailable_noRemoteMedia_returnFalse() {
public void getActiveRemoteToken_noRemoteMedia_returnNull() {
when(mMediaController.getPlaybackInfo()).thenReturn(
new MediaController.PlaybackInfo(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
0, 0, 0, null));
assertThat(mController.isAvailable()).isFalse();
assertThat(mController.getActiveRemoteToken(mContext)).isNull();
}
@Test
public void isAvailable_returnAvailableUnsearchable() {
assertThat(mController.isAvailable()).isTrue();
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE_UNSEARCHABLE);
}
@Test
@@ -122,17 +132,31 @@ public class RemoteVolumePreferenceControllerTest {
}
@Test
public void getMaxSteps_controllerNull_returnZero() {
public void getMinValue_controllerNull_returnZero() {
mController.mMediaController = null;
assertThat(mController.getMaxSteps()).isEqualTo(0);
assertThat(mController.getMin()).isEqualTo(0);
}
@Test
public void getMaxSteps_controllerExists_returnValue() {
public void getMinValue_controllerExists_returnValue() {
mController.mMediaController = mMediaController;
assertThat(mController.getMaxSteps()).isEqualTo(MAX_POS);
assertThat(mController.getMin()).isEqualTo(0);
}
@Test
public void getMaxValue_controllerNull_returnZero() {
mController.mMediaController = null;
assertThat(mController.getMax()).isEqualTo(0);
}
@Test
public void getMaxValue_controllerExists_returnValue() {
mController.mMediaController = mMediaController;
assertThat(mController.getMax()).isEqualTo(MAX_POS);
}
@Test

View File

@@ -106,8 +106,10 @@ public class VolumeSeekBarPreferenceControllerTest {
public void sliderMethods_handleNullPreference() {
when(mHelper.getStreamVolume(mController.getAudioStream())).thenReturn(4);
when(mHelper.getMaxVolume(mController.getAudioStream())).thenReturn(10);
when(mHelper.getMinVolume(mController.getAudioStream())).thenReturn(1);
assertThat(mController.getMaxSteps()).isEqualTo(10);
assertThat(mController.getMax()).isEqualTo(10);
assertThat(mController.getMin()).isEqualTo(1);
assertThat(mController.getSliderPosition()).isEqualTo(4);
mController.setSliderPosition(9);
@@ -123,11 +125,19 @@ public class VolumeSeekBarPreferenceControllerTest {
}
@Test
public void getMaxSteps_passesAlongValue() {
public void getMaxValue_passesAlongValue() {
when(mPreference.getMax()).thenReturn(6);
mController.displayPreference(mScreen);
assertThat(mController.getMaxSteps()).isEqualTo(6);
assertThat(mController.getMax()).isEqualTo(6);
}
@Test
public void getMinValue_passesAlongValue() {
when(mPreference.getMin()).thenReturn(1);
mController.displayPreference(mScreen);
assertThat(mController.getMin()).isEqualTo(1);
}
@Test

View File

@@ -1,50 +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.notification;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
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.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class ZenModeSoundSettingsPreferenceControllerTest {
private Context mContext;
private ZenModeSoundSettingsPreferenceController mController;
private static final String KEY_ZEN_MODE = "zen_mode";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new ZenModeSoundSettingsPreferenceController(mContext, KEY_ZEN_MODE);
}
@Test
public void getAvailabilityStatus_available() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
}

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -81,12 +82,16 @@ public class PanelFragmentTest {
}
@Test
public void onCreateView_adapterGetsDataset() {
public void onCreateView_countdownLatch_setup() {
mPanelFragment.onCreateView(LayoutInflater.from(mContext),
new LinearLayout(mContext), null);
PanelSlicesAdapter adapter = mPanelFragment.mAdapter;
PanelSlicesLoaderCountdownLatch countdownLatch =
mPanelFragment.mPanelSlicesLoaderCountdownLatch;
for (Uri sliecUri: mFakePanelContent.getSlices()) {
countdownLatch.markSliceLoaded(sliecUri);
}
assertThat(adapter.getData()).containsAllIn(mFakePanelContent.getSlices());
assertThat(countdownLatch.isPanelReadyToLoad()).isTrue();
}
@Test

View File

@@ -23,41 +23,53 @@ 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.when;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.lifecycle.LiveData;
import androidx.slice.Slice;
import com.android.settings.R;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class PanelSlicesAdapterTest {
private static final Uri DATA_URI = CustomSliceRegistry.DATA_USAGE_SLICE_URI;
private Context mContext;
private PanelFragment mPanelFragment;
private FakePanelContent mFakePanelContent;
private FakeFeatureFactory mFakeFeatureFactory;
private PanelFeatureProvider mPanelFeatureProvider;
private FakeFeatureFactory mFakeFeatureFactory;
private FakePanelContent mFakePanelContent;
private List<LiveData<Slice>> mData = new ArrayList<>();
@Mock
private LiveData<Slice> mLiveData;
private Slice mSlice;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPanelFeatureProvider = spy(new PanelFeatureProviderImpl());
@@ -76,12 +88,22 @@ public class PanelSlicesAdapterTest {
.get()
.getSupportFragmentManager()
.findFragmentById(R.id.main_content));
}
private void constructTestLiveData(Uri uri) {
// Create a slice to return for the LiveData
mSlice = spy(new Slice());
doReturn(uri).when(mSlice).getUri();
when(mLiveData.getValue()).thenReturn(mSlice);
mData.add(mLiveData);
}
@Test
public void onCreateViewHolder_returnsSliceRowViewHolder() {
constructTestLiveData(DATA_URI);
final PanelSlicesAdapter adapter =
new PanelSlicesAdapter(mPanelFragment, mFakePanelContent);
new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
final ViewGroup view = new FrameLayout(mContext);
final PanelSlicesAdapter.SliceRowViewHolder viewHolder =
adapter.onCreateViewHolder(view, 0);
@@ -89,24 +111,11 @@ public class PanelSlicesAdapterTest {
assertThat(viewHolder.sliceView).isNotNull();
}
@Test
public void onBindViewHolder_bindsSlice() {
final PanelSlicesAdapter adapter =
new PanelSlicesAdapter(mPanelFragment, mFakePanelContent);
final int position = 0;
final ViewGroup view = new FrameLayout(mContext);
final PanelSlicesAdapter.SliceRowViewHolder viewHolder =
adapter.onCreateViewHolder(view, 0 /* view type*/);
adapter.onBindViewHolder(viewHolder, position);
assertThat(viewHolder.sliceLiveData).isNotNull();
}
@Test
public void nonMediaOutputIndicatorSlice_shouldAllowDividerAboveAndBelow() {
constructTestLiveData(DATA_URI);
final PanelSlicesAdapter adapter =
new PanelSlicesAdapter(mPanelFragment, mFakePanelContent);
new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
final int position = 0;
final ViewGroup view = new FrameLayout(mContext);
final PanelSlicesAdapter.SliceRowViewHolder viewHolder =
@@ -120,32 +129,10 @@ public class PanelSlicesAdapterTest {
@Test
public void mediaOutputIndicatorSlice_shouldNotAllowDividerAbove() {
PanelContent mediaOutputIndicatorSlicePanelContent = new PanelContent() {
@Override
public CharSequence getTitle() {
return "title";
}
@Override
public List<Uri> getSlices() {
return Arrays.asList(
MEDIA_OUTPUT_INDICATOR_SLICE_URI
);
}
@Override
public Intent getSeeMoreIntent() {
return new Intent();
}
@Override
public int getMetricsCategory() {
return SettingsEnums.TESTING;
}
};
constructTestLiveData(MEDIA_OUTPUT_INDICATOR_SLICE_URI);
final PanelSlicesAdapter adapter =
new PanelSlicesAdapter(mPanelFragment, mediaOutputIndicatorSlicePanelContent);
new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
final int position = 0;
final ViewGroup view = new FrameLayout(mContext);
final PanelSlicesAdapter.SliceRowViewHolder viewHolder =

View File

@@ -0,0 +1,92 @@
/*
* 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.panel;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.net.Uri;
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 PanelSlicesLoaderCountdownLatchTest {
private Context mContext;
private PanelSlicesLoaderCountdownLatch mSliceCountdownLatch;
private static final Uri[] URIS = new Uri[] {
Uri.parse("content://testUri"),
Uri.parse("content://wowUri"),
Uri.parse("content://boxTurtle")
};
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mSliceCountdownLatch = new PanelSlicesLoaderCountdownLatch(URIS.length);
}
@Test
public void hasLoaded_newObject_returnsFalse() {
assertThat(mSliceCountdownLatch.isSliceLoaded(URIS[0])).isFalse();
}
@Test
public void hasLoaded_markSliceLoaded_returnsTrue() {
mSliceCountdownLatch.markSliceLoaded(URIS[0]);
assertThat(mSliceCountdownLatch.isSliceLoaded(URIS[0])).isTrue();
}
@Test
public void markSliceLoaded_onlyCountsDownUniqueUris() {
for (int i = 0; i < URIS.length; i++) {
mSliceCountdownLatch.markSliceLoaded(URIS[0]);
}
assertThat(mSliceCountdownLatch.isPanelReadyToLoad()).isFalse();
}
@Test
public void areSlicesReadyToLoad_allSlicesLoaded_returnsTrue() {
for (int i = 0; i < URIS.length; i++) {
mSliceCountdownLatch.markSliceLoaded(URIS[i]);
}
assertThat(mSliceCountdownLatch.isPanelReadyToLoad()).isTrue();
}
@Test
public void areSlicesReadyToLoad_onlyReturnsTrueOnce() {
for (int i = 0; i < URIS.length; i++) {
mSliceCountdownLatch.markSliceLoaded(URIS[i]);
}
// Verify that it returns true once
assertThat(mSliceCountdownLatch.isPanelReadyToLoad()).isTrue();
// Verify the second call returns false without external state change
assertThat(mSliceCountdownLatch.isPanelReadyToLoad()).isFalse();
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.sim;
import static com.android.settings.sim.SimDialogActivity.PREFERRED_PICK;
import static com.android.settings.sim.SimDialogActivity.PREFERRED_SIM;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.DialogInterface;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowAlertDialogCompat.class)
public class PreferredSimDialogFragmentTest extends
SimDialogFragmentTestBase<PreferredSimDialogFragment> {
@Override
public void setUp() {
super.setUp();
setDialogType(PREFERRED_PICK);
mFragment = spy(PreferredSimDialogFragment.newInstance());
doReturn(mSubscriptionManager).when(mFragment).getSubscriptionManager();
}
@Test
public void onCreateDialog_noSims_dismissed() {
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt()))
.thenReturn(null);
mIntent.putExtra(PREFERRED_SIM, 0);
startDialog();
verify(mFragment).dismiss();
}
@Test
public void onCreateDialog_oneSimWrongSlotArgument_dismissed() {
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(1)).thenReturn(null);
mIntent.putExtra(PREFERRED_SIM, 1);
startDialog();
verify(mFragment).dismiss();
}
@Test
public void onCreateDialog_twoSimsSelectFirst_correctMessage() {
mIntent.putExtra(PREFERRED_SIM, 0);
final AlertDialog alertDialog = startDialog();
final ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
final String message = (String) shadowDialog.getMessage();
assertThat(message).contains(SIM1_NAME);
assertThat(message).doesNotContain(SIM2_NAME);
}
@Test
public void onCreateDialog_twoSimsSelectSecond_correctMessage() {
mIntent.putExtra(PREFERRED_SIM, 1);
final AlertDialog alertDialog = startDialog();
final ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
final String message = (String) shadowDialog.getMessage();
assertThat(message).contains(SIM2_NAME);
assertThat(message).doesNotContain(SIM1_NAME);
}
@Test
public void onClick_yesClicked_callsOnSubscriptionSelected() {
mIntent.putExtra(PREFERRED_SIM, 0);
final AlertDialog alertDialog = startDialog();
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
doReturn(activity).when(mFragment).getActivity();
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
mFragment.onClick(alertDialog, DialogInterface.BUTTON_POSITIVE);
verify(activity).onSubscriptionSelected(PREFERRED_PICK, SIM1_ID);
}
@Test
public void onClick_noClicked_doesNotCallOnSubscriptionSelected() {
mIntent.putExtra(PREFERRED_SIM, 0);
final AlertDialog alertDialog = startDialog();
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
doReturn(activity).when(mFragment).getActivity();
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
mFragment.onClick(alertDialog, DialogInterface.BUTTON_NEGATIVE);
verify(activity, never()).onSubscriptionSelected(anyInt(), anyInt());
}
}

View File

@@ -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.sim;
import static com.android.settings.sim.SimDialogActivity.DIALOG_TYPE_KEY;
import static org.mockito.Mockito.when;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.shadows.androidx.fragment.FragmentController;
public abstract class SimDialogFragmentTestBase<T extends SimDialogFragment> {
protected static final int SIM1_ID = 111;
protected static final int SIM2_ID = 222;
protected static final String SIM1_NAME = "sim111";
protected static final String SIM2_NAME = "sim222";
@Mock
protected SubscriptionManager mSubscriptionManager;
@Mock
protected SubscriptionInfo mSim1;
@Mock
protected SubscriptionInfo mSim2;
protected T mFragment;
protected Intent mIntent;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mIntent = new Intent();
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(0)).thenReturn(mSim1);
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(1)).thenReturn(mSim2);
when(mSim1.getSubscriptionId()).thenReturn(SIM1_ID);
when(mSim1.getDisplayName()).thenReturn(SIM1_NAME);
when(mSim2.getSubscriptionId()).thenReturn(SIM2_ID);
when(mSim2.getDisplayName()).thenReturn(SIM2_NAME);
}
protected void setDialogType(int dialogType) {
mIntent.putExtra(DIALOG_TYPE_KEY, dialogType);
}
protected AlertDialog startDialog() {
final FragmentController controller = FragmentController.of(mFragment,
SimDialogActivity.class, mIntent);
controller.create(0 /* containerViewId */, null /* bundle */).start().visible();
return ShadowAlertDialogCompat.getLatestAlertDialog();
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.sim;
import static com.android.settings.sim.SimDialogActivity.DATA_PICK;
import static com.android.settings.sim.SimDialogActivity.SMS_PICK;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.telephony.SubscriptionManager;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowAlertDialogCompat.class)
public class SimListDialogFragmentTest extends SimDialogFragmentTestBase<SimListDialogFragment> {
@Test
public void onCreateDialog_noSubscriptions_dismissed() {
final int dialogType = DATA_PICK;
setDialogType(dialogType);
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
false /* includeAskEveryTime */));
doReturn(null).when(mFragment).getCurrentSubscriptions();
startDialog();
verify(mFragment).dismiss();
}
@Test
public void onCreateDialog_twoSubscriptionsNoAskEveryTime_twoSubsForDisplay() {
final int dialogType = DATA_PICK;
setDialogType(dialogType);
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
false /* includeAskEveryTime */));
doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
// Avoid problems robolectric has with our real adapter.
doNothing().when(mFragment).setAdapter(any());
final AlertDialog alertDialog = startDialog();
assertThat(mFragment.mSubscriptions).hasSize(2);
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
doReturn(activity).when(mFragment).getActivity();
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
mFragment.onClick(alertDialog, 1);
verify(activity).onSubscriptionSelected(dialogType, SIM2_ID);
}
@Test
public void onCreateDialog_twoSubscriptionsAskEveryTime_threeSubsForDisplay() {
final int dialogType = SMS_PICK;
setDialogType(dialogType);
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
true /* includeAskEveryTime */));
doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
// Avoid problems robolectric has with our real adapter.
doNothing().when(mFragment).setAdapter(any());
final AlertDialog alertDialog = startDialog();
assertThat(mFragment.mSubscriptions).hasSize(3);
assertThat(mFragment.mSubscriptions.get(0)).isNull();
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
doReturn(activity).when(mFragment).getActivity();
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
mFragment.onClick(alertDialog, 0);
verify(activity).onSubscriptionSelected(dialogType,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
}

View File

@@ -191,8 +191,8 @@ public class SliceBroadcastReceiverTest {
.build();
final ContentResolver resolver = mock(ContentResolver.class);
doReturn(resolver).when(mContext).getContentResolver();
final int position = FakeSliderController.MAX_STEPS - 1;
final int oldPosition = FakeSliderController.MAX_STEPS;
final int position = FakeSliderController.MAX_VALUE - 1;
final int oldPosition = FakeSliderController.MAX_VALUE;
mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
insertSpecialCase(FakeSliderController.class, key);
@@ -310,8 +310,8 @@ public class SliceBroadcastReceiverTest {
// Insert Fake Slider into Database
final String key = "key";
final int position = FakeSliderController.MAX_STEPS - 1;
final int oldPosition = FakeSliderController.MAX_STEPS;
final int position = FakeSliderController.MAX_VALUE - 1;
final int oldPosition = FakeSliderController.MAX_VALUE;
mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
insertSpecialCase(FakeSliderController.class, key);

View File

@@ -25,7 +25,7 @@ public class FakeSliderController extends SliderPreferenceController {
public static final String AVAILABILITY_KEY = "fake_slider_availability_key";
public static final int MAX_STEPS = 9;
public static final int MAX_VALUE = 9;
private static final String SETTING_KEY = "fake_slider_key";
@@ -44,8 +44,13 @@ public class FakeSliderController extends SliderPreferenceController {
}
@Override
public int getMaxSteps() {
return MAX_STEPS;
public int getMax() {
return MAX_VALUE;
}
@Override
public int getMin() {
return 0;
}
@Override

View File

@@ -18,12 +18,16 @@ package com.android.settings.widget;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Parcelable;
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;
@@ -39,9 +43,10 @@ public class SeekBarPreferenceTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mSeekBarPreference = new SeekBarPreference(mContext);
mSeekBarPreference = spy(new SeekBarPreference(mContext));
mSeekBarPreference.setMax(MAX);
mSeekBarPreference.setMin(MIN);
mSeekBarPreference.setProgress(PROGRESS);
@@ -59,4 +64,18 @@ public class SeekBarPreferenceTest {
assertThat(preference.getMin()).isEqualTo(MIN);
assertThat(preference.getProgress()).isEqualTo(PROGRESS);
}
@Test
public void isSelectable_disabledByAdmin_returnTrue() {
when(mSeekBarPreference.isDisabledByAdmin()).thenReturn(true);
assertThat(mSeekBarPreference.isSelectable()).isTrue();
}
@Test
public void isSelectable_notDisabledByAdmin_returnFalse() {
when(mSeekBarPreference.isDisabledByAdmin()).thenReturn(false);
assertThat(mSeekBarPreference.isSelectable()).isFalse();
}
}

View File

@@ -23,10 +23,13 @@ import static org.mockito.Mockito.doReturn;
import android.content.Intent;
import android.net.wifi.WifiConfiguration;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowConnectivityManager;
import com.android.settings.testutils.shadow.ShadowWifiManager;
import com.google.android.setupcompat.util.WizardManagerHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,7 +75,7 @@ public class WifiDialogActivityTest {
}
@Test
public void onSubmit_shouldNotConnectToNetwork_whenConnectForCallerIsFalse() {
public void onSubmit_whenConnectForCallerIsFalse_shouldNotConnectToNetwork() {
WifiDialogActivity activity =
Robolectric.buildActivity(
WifiDialogActivity.class,
@@ -88,4 +91,24 @@ public class WifiDialogActivityTest {
assertThat(ShadowWifiManager.get().savedWifiConfig).isNull();
}
@Test
public void onSubmit_whenLaunchInSetupFlow_shouldBeLightThemeForWifiDialog() {
WifiDialogActivity activity =
Robolectric.buildActivity(
WifiDialogActivity.class,
new Intent()
.putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, false)
.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true))
.setup().get();
WifiDialog dialog = (WifiDialog) ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
activity.onSubmit(dialog);
assertThat(dialog.getContext().getThemeResId())
.isEqualTo(R.style.SuwAlertDialogThemeCompat_Light);
}
}

View File

@@ -4,6 +4,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
import com.android.settings.wifi.WifiDialog.WifiDialogListener;
import com.android.settingslib.wifi.AccessPoint;
@@ -41,4 +42,16 @@ public class WifiDialogTest {
assertThat(modal.getContext().getThemeResId())
.isEqualTo(wifiDialog.getContext().getThemeResId());
}
@Test
public void createModal_whenSetTheme_shouldBeCustomizedTheme() {
WifiDialog modal = WifiDialog.createModal(mContext, mListener, mockAccessPoint,
WifiConfigUiBase.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light);
WifiDialog wifiDialog = new WifiDialog(mContext, mListener, mockAccessPoint,
WifiConfigUiBase.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light,
false /* hideSubmitButton */);
assertThat(modal.getContext().getThemeResId())
.isEqualTo(wifiDialog.getContext().getThemeResId());
}
}