android:parentActivityName="Settings">
<intent-filter android:priority="1">
<action android:name="android.settings.DEVICE_INFO_SETTINGS" />
+ <action android:name="android.settings.DEVICE_NAME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
</intent-filter>
</activity>
- <activity android:name=".deviceinfo.UsbModeChooserActivity"
+ <activity android:name=".connecteddevice.usb.UsbModeChooserActivity"
android:excludeFromRecents="true"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar">
</activity>
+ <activity android:name=".Settings$UsbDetailsActivity"
+ android:excludeFromRecents="true"
+ android:permission="android.permission.MANAGE_USB"
+ android:exported="true">
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.connecteddevice.usb.UsbDetailsFragment"/>
+ </activity>
+
<activity android:name=".RemoteBugreportActivity"
android:excludeFromRecents="true"
android:exported="true"
<activity android:name="Settings$AdvancedConnectedDeviceActivity"
android:label="@string/connected_device_connections_title"
android:taskAffinity="com.android.settings"
- android:parentActivityName="Settings$ConnectedDeviceDashboardActivity"
- android:enabled="false">
+ android:parentActivityName="Settings$ConnectedDeviceDashboardActivity">
<intent-filter android:priority="1">
<action android:name="com.android.settings.ADVANCED_CONNECTED_DEVICE_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.1" android:color="?android:attr/colorAccent" />
+</selector>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- style="@style/SuggestionConditionStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="5dp"
- android:paddingEnd="5dp"
- android:paddingBottom="@dimen/dashboard_padding_bottom">
-
- <android.support.v7.widget.CardView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:cardUseCompatPadding="true"
- app:cardElevation="2dp">
-
- <android.support.v7.widget.RecyclerView
- android:id="@+id/data"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/material_grey_300"
- android:scrollbars="none"/>
-
- </android.support.v7.widget.CardView>
-
-</FrameLayout>
limitations under the License.
-->
-<LinearLayout
+<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/suggestion_card"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ app:cardPreventCornerOverlap="false"
+ app:cardUseCompatPadding="true"
+ app:cardElevation="2dp"
+ app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@android:color/white"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:minHeight="@dimen/dashboard_tile_minimum_height">
+ android:minHeight="112dp"
+ android:orientation="vertical">
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/suggestion_card_icon_size"
- android:layout_height="@dimen/suggestion_card_icon_size"
- android:layout_marginStart="14dp"
- android:layout_marginEnd="24dp" />
-
- <LinearLayout
- android:layout_width="wrap_content"
+ <RelativeLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="horizontal">
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.TileTitle"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/suggestion_card_icon_size"
+ android:layout_height="@dimen/suggestion_card_icon_size"
+ style="@style/SuggestionCardIcon"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp" />
- <TextView
- android:id="@android:id/summary"
- android:textAppearance="@style/TextAppearance.SuggestionSummary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <ImageView
+ android:id="@+id/close_button"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:alpha="0.54"
+ android:layout_alignParentEnd="true"
+ android:layout_marginTop="8dp"
+ android:layout_marginEnd="8dp"
+ android:src="@drawable/ic_suggestion_close_button"/>
- </LinearLayout>
+ </RelativeLayout>
- </LinearLayout>
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/SuggestionCardText"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal" />
- <include layout="@layout/horizontal_divider" />
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/SuggestionCardText"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:textAppearance="@style/TextAppearance.SuggestionSummary" />
+
+ </LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</android.support.v7.widget.CardView>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<android.support.v7.widget.CardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/suggestion_card"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:cardPreventCornerOverlap="false"
- app:cardUseCompatPadding="true"
- app:cardElevation="2dp"
- app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="112dp"
- android:orientation="vertical">
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/suggestion_card_icon_size"
- android:layout_height="@dimen/suggestion_card_icon_size"
- style="@style/SuggestionCardIcon"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp" />
-
- <ImageView
- android:id="@+id/close_button"
- android:layout_width="18dp"
- android:layout_height="18dp"
- android:alpha="0.54"
- android:layout_alignParentEnd="true"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:src="@drawable/ic_suggestion_close_button"/>
-
- </RelativeLayout>
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/SuggestionCardText"
- android:layout_marginStart="12dp"
- android:layout_marginEnd="12dp"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
- android:ellipsize="end"
- android:fadingEdge="horizontal" />
-
- <TextView
- android:id="@android:id/summary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/SuggestionCardText"
- android:layout_marginStart="12dp"
- android:layout_marginEnd="12dp"
- android:textAppearance="@style/TextAppearance.SuggestionSummary" />
-
- </LinearLayout>
-
-</android.support.v7.widget.CardView>
limitations under the License.
-->
-<LinearLayout
+<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/suggestion_card"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@android:color/white"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingStart="16dp"
- android:paddingEnd="12dp"
- android:paddingBottom="20dp"
- android:paddingTop="16dp"
- android:orientation="horizontal"
- android:minHeight="@dimen/dashboard_tile_minimum_height">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/dashboard_tile_image_size"
- android:layout_height="@dimen/dashboard_tile_image_size" />
+ app:cardPreventCornerOverlap="false"
+ app:cardUseCompatPadding="true"
+ app:cardElevation="2dp"
+ app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:layout_marginStart="18dp"
- android:layout_marginTop="2dp"
- android:clipChildren="false"
- android:clipToPadding="false"
+ android:minHeight="112dp"
android:orientation="vertical">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/suggestion_card_icon_size"
+ android:layout_height="@dimen/suggestion_card_icon_size"
+ style="@style/SuggestionCardIcon"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp" />
+
+ <ImageView
+ android:id="@+id/close_button"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:alpha="0.54"
+ android:layout_alignParentEnd="true"
+ android:layout_marginTop="8dp"
+ android:layout_marginEnd="8dp"
+ android:src="@drawable/ic_suggestion_close_button"/>
+
+ </RelativeLayout>
+
<TextView
android:id="@android:id/title"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="6dp"
- android:textAppearance="@style/TextAppearance.TileTitle" />
+ style="@style/SuggestionCardText"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal" />
<TextView
android:id="@android:id/summary"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
- android:layout_marginStart="6dp"
- android:layout_marginEnd="50dp"
+ style="@style/SuggestionCardText"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
android:textAppearance="@style/TextAppearance.SuggestionSummary" />
<Button
android:id="@android:id/primary"
style="@style/ActionPrimaryButton"
+ android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/suggestion_button_text" />
+
</LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</android.support.v7.widget.CardView>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<android.support.v7.widget.CardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/suggestion_card"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:cardPreventCornerOverlap="false"
- app:cardUseCompatPadding="true"
- app:cardElevation="2dp"
- app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="112dp"
- android:orientation="vertical">
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/suggestion_card_icon_size"
- android:layout_height="@dimen/suggestion_card_icon_size"
- style="@style/SuggestionCardIcon"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp" />
-
- <ImageView
- android:id="@+id/close_button"
- android:layout_width="18dp"
- android:layout_height="18dp"
- android:alpha="0.54"
- android:layout_alignParentEnd="true"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:src="@drawable/ic_suggestion_close_button"/>
-
- </RelativeLayout>
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/SuggestionCardText"
- android:layout_marginStart="12dp"
- android:layout_marginEnd="12dp"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
- android:ellipsize="end"
- android:fadingEdge="horizontal" />
-
- <TextView
- android:id="@android:id/summary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/SuggestionCardText"
- android:layout_marginStart="12dp"
- android:layout_marginEnd="12dp"
- android:textAppearance="@style/TextAppearance.SuggestionSummary" />
-
- <Button
- android:id="@android:id/primary"
- style="@style/ActionPrimaryButton"
- android:layout_gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
- android:text="@string/suggestion_button_text" />
-
- </LinearLayout>
-
-</android.support.v7.widget.CardView>
<!-- The following two margins need to match, with the caveat that
the second should be negative. The second one ensures that the icons and text
align despite the additional padding caused by the search bar's card background. -->
- <dimen name="search_bar_margin">8dp</dimen>
- <dimen name="search_bar_negative_margin">-8dp</dimen>
+ <dimen name="search_bar_margin">16dp</dimen>
+ <dimen name="search_bar_negative_margin">-16dp</dimen>
<dimen name="search_bar_height">48dp</dimen>
<dimen name="search_bar_corner_radius">2dp</dimen>
*/
-->
<resources>
- <item type="id" name="preference_highlight_key" />
+ <item type="id" name="preference_highlighted" />
<item type="id" name="lock_none" />
<item type="id" name="lock_pin" />
<!-- User settings -->
<skip/>
- <!-- User settings screen title [CHAR LIMIT=25] -->
+ <!-- User settings screen title [CHAR LIMIT=40] -->
<string name="user_settings_title">Multiple users</string>
<!-- User settings header for list of users and profiles [CHAR LIMIT=40] -->
<string name="user_list_title">Users & profiles</string>
<string name="app_and_notification_dashboard_summary">Permissions, default apps</string>
<!-- Title for setting tile leading to account settings [CHAR LIMIT=40]-->
<string name="account_dashboard_title">Accounts</string>
+ <!-- Summary for account settings tiles when there is no accounts on device [CHAR LIMIT=NONE]-->
+ <string name="account_dashboard_default_summary">No accounts added</string>
<!-- Title for setting tile leading to setting UI which allows user set default app to
handle actions such as open web page, making phone calls, default SMS apps [CHAR LIMIT=40]-->
<string name="app_default_dashboard_title">Default apps</string>
<!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for powering the other device only. -->
- <string name="usb_use_power_only">Supply power</string>
+ <string name="usb_use_power_only">Charging connected device</string>
<!-- Decription of one of the choices in a dialog (with title defined in usb_use) that lets the
user select what the USB connection for this device should be used for. This choice
is for powering the other device. -->
- <string name="usb_use_power_only_desc">Charge the connected device. Works only with devices that support USB charging.</string>
+ <string name="usb_use_power_only_desc">Other settings unavailable when turned on</string>
<!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for transferring files via MTP. -->
- <string name="usb_use_file_transfers">Transfer files</string>
+ <string name="usb_use_file_transfers">File Transfer</string>
<!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for transferring files via MTP. -->
<!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for transferring photos via PTP. -->
- <string name="usb_use_photo_transfers">Transfer photos (PTP)</string>
+ <string name="usb_use_photo_transfers">PTP</string>
<!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for transferring photos via PTP. -->
<string name="usb_use_photo_transfers_desc">Transfer photos or files if MTP is not supported (PTP)</string>
<!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
+ is for USB tethering. -->
+ <string name="usb_use_tethering">USB tethering</string>
+ <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
+ select what the USB connection for this device should be used for. This choice
is for entering MIDI mode. -->
- <string name="usb_use_MIDI">Use device as MIDI</string>
+ <string name="usb_use_MIDI">MIDI</string>
<!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
select what the USB connection for this device should be used for. This choice
is for entering MIDI mode. -->
<string name="usb_use_MIDI_desc">Use this device as MIDI</string>
<!-- The title used in a dialog which lets the user select what the USB connection
- for this device should be used for. Choices are usb_use_charging_only,
- usb_use_file_transfer, use_use_photo_transfer, and usb_use_MIDI -->
- <string name="usb_use">Use USB to</string>
+ for this device should be used for. These options are more commonly used.
+ Choices are usb_use_file_transfer.-->
+ <string name="usb_use">Use USB for</string>
+ <!-- The title used in a dialog which lets the user select what the USB connection
+ for this device should be used for. These options are less commonly used.
+ Choices are usb_use_tethering, usb_use_photo_transfers, usb_use_MIDI, and usb_use_power_only.-->
+ <string name="usb_use_also">Also use USB for</string>
<!-- Settings item title for USB preference [CHAR LIMIT=35] -->
<string name="usb_pref">USB</string>
<!-- Settings item summary for USB preference when set to charging only [CHAR LIMIT=NONE] -->
<string name="usb_summary_charging_only">Charging this device</string>
<!-- Settings item summary for USB preference when set to powering the other device only [CHAR LIMIT=NONE] -->
- <string name="usb_summary_power_only">Supplying power</string>
+ <string name="usb_summary_power_only">Charging connected device</string>
<!-- Settings item summary for USB preference when set to transferring files via MTP [CHAR LIMIT=NONE] -->
- <string name="usb_summary_file_transfers">Transferring files</string>
+ <string name="usb_summary_file_transfers">File transfer</string>
+ <!-- Settings item summary for USB preference when set to USB tethering [CHAR LIMIT=NONE] -->
+ <string name="usb_summary_tether">USB tethering</string>
<!-- Settings item summary for USB preference when set to transferring photos via PTP [CHAR LIMIT=NONE] -->
- <string name="usb_summary_photo_transfers">Transferring photos (PTP)</string>
+ <string name="usb_summary_photo_transfers">PTP</string>
<!-- Settings item summary for USB preference when set to entering MIDI mode [CHAR LIMIT=NONE] -->
- <string name="usb_summary_MIDI">Using device as MIDI</string>
+ <string name="usb_summary_MIDI">MIDI</string>
+ <!-- Settings item summary for USB preference when set to transferring files via MTP
+ and powering other device [CHAR LIMIT=NONE] -->
+ <string name="usb_summary_file_transfers_power">File transfer and supplying power</string>
+ <!-- Settings item summary for USB preference when set to USB tethering
+ and powering other device [CHAR LIMIT=NONE] -->
+ <string name="usb_summary_tether_power">USB tethering and supplying power</string>
+ <!-- Settings item summary for USB preference when set to transferring photos via PTP
+ and powering other device [CHAR LIMIT=NONE] -->
+ <string name="usb_summary_photo_transfers_power">PTP and supplying power</string>
+ <!-- Settings item summary for USB preference when set to entering MIDI mode
+ and powering other device [CHAR LIMIT=NONE] -->
+ <string name="usb_summary_MIDI_power">MIDI and supplying power</string>
<!-- Settings item title for SMS Mirroring preference [CHAR LIMIT=35] -->
<string name="sms_mirroring_pref">SMS Mirroring</string>
<!-- Backup disabled summary [CHAR LIMIT=NONE] -->
<string name="backup_disabled">Back up disabled</string>
- <!-- Summary of device info page [CHAR LIMIT=NONE] -->
- <string name="about_summary">Updated to Android <xliff:g id="version" example="6.0">%1$s</xliff:g></string>
+ <!-- Summary of Android version info [CHAR LIMIT=NONE] -->
+ <string name="android_version_summary">Updated to Android <xliff:g id="version" example="6.0">%1$s</xliff:g></string>
+ <!-- Summary of Android version info (when there is a pending upgrade available) [CHAR LIMIT=NONE] -->
+ <string name="android_version_pending_update_summary">Update available</string>
<!-- Title for dialog displayed when user clicks on a setting locked by an admin [CHAR LIMIT=30] -->
<string name="disabled_by_policy_title">Action not allowed</string>
android:order="-2"/>
<Preference
- android:key="usb_mode"
- android:title="@string/usb_pref"
- android:icon="@drawable/ic_usb"
- android:order="-1">
- <intent android:action="android.intent.action.MAIN"
- android:targetPackage="com.android.settings"
- android:targetClass="com.android.settings.deviceinfo.UsbModeChooserActivity"/>
- </Preference>
-
- <Preference
android:key="bt_received_files"
android:icon="@drawable/ic_folder_vd_theme_24"
android:title="@string/bluetooth_show_received_files" />
android:order="-2">
<intent android:action="android.intent.action.MAIN"
android:targetPackage="com.android.settings"
- android:targetClass="com.android.settings.deviceinfo.UsbModeChooserActivity"/>
+ android:targetClass="com.android.settings.connecteddevice.usb.UsbModeChooserActivity"/>
</Preference>
<PreferenceCategory
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_system_update"
android:order="-30"
- settings:controller="com.android.settings.deviceinfo.SystemUpdatePreferenceController">
+ settings:controller="com.android.settings.system.SystemUpdatePreferenceController">
<intent android:action="android.settings.SYSTEM_UPDATE_SETTINGS" />
</Preference>
android:key="additional_system_update_settings"
android:title="@string/additional_system_update_settings_list_item_title"
android:order="-31"
- settings:controller="com.android.settings.deviceinfo.AdditionalSystemUpdatePreferenceController">
+ settings:controller="com.android.settings.system.AdditionalSystemUpdatePreferenceController">
<intent android:action="android.intent.action.MAIN"
android:targetPackage="@string/additional_system_update"
android:targetClass="@string/additional_system_update_menu" />
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/device_details_title">
+
+ <com.android.settings.applications.LayoutPreference
+ android:key="usb_device_header"
+ android:layout="@layout/settings_entity_header"
+ android:selectable="false"/>
+
+ <PreferenceCategory
+ android:key="usb_main_options"
+ android:title="@string/usb_use"/>
+
+ <PreferenceCategory
+ android:key="usb_secondary_options"
+ android:title="@string/usb_use_also"/>
+
+</PreferenceScreen>
public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ }
public static class ConditionProviderSettingsActivity extends SettingsActivity { /* empty */ }
public static class UsbSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class UsbDetailsActivity extends SettingsActivity { /* empty */ }
public static class TrustedCredentialsSettingsActivity extends SettingsActivity { /* empty */ }
public static class PaymentSettingsActivity extends SettingsActivity { /* empty */ }
public static class PrintSettingsActivity extends SettingsActivity { /* empty */ }
import android.support.annotation.XmlRes;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.PreferenceGroupAdapter;
import android.support.v7.preference.PreferenceScreen;
-import android.support.v7.preference.PreferenceViewHolder;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import com.android.settings.search.actionbar.SearchMenuController;
import com.android.settings.support.actionbar.HelpMenuController;
import com.android.settings.support.actionbar.HelpResourceProvider;
+import com.android.settings.widget.HighlightablePreferenceGroupAdapter;
import com.android.settings.widget.LoadingViewController;
import com.android.settingslib.CustomDialogPreference;
import com.android.settingslib.CustomEditTextPreference;
private static final String TAG = "SettingsPreference";
- @VisibleForTesting
- static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
-
private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
protected final FooterPreferenceMixin mFooterPreferenceMixin =
private static final int ORDER_FIRST = -1;
- private static final int ORDER_LAST = Integer.MAX_VALUE -1;
private SettingsDialogFragment mDialogFragment;
// Cache the content resolver for async callbacks
private ContentResolver mContentResolver;
- private String mPreferenceKey;
-
private RecyclerView.Adapter mCurrentRootAdapter;
private boolean mIsDataSetObserverRegistered = false;
private RecyclerView.AdapterDataObserver mDataSetObserver =
// Check if we should keep the preferences expanded.
if (arguments != null) {
- mPreferenceKey = arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
- if (!TextUtils.isEmpty(mPreferenceKey)) {
+ final String highlightKey =
+ arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
+ if (!TextUtils.isEmpty(highlightKey)) {
final PreferenceScreen screen = getPreferenceScreen();
if (screen != null) {
screen.setInitialExpandedChildrenCount(Integer.MAX_VALUE);
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ if (mAdapter != null) {
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mAdapter.isHighlightRequested());
+ }
}
@Override
@Override
public void onResume() {
super.onResume();
-
- if (mPreferenceKey != null) {
- highlightPreferenceIfNeeded();
- }
+ highlightPreferenceIfNeeded();
}
@Override
}
public void highlightPreferenceIfNeeded() {
- if (isAdded() && !mPreferenceHighlighted &&!TextUtils.isEmpty(mPreferenceKey)) {
- getView().postDelayed(new Runnable() {
- @Override
- public void run() {
- highlightPreference(mPreferenceKey);
- }
- }, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ if (!isAdded()) {
+ return;
+ }
+ if (mAdapter != null) {
+ mAdapter.requestHighlight(getView(), getListView());
}
}
return mEmptyView;
}
- /**
- * Return a valid ListView position or -1 if none is found
- */
- private int canUseListViewForHighLighting(String key) {
- if (getListView() == null) {
- return -1;
- }
-
- RecyclerView listView = getListView();
- RecyclerView.Adapter adapter = listView.getAdapter();
-
- if (adapter != null && adapter instanceof PreferenceGroupAdapter) {
- return findListPositionFromKey((PreferenceGroupAdapter) adapter, key);
- }
-
- return -1;
- }
-
@Override
public RecyclerView.LayoutManager onCreateLayoutManager() {
mLayoutManager = new LinearLayoutManager(getContext());
@Override
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
- mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen);
+ mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen,
+ getArguments().getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY),
+ mPreferenceHighlighted);
return mAdapter;
}
}
protected void cacheRemoveAllPrefs(PreferenceGroup group) {
- mPreferenceCache = new ArrayMap<String, Preference>();
+ mPreferenceCache = new ArrayMap<>();
final int N = group.getPreferenceCount();
for (int i = 0; i < N; i++) {
Preference p = group.getPreference(i);
return mPreferenceCache != null ? mPreferenceCache.size() : 0;
}
- private void highlightPreference(String key) {
- final int position = canUseListViewForHighLighting(key);
- if (position < 0) {
- return;
- }
-
- mPreferenceHighlighted = true;
- mLayoutManager.scrollToPosition(position);
- mAdapter.highlight(position);
- }
-
- private int findListPositionFromKey(PreferenceGroupAdapter adapter, String key) {
- final int count = adapter.getItemCount();
- for (int n = 0; n < count; n++) {
- final Preference preference = adapter.getItem(n);
- final String preferenceKey = preference.getKey();
- if (preferenceKey != null && preferenceKey.equals(key)) {
- return n;
- }
- }
- return -1;
- }
-
protected boolean removePreference(String key) {
return removePreference(getPreferenceScreen(), key);
}
}
protected boolean hasNextButton() {
- return ((ButtonBarHandler)getActivity()).hasNextButton();
+ return ((ButtonBarHandler) getActivity()).hasNextButton();
}
protected Button getNextButton() {
- return ((ButtonBarHandler)getActivity()).getNextButton();
+ return ((ButtonBarHandler) getActivity()).getNextButton();
}
public void finish() {
} else {
Log.w(TAG,
"Parent isn't SettingsActivity nor PreferenceActivity, thus there's no way to "
- + "launch the given Fragment (name: " + fragmentClass
- + ", requestCode: " + requestCode + ")");
+ + "launch the given Fragment (name: " + fragmentClass
+ + ", requestCode: " + requestCode + ")");
return false;
}
}
- public static class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
-
- @VisibleForTesting(otherwise=VisibleForTesting.NONE)
- int initialHighlightedPosition = -1;
-
- private int mHighlightPosition = -1;
-
- public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
- super(preferenceGroup);
- }
-
- public void highlight(int position) {
- mHighlightPosition = position;
- initialHighlightedPosition = position;
- notifyDataSetChanged();
- }
-
- @Override
- public void onBindViewHolder(PreferenceViewHolder holder, int position) {
- super.onBindViewHolder(holder, position);
- if (position == mHighlightPosition) {
- View v = holder.itemView;
- v.post(() -> {
- if (v.getBackground() != null) {
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- v.getBackground().setHotspot(centerX, centerY);
- }
- v.setPressed(true);
- v.setPressed(false);
- mHighlightPosition = -1;
- });
- }
- }
- }
}
final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
CharSequence summary = null;
-
- // Show up to 3 account types
- final int size = Math.min(3, types.length);
-
- for (int i = 0; i < size; i++) {
- final CharSequence label = authHelper.getLabelForType(mContext, types[i]);
- if (summary == null) {
- summary = bidiFormatter.unicodeWrap(label);
- } else {
- summary = mContext.getString(R.string.join_many_items_middle, summary,
- bidiFormatter.unicodeWrap(label));
+ if (types == null || types.length == 0) {
+ summary = mContext.getString(R.string.account_dashboard_default_summary);
+ } else {
+ // Show up to 3 account types
+ final int size = Math.min(3, types.length);
+
+ for (int i = 0; i < size; i++) {
+ final CharSequence label = authHelper.getLabelForType(mContext, types[i]);
+ if (summary == null) {
+ summary = bidiFormatter.unicodeWrap(label);
+ } else {
+ summary = mContext.getString(R.string.join_many_items_middle, summary,
+ bidiFormatter.unicodeWrap(label));
+ }
}
}
mSummaryLoader.setSummary(this, summary);
mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
mPm = activity.getPackageManager();
- retrieveAppEntry();
- startListeningToPackageRemove();
-
if (!ensurePackageInfoAvailable(activity)) {
return;
}
+ startListeningToPackageRemove();
+
mForceStopOptionsMenuController =
new ForceStopOptionsMenuController(activity, this /* parent */, mDpm,
mMetricsFeatureProvider, getLifecycle());
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+ retrieveAppEntry();
+ if (mPackageInfo == null) {
+ return null;
+ }
final String packageName = getPackageName();
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final Lifecycle lifecycle = getLifecycle();
}
ApplicationsState.AppEntry getAppEntry() {
- if (mAppEntry == null) {
- retrieveAppEntry();
- }
return mAppEntry;
}
}
PackageInfo getPackageInfo() {
- if (mAppEntry == null) {
- retrieveAppEntry();
- }
return mPackageInfo;
}
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
.execute((Object) null);
}
- // continue with following operations
+ if (!refreshUi()) {
+ onPackageRemoved();
+ } else {
+ startListeningToPackageRemove();
+ }
+ break;
case REQUEST_REMOVE_DEVICE_ADMIN:
if (!refreshUi()) {
setIntentAndFinish(true, true);
return mPackageName;
}
- private void retrieveAppEntry() {
+ @VisibleForTesting
+ void retrieveAppEntry() {
final Activity activity = getActivity();
if (activity == null) {
return;
import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController;
import com.android.settings.bluetooth.BluetoothSwitchPreferenceController;
+import com.android.settings.connecteddevice.usb.UsbBackend;
+import com.android.settings.connecteddevice.usb.UsbModePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.deviceinfo.UsbBackend;
import com.android.settings.nfc.NfcPreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.SettingsActivity;
import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController;
import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.usb.UsbBackend;
+import com.android.settings.connecteddevice.usb.UsbModePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader;
-import com.android.settings.deviceinfo.UsbBackend;
import com.android.settings.nfc.NfcPreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen;
+import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.bluetooth.BluetoothDeviceUpdater;
import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) {
super(fragment.getContext());
init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this),
- new ConnectedUsbDeviceUpdater(fragment.getContext(), this));
+ new ConnectedUsbDeviceUpdater(fragment, this));
}
@VisibleForTesting
+++ /dev/null
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.connecteddevice;
-
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.usb.UsbManager;
-
-/**
- * Receiver to receive usb update and use {@link UsbConnectionListener} to invoke callback
- */
-public class UsbConnectionBroadcastReceiver extends BroadcastReceiver {
- private Context mContext;
- private UsbConnectionListener mUsbConnectionListener;
- private boolean mListeningToUsbEvents;
- private boolean mConnected;
-
- public UsbConnectionBroadcastReceiver(Context context,
- UsbConnectionListener usbConnectionListener) {
- mContext = context;
- mUsbConnectionListener = usbConnectionListener;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- mConnected = intent != null
- && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
- if (mUsbConnectionListener != null) {
- mUsbConnectionListener.onUsbConnectionChanged(mConnected);
- }
- }
-
- public void register() {
- if (!mListeningToUsbEvents) {
- final IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_STATE);
- final Intent intent = mContext.registerReceiver(this, intentFilter);
- mConnected = intent != null
- && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
- mListeningToUsbEvents = true;
- }
- }
-
- public void unregister() {
- if (mListeningToUsbEvents) {
- mContext.unregisterReceiver(this);
- mListeningToUsbEvents = false;
- }
- }
-
- public boolean isConnected() {
- return mConnected;
- }
-
- /**
- * Interface definition for a callback to be invoked when usb connection is changed.
- */
- interface UsbConnectionListener {
- void onUsbConnectionChanged(boolean connected);
- }
-}
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
import android.content.Context;
-import android.content.Intent;
+import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.PreferenceFragment;
import com.android.settings.R;
-import com.android.settings.deviceinfo.UsbBackend;
-import com.android.settings.deviceinfo.UsbModeChooserActivity;
+import com.android.settings.SettingsActivity;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.GearPreference;
/**
* Controller to maintain connected usb device
*/
public class ConnectedUsbDeviceUpdater {
- private Context mContext;
+ private PreferenceFragment mFragment;
private UsbBackend mUsbBackend;
private DevicePreferenceCallback mDevicePreferenceCallback;
@VisibleForTesting
@VisibleForTesting
UsbConnectionBroadcastReceiver mUsbReceiver;
- private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
- (connected) -> {
+ @VisibleForTesting
+ UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
+ (connected, newMode) -> {
if (connected) {
mUsbPreference.setSummary(
UsbModePreferenceController.getSummary(mUsbBackend.getCurrentMode()));
}
};
- public ConnectedUsbDeviceUpdater(Context context,
+ public ConnectedUsbDeviceUpdater(DashboardFragment fragment,
DevicePreferenceCallback devicePreferenceCallback) {
- this(context, devicePreferenceCallback, new UsbBackend(context));
+ this(fragment, devicePreferenceCallback, new UsbBackend(fragment.getContext()));
}
@VisibleForTesting
- ConnectedUsbDeviceUpdater(Context context, DevicePreferenceCallback devicePreferenceCallback,
- UsbBackend usbBackend) {
- mContext = context;
+ ConnectedUsbDeviceUpdater(DashboardFragment fragment,
+ DevicePreferenceCallback devicePreferenceCallback, UsbBackend usbBackend) {
+ mFragment = fragment;
mDevicePreferenceCallback = devicePreferenceCallback;
mUsbBackend = usbBackend;
- mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener);
+ mUsbReceiver = new UsbConnectionBroadcastReceiver(fragment.getContext(),
+ mUsbConnectionListener, mUsbBackend);
}
public void registerCallback() {
mUsbPreference.setIcon(R.drawable.ic_usb);
mUsbPreference.setSelectable(false);
mUsbPreference.setOnGearClickListener((GearPreference p) -> {
- final Intent intent = new Intent(mContext, UsbModeChooserActivity.class);
- mContext.startActivity(intent);
+ // New version - uses a separate screen.
+ final Bundle args = new Bundle();
+ final SettingsActivity activity = (SettingsActivity) mFragment.getContext();
+ activity.startPreferencePanel(mFragment,
+ UsbDetailsFragment.class.getName(), args,
+ R.string.device_details_title, null /* titleText */, null /* resultTo */, 0);
});
forceUpdate();
// Register so we can get the connection state from sticky intent.
//TODO(b/70336520): Use an API to get data instead of sticky intent
mUsbReceiver.register();
- mUsbConnectionListener.onUsbConnectionChanged(mUsbReceiver.isConnected());
}
}
--- /dev/null
+# Default reviewers for this and subdirectories.
+zhangjerry@google.com
+badhri@google.com
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.connecteddevice.usb;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.net.ConnectivityManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
public static final int MODE_POWER_SINK = 0x00;
public static final int MODE_POWER_SOURCE = 0x01;
- public static final int MODE_DATA_MASK = 0x03 << 1;
- public static final int MODE_DATA_NONE = 0x00 << 1;
+ public static final int MODE_DATA_MASK = 0x0f << 1;
+ public static final int MODE_DATA_NONE = 0;
public static final int MODE_DATA_MTP = 0x01 << 1;
- public static final int MODE_DATA_PTP = 0x02 << 1;
- public static final int MODE_DATA_MIDI = 0x03 << 1;
+ public static final int MODE_DATA_PTP = 0x01 << 2;
+ public static final int MODE_DATA_MIDI = 0x01 << 3;
+ public static final int MODE_DATA_TETHER = 0x01 << 4;
- private final boolean mRestricted;
- private final boolean mRestrictedBySystem;
- private final boolean mMidi;
+ private final boolean mFileTransferRestricted;
+ private final boolean mFileTransferRestrictedBySystem;
+ private final boolean mTetheringRestricted;
+ private final boolean mTetheringRestrictedBySystem;
+ private final boolean mMidiSupported;
+ private final boolean mTetheringSupported;
private UsbManager mUsbManager;
+ @VisibleForTesting
+ UsbManagerPassThrough mUsbManagerPassThrough;
private UsbPort mPort;
private UsbPortStatus mPortStatus;
private Context mContext;
public UsbBackend(Context context) {
- this(context, new UserRestrictionUtil(context));
+ this(context, new UserRestrictionUtil(context), null);
}
@VisibleForTesting
- public UsbBackend(Context context, UserRestrictionUtil userRestrictionUtil) {
+ public UsbBackend(Context context, UserRestrictionUtil userRestrictionUtil,
+ UsbManagerPassThrough usbManagerPassThrough) {
mContext = context;
mUsbManager = context.getSystemService(UsbManager.class);
- mRestricted = userRestrictionUtil.isUsbFileTransferRestricted();
- mRestrictedBySystem = userRestrictionUtil.isUsbFileTransferRestrictedBySystem();
- mMidi = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
+ mUsbManagerPassThrough = usbManagerPassThrough;
+ if (mUsbManagerPassThrough == null) {
+ mUsbManagerPassThrough = new UsbManagerPassThrough(mUsbManager);
+ }
+
+ mFileTransferRestricted = userRestrictionUtil.isUsbFileTransferRestricted();
+ mFileTransferRestrictedBySystem = userRestrictionUtil.isUsbFileTransferRestrictedBySystem();
+ mTetheringRestricted = userRestrictionUtil.isUsbTetheringRestricted();
+ mTetheringRestrictedBySystem = userRestrictionUtil.isUsbTetheringRestrictedBySystem();
+
+ mMidiSupported = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
+ ConnectivityManager cm =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mTetheringSupported = cm.isTetheringSupported();
UsbPort[] ports = mUsbManager.getPorts();
if (ports == null) {
public int getCurrentMode() {
if (mPort != null) {
int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
+ && mPortStatus.isConnected()
? MODE_POWER_SOURCE : MODE_POWER_SINK;
return power | getUsbDataMode();
}
}
public int getUsbDataMode() {
- if (!isUsbDataUnlocked()) {
- return MODE_DATA_NONE;
- } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) {
+ long functions = mUsbManagerPassThrough.getCurrentFunctions();
+ if (functions == UsbManager.FUNCTION_MTP) {
return MODE_DATA_MTP;
- } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) {
+ } else if (functions == UsbManager.FUNCTION_PTP) {
return MODE_DATA_PTP;
- } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) {
+ } else if (functions == UsbManager.FUNCTION_MIDI) {
return MODE_DATA_MIDI;
+ } else if (functions == UsbManager.FUNCTION_RNDIS) {
+ return MODE_DATA_TETHER;
}
- return MODE_DATA_NONE; // ...
- }
-
- private boolean isUsbDataUnlocked() {
- Intent intent = mContext.registerReceiver(null,
- new IntentFilter(UsbManager.ACTION_USB_STATE));
- return intent == null ?
- false : intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);
+ return MODE_DATA_NONE;
}
private void setUsbFunction(int mode) {
switch (mode) {
case MODE_DATA_MTP:
- mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true);
+ mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_MTP);
break;
case MODE_DATA_PTP:
- mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP, true);
+ mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_PTP);
break;
case MODE_DATA_MIDI:
- mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI, true);
+ mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_MIDI);
+ break;
+ case MODE_DATA_TETHER:
+ mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
break;
default:
- mUsbManager.setCurrentFunction(null, false);
+ mUsbManager.setCurrentFunctions(UsbManager.FUNCTION_NONE);
break;
}
}
}
public boolean isModeDisallowed(int mode) {
- if (mRestricted && (mode & MODE_DATA_MASK) != MODE_DATA_NONE
- && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) {
- // No USB data modes are supported.
+ if (mFileTransferRestricted && ((mode & MODE_DATA_MASK) == MODE_DATA_MTP
+ || (mode & MODE_DATA_MASK) == MODE_DATA_PTP)) {
+ return true;
+ } else if (mTetheringRestricted && ((mode & MODE_DATA_MASK) == MODE_DATA_TETHER)) {
return true;
}
return false;
}
public boolean isModeDisallowedBySystem(int mode) {
- if (mRestrictedBySystem && (mode & MODE_DATA_MASK) != MODE_DATA_NONE
- && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) {
- // No USB data modes are supported.
+ if (mFileTransferRestrictedBySystem && ((mode & MODE_DATA_MASK) == MODE_DATA_MTP
+ || (mode & MODE_DATA_MASK) == MODE_DATA_PTP)) {
+ return true;
+ } else if (mTetheringRestrictedBySystem && ((mode & MODE_DATA_MASK) == MODE_DATA_TETHER)) {
return true;
}
return false;
}
public boolean isModeSupported(int mode) {
- if (!mMidi && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) {
+ if (!mMidiSupported && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) {
return false;
}
-
+ if (!mTetheringSupported && (mode & MODE_DATA_MASK) == MODE_DATA_TETHER) {
+ return false;
+ }
if (mPort != null) {
int power = modeToPower(mode);
if ((mode & MODE_DATA_MASK) != 0) {
return mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
}
+ public boolean isUsbTetheringRestricted() {
+ return mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
+ }
+
public boolean isUsbFileTransferRestrictedBySystem() {
return mUserManager.hasBaseUserRestriction(
UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId()));
}
+
+ public boolean isUsbTetheringRestrictedBySystem() {
+ return mUserManager.hasBaseUserRestriction(
+ UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(UserHandle.myUserId()));
+ }
+ }
+
+ // Temporary pass-through to allow roboelectric to use getCurrentFunctions()
+ public static class UsbManagerPassThrough {
+ private UsbManager mUsbManager;
+
+ public UsbManagerPassThrough(UsbManager manager) {
+ mUsbManager = manager;
+ }
+
+ public long getCurrentFunctions() {
+ return mUsbManager.getCurrentFunctions();
+ }
+
+ public long usbFunctionsFromString(String str) {
+ return UsbManager.usbFunctionsFromString(str);
+ }
}
}
--- /dev/null
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.connecteddevice.usb;
+
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+
+/**
+ * Receiver to receive usb update and use {@link UsbConnectionListener} to invoke callback
+ */
+public class UsbConnectionBroadcastReceiver extends BroadcastReceiver implements LifecycleObserver,
+ OnResume, OnPause {
+ private Context mContext;
+ private UsbConnectionListener mUsbConnectionListener;
+ private boolean mListeningToUsbEvents;
+ private int mMode;
+ private boolean mConnected;
+ private UsbBackend mUsbBackend;
+
+ public UsbConnectionBroadcastReceiver(Context context,
+ UsbConnectionListener usbConnectionListener, UsbBackend backend) {
+ mContext = context;
+ mUsbConnectionListener = usbConnectionListener;
+ mUsbBackend = backend;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+ mConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED)
+ || intent.getExtras().getBoolean(UsbManager.USB_HOST_CONNECTED);
+ if (mConnected) {
+ mMode &= UsbBackend.MODE_POWER_MASK;
+ if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_MTP)
+ && intent.getExtras().getBoolean(UsbManager.USB_DATA_UNLOCKED, false)) {
+ mMode |= UsbBackend.MODE_DATA_MTP;
+ }
+ if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_PTP)
+ && intent.getExtras().getBoolean(UsbManager.USB_DATA_UNLOCKED, false)) {
+ mMode |= UsbBackend.MODE_DATA_PTP;
+ }
+ if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_MIDI)) {
+ mMode |= UsbBackend.MODE_DATA_MIDI;
+ }
+ if (intent.getExtras().getBoolean(UsbManager.USB_FUNCTION_RNDIS)) {
+ mMode |= UsbBackend.MODE_DATA_TETHER;
+ }
+ }
+ } else if (UsbManager.ACTION_USB_PORT_CHANGED.equals(intent.getAction())) {
+ mMode &= UsbBackend.MODE_DATA_MASK;
+ UsbPortStatus portStatus = intent.getExtras()
+ .getParcelable(UsbManager.EXTRA_PORT_STATUS);
+ if (portStatus != null) {
+ mConnected = portStatus.isConnected();
+ if (mConnected) {
+ mMode |= portStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
+ ? UsbBackend.MODE_POWER_SOURCE : UsbBackend.MODE_POWER_SINK;
+ }
+ }
+ }
+ if (mUsbConnectionListener != null) {
+ mUsbConnectionListener.onUsbConnectionChanged(mConnected, mMode);
+ }
+ }
+
+ public void register() {
+ if (!mListeningToUsbEvents) {
+ mMode = mUsbBackend.getCurrentMode();
+ mConnected = false;
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(UsbManager.ACTION_USB_STATE);
+ intentFilter.addAction(UsbManager.ACTION_USB_PORT_CHANGED);
+ mContext.registerReceiver(this, intentFilter);
+ mListeningToUsbEvents = true;
+ }
+ }
+
+ public void unregister() {
+ if (mListeningToUsbEvents) {
+ mContext.unregisterReceiver(this);
+ mListeningToUsbEvents = false;
+ }
+ }
+
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ @Override
+ public void onResume() {
+ register();
+ }
+
+ @Override
+ public void onPause() {
+ unregister();
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when usb connection is changed.
+ */
+ interface UsbConnectionListener {
+ void onUsbConnectionChanged(boolean connected, int newMode);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.usb;
+
+import android.content.Context;
+import android.support.annotation.UiThread;
+import android.support.v14.preference.PreferenceFragment;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This class provides common members and refresh functionality for usb controllers.
+ */
+public abstract class UsbDetailsController extends AbstractPreferenceController
+ implements PreferenceControllerMixin {
+
+ protected final Context mContext;
+ protected final PreferenceFragment mFragment;
+ protected final UsbBackend mUsbBackend;
+
+ public UsbDetailsController(Context context, PreferenceFragment fragment, UsbBackend backend) {
+ super(context);
+ mContext = context;
+ mFragment = fragment;
+ mUsbBackend = backend;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ /**
+ * This method is called when the USB mode has changed and the controller needs to update.
+ * @param newMode the new mode, made up of OR'd values from UsbBackend
+ */
+ @UiThread
+ protected abstract void refresh(int newMode);
+}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.usb;
+
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.provider.SearchIndexableResource;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controls the USB device details and provides updates to individual controllers.
+ */
+public class UsbDetailsFragment extends DashboardFragment {
+ private static final String TAG = UsbDetailsFragment.class.getSimpleName();
+
+ private List<UsbDetailsController> mControllers;
+ private UsbBackend mUsbBackend;
+
+ @VisibleForTesting
+ UsbConnectionBroadcastReceiver mUsbReceiver;
+
+ private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
+ (connected, newMode) -> {
+ if (!connected) {
+ this.finish();
+ } else {
+ for (UsbDetailsController controller : mControllers) {
+ controller.refresh(newMode);
+ }
+ }
+ };
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.USB_DEVICE_DETAILS;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.usb_details_fragment;
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ super.onCreatePreferences(savedInstanceState, rootKey);
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+ mUsbBackend = new UsbBackend(context);
+ mControllers = createControllerList(context, mUsbBackend, this);
+ mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener,
+ mUsbBackend);
+ this.getLifecycle().addObserver(mUsbReceiver);
+
+ List<AbstractPreferenceController> ret = new ArrayList<>();
+ ret.addAll(mControllers);
+ return ret;
+ }
+
+ private static List<UsbDetailsController> createControllerList(Context context,
+ UsbBackend usbBackend, DashboardFragment fragment) {
+ List<UsbDetailsController> ret = new ArrayList<>();
+ ret.add(new UsbDetailsHeaderController(context, fragment, usbBackend));
+ ret.add(new UsbDetailsProfilesController(context, fragment,
+ usbBackend, Lists.newArrayList(UsbManager.USB_FUNCTION_MTP), "usb_main_options"));
+ ret.add(new UsbDetailsProfilesController(context, fragment,
+ usbBackend, Lists.newArrayList(UsbDetailsProfilesController.KEY_POWER,
+ UsbManager.USB_FUNCTION_RNDIS, UsbManager.USB_FUNCTION_MIDI,
+ UsbManager.USB_FUNCTION_PTP), "usb_secondary_options"));
+ return ret;
+ }
+
+ /**
+ * For Search.
+ */
+ public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex(
+ Context context, boolean enabled) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List<String> getNonIndexableKeys(Context context) {
+ return super.getNonIndexableKeys(context);
+ }
+
+ @Override
+ public List<AbstractPreferenceController> getPreferenceControllers(
+ Context context) {
+ List<AbstractPreferenceController> ret = new ArrayList<>();
+ ret.addAll(createControllerList(context, new UsbBackend(context), null));
+ return ret;
+ }
+ };
+}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.usb;
+
+import android.content.Context;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.widget.EntityHeaderController;
+
+/**
+ * This class adds a header with device name and current function.
+ */
+public class UsbDetailsHeaderController extends UsbDetailsController {
+ private static final String KEY_DEVICE_HEADER = "usb_device_header";
+
+ private EntityHeaderController mHeaderController;
+
+ public UsbDetailsHeaderController(Context context, PreferenceFragment fragment,
+ UsbBackend backend) {
+ super(context, fragment, backend);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final LayoutPreference headerPreference =
+ (LayoutPreference) screen.findPreference(KEY_DEVICE_HEADER);
+ mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
+ headerPreference.findViewById(R.id.entity_header));
+ screen.addPreference(headerPreference);
+ }
+
+
+ @Override
+ protected void refresh(int newMode) {
+ mHeaderController.setLabel(mContext.getString(R.string.usb_pref));
+ mHeaderController.setIcon(mContext.getDrawable(R.drawable.ic_usb));
+ mHeaderController.setSummary(
+ mContext.getString(UsbModePreferenceController.getSummary(newMode)));
+ mHeaderController.done(mFragment.getActivity(), true /* rebindActions */);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_DEVICE_HEADER;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.usb;
+
+import com.android.settings.R;
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceScreen;
+
+import java.util.List;
+
+/**
+ * This class adds switches for toggling individual USB options, such as "transfer files",
+ * "supply power", "usb tethering", etc.
+ */
+public class UsbDetailsProfilesController extends UsbDetailsController
+ implements Preference.OnPreferenceClickListener {
+
+ static final String KEY_POWER = "power";
+
+ private PreferenceCategory mProfilesContainer;
+ private List<String> mOptions;
+ private String mKey;
+
+ public UsbDetailsProfilesController(Context context, PreferenceFragment fragment,
+ UsbBackend backend, List<String> options, String key) {
+ super(context, fragment, backend);
+ mOptions = options;
+ mKey = key;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mProfilesContainer = (PreferenceCategory) screen.findPreference(getPreferenceKey());
+ }
+
+ /**
+ * Gets a switch preference for the particular option, creating it if needed.
+ */
+ private SwitchPreference getProfilePreference(String key, int titleId) {
+ SwitchPreference pref = (SwitchPreference) mProfilesContainer.findPreference(key);
+ if (pref == null) {
+ pref = new SwitchPreference(mProfilesContainer.getContext());
+ pref.setKey(key);
+ pref.setTitle(titleId);
+ pref.setOnPreferenceClickListener(this);
+ mProfilesContainer.addPreference(pref);
+ }
+ return pref;
+ }
+
+ @Override
+ protected void refresh(int mode) {
+ SwitchPreference pref;
+ for (String option : mOptions) {
+ int newMode;
+ int summary = -1;
+ int title;
+ if (option.equals(UsbManager.USB_FUNCTION_MTP)) {
+ newMode = UsbBackend.MODE_DATA_MTP;
+ title = R.string.usb_use_file_transfers;
+ } else if (option.equals(KEY_POWER)) {
+ newMode = UsbBackend.MODE_POWER_SOURCE;
+ title = R.string.usb_use_power_only;
+ summary = R.string.usb_use_power_only_desc;
+ } else if (option.equals(UsbManager.USB_FUNCTION_PTP)) {
+ newMode = UsbBackend.MODE_DATA_PTP;
+ title = R.string.usb_use_photo_transfers;
+ } else if (option.equals(UsbManager.USB_FUNCTION_MIDI)) {
+ newMode = UsbBackend.MODE_DATA_MIDI;
+ title = R.string.usb_use_MIDI;
+ } else if (option.equals(UsbManager.USB_FUNCTION_RNDIS)) {
+ newMode = UsbBackend.MODE_DATA_TETHER;
+ title = R.string.usb_use_tethering;
+ } else {
+ continue;
+ }
+
+ pref = getProfilePreference(option, title);
+ // Only show supported and allowed options
+ if (mUsbBackend.isModeSupported(newMode)
+ && !mUsbBackend.isModeDisallowedBySystem(newMode)
+ && !mUsbBackend.isModeDisallowed(newMode)) {
+ if (summary != -1) {
+ pref.setSummary(summary);
+ }
+ pref.setChecked((mode & newMode) != 0);
+ } else {
+ mProfilesContainer.removePreference(pref);
+ }
+ }
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ SwitchPreference profilePref = (SwitchPreference) preference;
+ String key = profilePref.getKey();
+ int mode = mUsbBackend.getCurrentMode();
+ int thisMode = 0;
+ if (key.equals(KEY_POWER)) {
+ thisMode = UsbBackend.MODE_POWER_SOURCE;
+ } else if (key.equals(UsbManager.USB_FUNCTION_MTP)) {
+ thisMode = UsbBackend.MODE_DATA_MTP;
+ } else if (key.equals(UsbManager.USB_FUNCTION_PTP)) {
+ thisMode = UsbBackend.MODE_DATA_PTP;
+ } else if (key.equals(UsbManager.USB_FUNCTION_RNDIS)) {
+ thisMode = UsbBackend.MODE_DATA_TETHER;
+ } else if (key.equals(UsbManager.USB_FUNCTION_MIDI)) {
+ thisMode = UsbBackend.MODE_DATA_MIDI;
+ }
+ if (profilePref.isChecked()) {
+ if (!key.equals(KEY_POWER)) {
+ // Only one non power mode can currently be set at once.
+ mode &= UsbBackend.MODE_POWER_MASK;
+ }
+ mode |= thisMode;
+ } else {
+ mode &= ~thisMode;
+ }
+ mUsbBackend.setMode(mode);
+ return false;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return mKey;
+ }
+}
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.connecteddevice.usb;
import android.annotation.Nullable;
import android.app.Activity;
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
import android.content.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.deviceinfo.UsbBackend;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
private static final String KEY_USB_MODE = "usb_mode";
private UsbBackend mUsbBackend;
- private UsbConnectionBroadcastReceiver mUsbReceiver;
+ @VisibleForTesting
+ UsbConnectionBroadcastReceiver mUsbReceiver;
private Preference mUsbPreference;
public UsbModePreferenceController(Context context, UsbBackend usbBackend) {
super(context);
mUsbBackend = usbBackend;
- mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected) -> {
- updateSummary(mUsbPreference);
- });
+ mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected, newMode) -> {
+ updateSummary(mUsbPreference, connected, newMode);
+ }, mUsbBackend);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mUsbPreference = screen.findPreference(KEY_USB_MODE);
- updateSummary(mUsbPreference);
}
@Override
public void updateState(Preference preference) {
- updateSummary(preference);
+ updateSummary(preference, mUsbReceiver.isConnected(), mUsbBackend.getCurrentMode());
}
@Override
return R.string.usb_summary_photo_transfers;
case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI:
return R.string.usb_summary_MIDI;
+ case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_TETHER:
+ return R.string.usb_summary_tether;
+ case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_MTP:
+ return R.string.usb_summary_file_transfers_power;
+ case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_PTP:
+ return R.string.usb_summary_photo_transfers_power;
+ case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_MIDI:
+ return R.string.usb_summary_MIDI_power;
+ case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_TETHER:
+ return R.string.usb_summary_tether_power;
+ default:
+ return R.string.usb_summary_charging_only;
}
- return 0;
- }
-
- private void updateSummary(Preference preference) {
- updateSummary(preference, mUsbBackend.getCurrentMode());
}
- private void updateSummary(Preference preference, int mode) {
+ private void updateSummary(Preference preference, boolean connected, int mode) {
if (preference != null) {
- if (mUsbReceiver.isConnected()) {
+ if (connected) {
preference.setEnabled(true);
preference.setSummary(getSummary(mode));
} else {
}
}
}
-
}
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
public static final String ZONE_PICKER_V2 = "settings_zone_picker_v2";
- public static final String SUGGESTION_UI_V2 = "settings_suggestion_ui_v2";
public static final String ABOUT_PHONE_V2 = "settings_about_phone_v2";
public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving";
}
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
+import com.android.settings.connecteddevice.usb.UsbDetailsFragment;
import com.android.settings.datausage.DataPlanUsageSummary;
import com.android.settings.datausage.DataUsageList;
import com.android.settings.datausage.DataUsageSummary;
NetworkDashboardFragment.class.getName(),
ConnectedDeviceDashboardFragment.class.getName(),
ConnectedDeviceDashboardFragmentOld.class.getName(),
+ UsbDetailsFragment.class.getName(),
AppAndNotificationDashboardFragment.class.getName(),
AccountDashboardFragment.class.getName(),
EnterprisePrivacySettings.class.getName(),
import android.app.Activity;
import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.R.id;
-import com.android.settings.dashboard.DashboardData.SuggestionConditionHeaderData;
+import com.android.settings.dashboard.DashboardData.ConditionHeaderData;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settings.dashboard.conditional.ConditionAdapter;
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
-import com.android.settings.dashboard.suggestions.SuggestionDismissController;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.Utils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionControllerMixin;
-import java.util.ArrayList;
import java.util.List;
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
- implements SummaryLoader.SummaryConsumer {
+ implements SummaryLoader.SummaryConsumer, SuggestionAdapter.Callback, LifecycleObserver,
+ OnSaveInstanceState {
public static final String TAG = "DashboardAdapter";
- private static final String STATE_SUGGESTION_LIST = "suggestion_list";
private static final String STATE_CATEGORY_LIST = "category_list";
- private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
@VisibleForTesting
- static final String STATE_SUGGESTION_CONDITION_MODE = "suggestion_condition_mode";
- @VisibleForTesting
- static final int SUGGESTION_CONDITION_HEADER_POSITION = 0;
+ static final String STATE_CONDITION_EXPANDED = "condition_expanded";
private final IconCache mCache;
private final Context mContext;
- private final SuggestionControllerMixin mSuggestionControllerMixin;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final DashboardFeatureProvider mDashboardFeatureProvider;
- private final ArrayList<String> mSuggestionsShownLogged;
private boolean mFirstFrameDrawn;
private RecyclerView mRecyclerView;
private SuggestionAdapter mSuggestionAdapter;
- private SuggestionDismissController mSuggestionDismissHandler;
- private SuggestionDismissController.Callback mCallback;
@VisibleForTesting
DashboardData mDashboardData;
};
public DashboardAdapter(Context context, Bundle savedInstanceState,
- List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
- SuggestionDismissController.Callback callback) {
+ List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
+ Lifecycle lifecycle) {
- List<Suggestion> suggestions = null;
DashboardCategory category = null;
- int suggestionConditionMode = DashboardData.HEADER_MODE_DEFAULT;
+ boolean conditionExpanded = false;
mContext = context;
final FeatureFactory factory = FeatureFactory.getFactory(context);
- mSuggestionControllerMixin = suggestionControllerMixin;
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
mCache = new IconCache(context);
- mCallback = callback;
+ mSuggestionAdapter = new SuggestionAdapter(mContext, suggestionControllerMixin,
+ savedInstanceState, this /* callback */, lifecycle);
setHasStableIds(true);
if (savedInstanceState != null) {
- suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
- suggestionConditionMode = savedInstanceState.getInt(
- STATE_SUGGESTION_CONDITION_MODE, suggestionConditionMode);
- mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
- STATE_SUGGESTIONS_SHOWN_LOGGED);
- } else {
- mSuggestionsShownLogged = new ArrayList<>();
+ conditionExpanded = savedInstanceState.getBoolean(
+ STATE_CONDITION_EXPANDED, conditionExpanded);
+ }
+
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
}
mDashboardData = new DashboardData.Builder()
- .setConditions(conditions)
- .setSuggestions(suggestions)
- .setCategory(category)
- .setSuggestionConditionMode(suggestionConditionMode)
- .build();
+ .setConditions(conditions)
+ .setSuggestions(mSuggestionAdapter.getSuggestions())
+ .setCategory(category)
+ .setConditionExpanded(conditionExpanded)
+ .build();
}
public void setSuggestions(List<Suggestion> data) {
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
- .setSuggestions(data)
- .build();
+ .setSuggestions(data)
+ .build();
notifyDashboardDataChanged(prevData);
}
public void setCategory(DashboardCategory category) {
- tintIcons(category, null);
final DashboardData prevData = mDashboardData;
Log.d(TAG, "adapter setCategory called");
mDashboardData = new DashboardData.Builder(prevData)
- .setCategory(category)
- .build();
+ .setCategory(category)
+ .build();
notifyDashboardDataChanged(prevData);
}
final DashboardData prevData = mDashboardData;
Log.d(TAG, "adapter setConditions called");
mDashboardData = new DashboardData.Builder(prevData)
- .setConditions(conditions)
- .build();
+ .setConditions(conditions)
+ .build();
notifyDashboardDataChanged(prevData);
}
- public void onSuggestionDismissed(Suggestion suggestion) {
+ @Override
+ public void onSuggestionClosed(Suggestion suggestion) {
final List<Suggestion> list = mDashboardData.getSuggestions();
if (list == null || list.size() == 0) {
return;
if (list.size() == 1) {
// The only suggestion is dismissed, and the the empty suggestion container will
// remain as the dashboard item. Need to refresh the dashboard list.
- final DashboardData prevData = mDashboardData;
- mDashboardData = new DashboardData.Builder(prevData)
- .setSuggestions(null)
- .build();
- notifyDashboardDataChanged(prevData);
+ setSuggestions(null);
} else {
mSuggestionAdapter.removeSuggestion(suggestion);
+ notifyItemChanged(0, null);
}
}
@Override
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
- if (viewType == R.layout.suggestion_condition_header) {
- return new SuggestionAndConditionHeaderHolder(view);
+ if (viewType == R.layout.condition_header) {
+ return new ConditionHeaderHolder(view);
+ }
+ if (viewType == R.layout.condition_container) {
+ return new ConditionContainerHolder(view);
}
- if (viewType == R.layout.suggestion_condition_container) {
- return new SuggestionAndConditionContainerHolder(view);
+ if (viewType == R.layout.suggestion_container) {
+ return new SuggestionContainerHolder(view);
}
return new DashboardItemHolder(view);
}
holder.itemView.setTag(tile);
holder.itemView.setOnClickListener(mTileClickListener);
break;
- case R.layout.suggestion_condition_container:
- onBindConditionAndSuggestion(
- (SuggestionAndConditionContainerHolder) holder, position);
+ case R.layout.suggestion_container:
+ onBindSuggestion((SuggestionContainerHolder) holder, position);
break;
- case R.layout.suggestion_condition_header:
- onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,
- (SuggestionConditionHeaderData)
- mDashboardData.getItemEntityByPosition(position));
+ case R.layout.condition_container:
+ onBindCondition((ConditionContainerHolder) holder, position);
break;
- case R.layout.suggestion_condition_footer:
+ case R.layout.condition_header:
+ onBindConditionHeader((ConditionHeaderHolder) holder,
+ (ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
+ break;
+ case R.layout.condition_footer:
holder.itemView.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
+ MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
DashboardData prevData = mDashboardData;
- mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(
- DashboardData.HEADER_MODE_COLLAPSED).build();
+ mDashboardData = new DashboardData.Builder(prevData).
+ setConditionExpanded(false).build();
notifyDashboardDataChanged(prevData);
- mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
+ scrollToTopOfConditions();
});
break;
}
void notifyDashboardDataChanged(DashboardData prevData) {
if (mFirstFrameDrawn && prevData != null) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
- .ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
+ .ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
diffResult.dispatchUpdatesTo(this);
} else {
mFirstFrameDrawn = true;
}
@VisibleForTesting
- void onBindSuggestionConditionHeader(final SuggestionAndConditionHeaderHolder holder,
- SuggestionConditionHeaderData data) {
- final int curMode = mDashboardData.getSuggestionConditionMode();
- final int nextMode = data.hiddenSuggestionCount > 0
- && data.conditionCount > 0
- && curMode != DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
- ? DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
- : DashboardData.HEADER_MODE_FULLY_EXPANDED;
-
- final boolean hasConditions = data.conditionCount > 0;
- if (data.conditionCount > 0) {
- holder.icon.setImageIcon(data.conditionIcons.get(0));
- holder.icon.setVisibility(View.VISIBLE);
- if (data.conditionCount == 1) {
- holder.title.setText(data.title);
- holder.title.setTextColor(Utils.getColorAccent(mContext));
- holder.icons.setVisibility(View.INVISIBLE);
- } else {
- holder.title.setText(null);
- updateConditionIcons(data.conditionIcons, holder.icons);
- holder.icons.setVisibility(View.VISIBLE);
- }
- } else {
- holder.icon.setVisibility(View.INVISIBLE);
+ void onBindConditionHeader(final ConditionHeaderHolder holder, ConditionHeaderData data) {
+ holder.icon.setImageIcon(data.conditionIcons.get(0));
+ if (data.conditionCount == 1) {
+ holder.title.setText(data.title);
+ holder.summary.setText(null);
holder.icons.setVisibility(View.INVISIBLE);
- }
-
- if (data.hiddenSuggestionCount > 0) {
- holder.summary.setTextColor(Color.BLACK);
- if (curMode == DashboardData.HEADER_MODE_COLLAPSED) {
- if (data.conditionCount > 0) {
- holder.summary.setText(mContext.getResources().getQuantityString(
- R.plurals.suggestions_collapsed_summary,
- data.hiddenSuggestionCount, data.hiddenSuggestionCount));
- } else {
- holder.title.setText(mContext.getResources().getQuantityString(
- R.plurals.suggestions_collapsed_title,
- data.hiddenSuggestionCount, data.hiddenSuggestionCount));
- holder.title.setTextColor(Color.BLACK);
- holder.summary.setText(null);
- }
- } else if (curMode == DashboardData.HEADER_MODE_DEFAULT) {
- if (data.conditionCount > 0) {
- holder.summary.setText(mContext.getString(
- R.string.suggestions_summary, data.hiddenSuggestionCount));
- } else {
- holder.title.setText(mContext.getString(
- R.string.suggestions_more_title, data.hiddenSuggestionCount));
- holder.title.setTextColor(Color.BLACK);
- holder.summary.setText(null);
- }
- }
- } else if (data.conditionCount > 1) {
- holder.summary.setTextColor(Utils.getColorAccent(mContext));
- holder.summary.setText(
- mContext.getString(R.string.condition_summary, data.conditionCount));
} else {
- holder.summary.setText(null);
+ holder.title.setText(null);
+ holder.summary.setText(
+ mContext.getString(R.string.condition_summary, data.conditionCount));
+ updateConditionIcons(data.conditionIcons, holder.icons);
+ holder.icons.setVisibility(View.VISIBLE);
}
- final Resources res = mContext.getResources();
- final int padding = res.getDimensionPixelOffset(
- curMode == DashboardData.HEADER_MODE_COLLAPSED
- ? R.dimen.suggestion_condition_header_padding_collapsed
- : R.dimen.suggestion_condition_header_padding_expanded);
- holder.itemView.setPadding(0, padding, 0, padding);
-
holder.itemView.setOnClickListener(v -> {
- if (hasConditions) {
- mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
- }
- DashboardData prevData = mDashboardData;
- final boolean wasCollapsed = curMode == DashboardData.HEADER_MODE_COLLAPSED;
+ mMetricsFeatureProvider.action(mContext,
+ MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
+ final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
- .setSuggestionConditionMode(nextMode).build();
+ .setConditionExpanded(true).build();
notifyDashboardDataChanged(prevData);
- if (wasCollapsed) {
- mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
- }
+ scrollToTopOfConditions();
});
}
@VisibleForTesting
- void onBindConditionAndSuggestion(final SuggestionAndConditionContainerHolder holder,
- int position) {
+ void onBindCondition(final ConditionContainerHolder holder, int position) {
+ final ConditionAdapter adapter = new ConditionAdapter(mContext,
+ (List<Condition>) mDashboardData.getItemEntityByPosition(position),
+ mDashboardData.isConditionExpanded());
+ adapter.addDismissHandling(holder.data);
+ holder.data.setAdapter(adapter);
+ holder.data.setLayoutManager(new LinearLayoutManager(mContext));
+ }
+
+ @VisibleForTesting
+ void onBindSuggestion(final SuggestionContainerHolder holder, int position) {
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
// header anymore.
- final List<Suggestion> suggestions = mDashboardData.getSuggestions();
-
- boolean conditionOnly = true;
- if (position == SUGGESTION_CONDITION_HEADER_POSITION) {
- if (suggestions != null && suggestions.size() > 0) {
- conditionOnly = false;
- mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
- (List<Suggestion>) mDashboardData.getItemEntityByPosition(position),
- mSuggestionsShownLogged);
- mSuggestionDismissHandler = new SuggestionDismissController(mContext,
- holder.data, mSuggestionControllerMixin, mCallback);
- holder.data.setAdapter(mSuggestionAdapter);
- }
- }
- if (conditionOnly) {
- ConditionAdapter adapter = new ConditionAdapter(mContext,
- (List<Condition>) mDashboardData.getItemEntityByPosition(position),
- mDashboardData.getSuggestionConditionMode());
- adapter.addDismissHandling(holder.data);
- holder.data.setAdapter(adapter);
+ final List<Suggestion> suggestions =
+ (List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
+ final int suggestionCount = suggestions.size();
+ if (suggestions != null && suggestionCount > 0) {
+ holder.summary.setText("" + suggestionCount);
+ mSuggestionAdapter.setSuggestions(suggestions);
+ holder.data.setAdapter(mSuggestionAdapter);
}
- holder.data.setLayoutManager(new LinearLayoutManager(mContext));
+ final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
+ layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
+ holder.data.setLayoutManager(layoutManager);
}
- private void onBindTile(DashboardItemHolder holder, Tile tile) {
- holder.icon.setImageDrawable(mCache.getIcon(tile.icon));
+ @VisibleForTesting
+ void onBindTile(DashboardItemHolder holder, Tile tile) {
+ Drawable icon = mCache.getIcon(tile.icon);
+ if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())) {
+ icon = new RoundedHomepageIcon(mContext, icon);
+ mCache.updateIcon(tile.icon, icon);
+ }
+ holder.icon.setImageDrawable(icon);
holder.title.setText(tile.title);
if (!TextUtils.isEmpty(tile.summary)) {
holder.summary.setText(tile.summary);
}
}
- private void tintIcons(DashboardCategory category, List<Tile> suggestions) {
- if (!mDashboardFeatureProvider.shouldTintIcon()) {
- return;
- }
- // TODO: Better place for tinting?
- final TypedArray a = mContext.obtainStyledAttributes(new int[]{
- android.R.attr.colorControlNormal});
- final int tintColor = a.getColor(0, mContext.getColor(R.color.fallback_tintColor));
- a.recycle();
- if (category != null) {
- for (Tile tile : category.getTiles()) {
- if (tile.isIconTintable) {
- // If this drawable is tintable, tint it to match the color.
- tile.icon.setTint(tintColor);
- }
- }
- }
- if (suggestions != null) {
- for (Tile suggestion : suggestions) {
- if (suggestion.isIconTintable) {
- suggestion.icon.setTint(tintColor);
- }
- }
- }
- }
-
- void onSaveInstanceState(Bundle outState) {
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
final DashboardCategory category = mDashboardData.getCategory();
- final List<Suggestion> suggestions = mDashboardData.getSuggestions();
- if (suggestions != null) {
- outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
- new ArrayList<>(suggestions));
- }
if (category != null) {
outState.putParcelable(STATE_CATEGORY_LIST, category);
}
- outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
- outState.putInt(STATE_SUGGESTION_CONDITION_MODE,
- mDashboardData.getSuggestionConditionMode());
+ outState.putBoolean(STATE_CONDITION_EXPANDED, mDashboardData.isConditionExpanded());
}
private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
parent.setVisibility(View.VISIBLE);
}
+ private void scrollToTopOfConditions() {
+ mRecyclerView.scrollToPosition(mDashboardData.hasSuggestion() ? 1 : 0);
+ }
+
public static class IconCache {
private final Context mContext;
private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
Drawable drawable = mMap.get(icon);
if (drawable == null) {
drawable = icon.loadDrawable(mContext);
- mMap.put(icon, drawable);
+ updateIcon(icon, drawable);
}
return drawable;
}
+
+ public void updateIcon(Icon icon, Drawable drawable) {
+ mMap.put(icon, drawable);
+ }
}
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
}
}
- public static class SuggestionAndConditionHeaderHolder extends DashboardItemHolder {
+ public static class ConditionHeaderHolder extends DashboardItemHolder {
public final LinearLayout icons;
public final ImageView expandIndicator;
- public SuggestionAndConditionHeaderHolder(View itemView) {
+ public ConditionHeaderHolder(View itemView) {
super(itemView);
icons = itemView.findViewById(id.additional_icons);
expandIndicator = itemView.findViewById(id.expand_indicator);
}
}
- public static class SuggestionAndConditionContainerHolder extends DashboardItemHolder {
+ public static class ConditionContainerHolder extends DashboardItemHolder {
public final RecyclerView data;
- public SuggestionAndConditionContainerHolder(View itemView) {
+ public ConditionContainerHolder(View itemView) {
super(itemView);
data = itemView.findViewById(id.data);
}
}
+ public static class SuggestionContainerHolder extends DashboardItemHolder {
+ public final RecyclerView data;
+
+ public SuggestionContainerHolder(View itemView) {
+ super(itemView);
+ data = itemView.findViewById(id.suggestion_list);
+ }
+ }
+
}
+++ /dev/null
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.dashboard;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.service.settings.suggestions.Suggestion;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
-import com.android.settings.R.id;
-import com.android.settings.dashboard.DashboardDataV2.ConditionHeaderData;
-import com.android.settings.dashboard.conditional.Condition;
-import com.android.settings.dashboard.conditional.ConditionAdapterV2;
-import com.android.settings.dashboard.suggestions.SuggestionAdapterV2;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
-import com.android.settingslib.drawer.DashboardCategory;
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.suggestions.SuggestionControllerMixin;
-
-import java.util.List;
-
-public class DashboardAdapterV2 extends RecyclerView.Adapter<DashboardAdapterV2.DashboardItemHolder>
- implements SummaryLoader.SummaryConsumer, SuggestionAdapterV2.Callback, LifecycleObserver,
- OnSaveInstanceState {
- public static final String TAG = "DashboardAdapterV2";
- private static final String STATE_CATEGORY_LIST = "category_list";
-
- @VisibleForTesting
- static final String STATE_CONDITION_EXPANDED = "condition_expanded";
-
- private final IconCache mCache;
- private final Context mContext;
- private final MetricsFeatureProvider mMetricsFeatureProvider;
- private final DashboardFeatureProvider mDashboardFeatureProvider;
- private boolean mFirstFrameDrawn;
- private RecyclerView mRecyclerView;
- private SuggestionAdapterV2 mSuggestionAdapter;
-
- @VisibleForTesting
- DashboardDataV2 mDashboardData;
-
- private View.OnClickListener mTileClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- //TODO: get rid of setTag/getTag
- mDashboardFeatureProvider.openTileIntent((Activity) mContext, (Tile) v.getTag());
- }
- };
-
- public DashboardAdapterV2(Context context, Bundle savedInstanceState,
- List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
- Lifecycle lifecycle) {
-
- DashboardCategory category = null;
- boolean conditionExpanded = false;
-
- mContext = context;
- final FeatureFactory factory = FeatureFactory.getFactory(context);
- mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
- mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
- mCache = new IconCache(context);
- mSuggestionAdapter = new SuggestionAdapterV2(mContext, suggestionControllerMixin,
- savedInstanceState, this /* callback */, lifecycle);
-
- setHasStableIds(true);
-
- if (savedInstanceState != null) {
- category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
- conditionExpanded = savedInstanceState.getBoolean(
- STATE_CONDITION_EXPANDED, conditionExpanded);
- }
-
- if (lifecycle != null) {
- lifecycle.addObserver(this);
- }
-
- mDashboardData = new DashboardDataV2.Builder()
- .setConditions(conditions)
- .setSuggestions(mSuggestionAdapter.getSuggestions())
- .setCategory(category)
- .setConditionExpanded(conditionExpanded)
- .build();
- }
-
- public void setSuggestions(List<Suggestion> data) {
- final DashboardDataV2 prevData = mDashboardData;
- mDashboardData = new DashboardDataV2.Builder(prevData)
- .setSuggestions(data)
- .build();
- notifyDashboardDataChanged(prevData);
- }
-
- public void setCategory(DashboardCategory category) {
- final DashboardDataV2 prevData = mDashboardData;
- Log.d(TAG, "adapter setCategory called");
- mDashboardData = new DashboardDataV2.Builder(prevData)
- .setCategory(category)
- .build();
- notifyDashboardDataChanged(prevData);
- }
-
- public void setConditions(List<Condition> conditions) {
- final DashboardDataV2 prevData = mDashboardData;
- Log.d(TAG, "adapter setConditions called");
- mDashboardData = new DashboardDataV2.Builder(prevData)
- .setConditions(conditions)
- .build();
- notifyDashboardDataChanged(prevData);
- }
-
- @Override
- public void onSuggestionClosed(Suggestion suggestion) {
- final List<Suggestion> list = mDashboardData.getSuggestions();
- if (list == null || list.size() == 0) {
- return;
- }
- if (list.size() == 1) {
- // The only suggestion is dismissed, and the the empty suggestion container will
- // remain as the dashboard item. Need to refresh the dashboard list.
- setSuggestions(null);
- } else {
- mSuggestionAdapter.removeSuggestion(suggestion);
- notifyItemChanged(0, null);
- }
- }
-
- @Override
- public void notifySummaryChanged(Tile tile) {
- final int position = mDashboardData.getPositionByTile(tile);
- if (position != DashboardDataV2.POSITION_NOT_FOUND) {
- // Since usually tile in parameter and tile in mCategories are same instance,
- // which is hard to be detected by DiffUtil, so we notifyItemChanged directly.
- notifyItemChanged(position, mDashboardData.getItemTypeByPosition(position));
- }
- }
-
- @Override
- public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
- if (viewType == R.layout.suggestion_condition_header) {
- return new ConditionHeaderHolder(view);
- }
- if (viewType == R.layout.condition_container) {
- return new ConditionContainerHolder(view);
- }
- if (viewType == R.layout.suggestion_container) {
- return new SuggestionContainerHolder(view);
- }
- return new DashboardItemHolder(view);
- }
-
- @Override
- public void onBindViewHolder(DashboardItemHolder holder, int position) {
- final int type = mDashboardData.getItemTypeByPosition(position);
- switch (type) {
- case R.layout.dashboard_tile:
- final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
- onBindTile(holder, tile);
- holder.itemView.setTag(tile);
- holder.itemView.setOnClickListener(mTileClickListener);
- break;
- case R.layout.suggestion_container:
- onBindSuggestion((SuggestionContainerHolder) holder, position);
- break;
- case R.layout.condition_container:
- onBindCondition((ConditionContainerHolder) holder, position);
- break;
- case R.layout.suggestion_condition_header:
- onBindConditionHeader((ConditionHeaderHolder) holder,
- (ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
- break;
- case R.layout.suggestion_condition_footer:
- holder.itemView.setOnClickListener(v -> {
- mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
- DashboardDataV2 prevData = mDashboardData;
- mDashboardData = new DashboardDataV2.Builder(prevData).
- setConditionExpanded(false).build();
- notifyDashboardDataChanged(prevData);
- scrollToTopOfConditions();
- });
- break;
- }
- }
-
- @Override
- public long getItemId(int position) {
- return mDashboardData.getItemIdByPosition(position);
- }
-
- @Override
- public int getItemViewType(int position) {
- return mDashboardData.getItemTypeByPosition(position);
- }
-
- @Override
- public int getItemCount() {
- return mDashboardData.size();
- }
-
- @Override
- public void onAttachedToRecyclerView(RecyclerView recyclerView) {
- super.onAttachedToRecyclerView(recyclerView);
- // save the view so that we can scroll it when expanding/collapsing the suggestion and
- // conditions.
- mRecyclerView = recyclerView;
- }
-
- public Object getItem(long itemId) {
- return mDashboardData.getItemEntityById(itemId);
- }
-
- public Suggestion getSuggestion(int position) {
- return mSuggestionAdapter.getSuggestion(position);
- }
-
- @VisibleForTesting
- void notifyDashboardDataChanged(DashboardDataV2 prevData) {
- if (mFirstFrameDrawn && prevData != null) {
- final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardDataV2
- .ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
- diffResult.dispatchUpdatesTo(this);
- } else {
- mFirstFrameDrawn = true;
- notifyDataSetChanged();
- }
- }
-
- @VisibleForTesting
- void onBindConditionHeader(final ConditionHeaderHolder holder, ConditionHeaderData data) {
- holder.icon.setImageIcon(data.conditionIcons.get(0));
- if (data.conditionCount == 1) {
- holder.title.setText(data.title);
- holder.summary.setText(null);
- holder.icons.setVisibility(View.INVISIBLE);
- } else {
- holder.title.setText(null);
- holder.summary.setText(
- mContext.getString(R.string.condition_summary, data.conditionCount));
- updateConditionIcons(data.conditionIcons, holder.icons);
- holder.icons.setVisibility(View.VISIBLE);
- }
-
- holder.itemView.setOnClickListener(v -> {
- mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
- final DashboardDataV2 prevData = mDashboardData;
- mDashboardData = new DashboardDataV2.Builder(prevData)
- .setConditionExpanded(true).build();
- notifyDashboardDataChanged(prevData);
- scrollToTopOfConditions();
- });
- }
-
- @VisibleForTesting
- void onBindCondition(final ConditionContainerHolder holder, int position) {
- final ConditionAdapterV2 adapter = new ConditionAdapterV2(mContext,
- (List<Condition>) mDashboardData.getItemEntityByPosition(position),
- mDashboardData.isConditionExpanded());
- adapter.addDismissHandling(holder.data);
- holder.data.setAdapter(adapter);
- holder.data.setLayoutManager(new LinearLayoutManager(mContext));
- }
-
- @VisibleForTesting
- void onBindSuggestion(final SuggestionContainerHolder holder, int position) {
- // If there is suggestions to show, it will be at position 0 as we don't show the suggestion
- // header anymore.
- final List<Suggestion> suggestions =
- (List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
- final int suggestionCount = suggestions.size();
- if (suggestions != null && suggestionCount > 0) {
- holder.summary.setText("" + suggestionCount);
- mSuggestionAdapter.setSuggestions(suggestions);
- holder.data.setAdapter(mSuggestionAdapter);
- }
- final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
- layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
- holder.data.setLayoutManager(layoutManager);
- }
-
- @VisibleForTesting
- void onBindTile(DashboardItemHolder holder, Tile tile) {
- Drawable icon = mCache.getIcon(tile.icon);
- if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())) {
- icon = new RoundedHomepageIcon(mContext, icon);
- mCache.updateIcon(tile.icon, icon);
- }
- holder.icon.setImageDrawable(icon);
- holder.title.setText(tile.title);
- if (!TextUtils.isEmpty(tile.summary)) {
- holder.summary.setText(tile.summary);
- holder.summary.setVisibility(View.VISIBLE);
- } else {
- holder.summary.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- final DashboardCategory category = mDashboardData.getCategory();
- if (category != null) {
- outState.putParcelable(STATE_CATEGORY_LIST, category);
- }
- outState.putBoolean(STATE_CONDITION_EXPANDED, mDashboardData.isConditionExpanded());
- }
-
- private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
- if (icons == null || icons.size() < 2) {
- parent.setVisibility(View.INVISIBLE);
- return;
- }
- final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- parent.removeAllViews();
- for (int i = 1, size = icons.size(); i < size; i++) {
- ImageView icon = (ImageView) inflater.inflate(
- R.layout.condition_header_icon, parent, false);
- icon.setImageIcon(icons.get(i));
- parent.addView(icon);
- }
- parent.setVisibility(View.VISIBLE);
- }
-
- private void scrollToTopOfConditions() {
- mRecyclerView.scrollToPosition(mDashboardData.hasSuggestion() ? 1 : 0);
- }
-
- public static class IconCache {
- private final Context mContext;
- private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
-
- public IconCache(Context context) {
- mContext = context;
- }
-
- public Drawable getIcon(Icon icon) {
- if (icon == null) {
- return null;
- }
- Drawable drawable = mMap.get(icon);
- if (drawable == null) {
- drawable = icon.loadDrawable(mContext);
- updateIcon(icon, drawable);
- }
- return drawable;
- }
-
- public void updateIcon(Icon icon, Drawable drawable) {
- mMap.put(icon, drawable);
- }
- }
-
- public static class DashboardItemHolder extends RecyclerView.ViewHolder {
- public final ImageView icon;
- public final TextView title;
- public final TextView summary;
-
- public DashboardItemHolder(View itemView) {
- super(itemView);
- icon = itemView.findViewById(android.R.id.icon);
- title = itemView.findViewById(android.R.id.title);
- summary = itemView.findViewById(android.R.id.summary);
- }
- }
-
- public static class ConditionHeaderHolder extends DashboardItemHolder {
- public final LinearLayout icons;
- public final ImageView expandIndicator;
-
- public ConditionHeaderHolder(View itemView) {
- super(itemView);
- icons = itemView.findViewById(id.additional_icons);
- expandIndicator = itemView.findViewById(id.expand_indicator);
- }
- }
-
- public static class ConditionContainerHolder extends DashboardItemHolder {
- public final RecyclerView data;
-
- public ConditionContainerHolder(View itemView) {
- super(itemView);
- data = itemView.findViewById(id.data);
- }
- }
-
- public static class SuggestionContainerHolder extends DashboardItemHolder {
- public final RecyclerView data;
-
- public SuggestionContainerHolder(View itemView) {
- super(itemView);
- data = itemView.findViewById(id.suggestion_list);
- }
- }
-
-}
* ItemsData has inner class Item, which represents the Item in data list.
*/
public class DashboardData {
- public static final int HEADER_MODE_DEFAULT = 0;
- public static final int HEADER_MODE_SUGGESTION_EXPANDED = 1;
- public static final int HEADER_MODE_FULLY_EXPANDED = 2;
- public static final int HEADER_MODE_COLLAPSED = 3;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({HEADER_MODE_DEFAULT, HEADER_MODE_SUGGESTION_EXPANDED, HEADER_MODE_FULLY_EXPANDED,
- HEADER_MODE_COLLAPSED})
- public @interface HeaderMode {
- }
-
public static final int POSITION_NOT_FOUND = -1;
- public static final int DEFAULT_SUGGESTION_COUNT = 2;
+ public static final int MAX_SUGGESTION_COUNT = 4;
// stable id for different type of items.
@VisibleForTesting
- static final int STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER = 0;
- @VisibleForTesting
- static final int STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER = 1;
+ static final int STABLE_ID_SUGGESTION_CONTAINER = 0;
+ static final int STABLE_ID_SUGGESTION_CONDITION_DIVIDER = 1;
@VisibleForTesting
- static final int STABLE_ID_SUGGESTION_CONDITION_FOOTER = 2;
+ static final int STABLE_ID_CONDITION_HEADER = 2;
@VisibleForTesting
- static final int STABLE_ID_SUGGESTION_CONTAINER = 3;
+ static final int STABLE_ID_CONDITION_FOOTER = 3;
@VisibleForTesting
static final int STABLE_ID_CONDITION_CONTAINER = 4;
private final DashboardCategory mCategory;
private final List<Condition> mConditions;
private final List<Suggestion> mSuggestions;
- @HeaderMode
- private final int mSuggestionConditionMode;
+ private final boolean mConditionExpanded;
private DashboardData(Builder builder) {
mCategory = builder.mCategory;
mConditions = builder.mConditions;
- mSuggestions = builder.mSuggestionsV2;
- mSuggestionConditionMode = builder.mSuggestionConditionMode;
-
+ mSuggestions = builder.mSuggestions;
+ mConditionExpanded = builder.mConditionExpanded;
mItems = new ArrayList<>();
buildItemsData();
return mSuggestions;
}
- public int getSuggestionConditionMode() {
- return mSuggestionConditionMode;
+ public boolean hasSuggestion() {
+ return sizeOf(mSuggestions) > 0;
+ }
+
+ public boolean isConditionExpanded() {
+ return mConditionExpanded;
}
/**
/**
* Build the mItems list using mConditions, mSuggestions, mCategories data
- * and mIsShowingAll, mSuggestionConditionMode flag.
+ * and mIsShowingAll, mConditionExpanded flag.
*/
private void buildItemsData() {
- final boolean hasSuggestions = sizeOf(mSuggestions) > 0;
final List<Condition> conditions = getConditionsToShow(mConditions);
final boolean hasConditions = sizeOf(conditions) > 0;
final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
+ final boolean hasSuggestions = sizeOf(suggestions) > 0;
- final int hiddenSuggestion = hasSuggestions
- ? sizeOf(mSuggestions) - sizeOf(suggestions)
- : 0;
-
- final boolean hasSuggestionAndCollapsed = hasSuggestions
- && mSuggestionConditionMode == HEADER_MODE_COLLAPSED;
- final boolean onlyHasConditionAndCollapsed = !hasSuggestions
- && hasConditions
- && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED;
-
- /* Top suggestion/condition header. This will be present when there is any suggestion
- * and the mode is collapsed */
- addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
- R.layout.suggestion_condition_header,
- STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER, hasSuggestionAndCollapsed);
-
- /* Use mid header if there is only condition & it's in collapsed mode */
- addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
- R.layout.suggestion_condition_header,
- STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER, onlyHasConditionAndCollapsed);
-
- addToItemList(suggestions, R.layout.suggestion_condition_container,
- STABLE_ID_SUGGESTION_CONTAINER, sizeOf(suggestions) > 0);
-
- /* Second suggestion/condition header. This will be added when there is at least one
- * suggestion or condition that is not currently displayed, and the user can expand the
- * section to view more items. */
- addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
- R.layout.suggestion_condition_header,
- STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER,
- mSuggestionConditionMode != HEADER_MODE_COLLAPSED
- && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED
- && (hiddenSuggestion > 0 || hasConditions && hasSuggestions));
-
- /* Condition container. This is the card view that contains the list of conditions.
- * This will be added whenever the condition list is not empty */
- addToItemList(conditions, R.layout.suggestion_condition_container,
- STABLE_ID_CONDITION_CONTAINER,
- hasConditions && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED);
-
- /* Suggestion/condition footer. This will be present when the section is fully expanded
- * or when there is no conditions and no hidden suggestions */
- addToItemList(null /* item */, R.layout.suggestion_condition_footer,
- STABLE_ID_SUGGESTION_CONDITION_FOOTER,
- (hasConditions || hasSuggestions)
- && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED
- || hasSuggestions
- && !hasConditions
- && hiddenSuggestion == 0);
+ /* Suggestion container. This is the card view that contains the list of suggestions.
+ * This will be added whenever the suggestion list is not empty */
+ addToItemList(suggestions, R.layout.suggestion_container,
+ STABLE_ID_SUGGESTION_CONTAINER, hasSuggestions);
+
+ /* Divider between suggestion and conditions if both are present. */
+ addToItemList(null /* item */, R.layout.horizontal_divider,
+ STABLE_ID_SUGGESTION_CONDITION_DIVIDER, hasSuggestions && hasConditions);
+
+ /* Condition header. This will be present when there is condition and it is collapsed */
+ addToItemList(new ConditionHeaderData(conditions),
+ R.layout.condition_header,
+ STABLE_ID_CONDITION_HEADER, hasConditions && !mConditionExpanded);
+
+ /* Condition container. This is the card view that contains the list of conditions.
+ * This will be added whenever the condition list is not empty and expanded */
+ addToItemList(conditions, R.layout.condition_container,
+ STABLE_ID_CONDITION_CONTAINER, hasConditions && mConditionExpanded);
+
+ /* Condition footer. This will be present when there is condition and it is expanded */
+ addToItemList(null /* item */, R.layout.condition_footer,
+ STABLE_ID_CONDITION_FOOTER, hasConditions && mConditionExpanded);
if (mCategory != null) {
final List<Tile> tiles = mCategory.getTiles();
- for (int j = 0; j < tiles.size(); j++) {
- final Tile tile = tiles.get(j);
+ for (int i = 0; i < tiles.size(); i++) {
+ final Tile tile = tiles.get(i);
addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title),
true /* add */);
}
}
private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
- if (suggestions == null || mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
+ if (suggestions == null) {
return null;
}
- if (mSuggestionConditionMode != HEADER_MODE_DEFAULT
- || suggestions.size() <= DEFAULT_SUGGESTION_COUNT) {
+ if (suggestions.size() <= MAX_SUGGESTION_COUNT) {
return suggestions;
}
- return suggestions.subList(0, DEFAULT_SUGGESTION_COUNT);
+ return suggestions.subList(0, MAX_SUGGESTION_COUNT);
}
/**
* Builder used to build the ItemsData
- * <p>
- * {@link #mSuggestionConditionMode} have default value while others are not.
*/
public static class Builder {
- @HeaderMode
- private int mSuggestionConditionMode = HEADER_MODE_DEFAULT;
-
private DashboardCategory mCategory;
private List<Condition> mConditions;
- private List<Suggestion> mSuggestionsV2;
+ private List<Suggestion> mSuggestions;
+ private boolean mConditionExpanded;
public Builder() {
}
public Builder(DashboardData dashboardData) {
mCategory = dashboardData.mCategory;
mConditions = dashboardData.mConditions;
- mSuggestionsV2 = dashboardData.mSuggestions;
- mSuggestionConditionMode = dashboardData.mSuggestionConditionMode;
+ mSuggestions = dashboardData.mSuggestions;
+ mConditionExpanded = dashboardData.mConditionExpanded;
}
public Builder setCategory(DashboardCategory category) {
}
public Builder setSuggestions(List<Suggestion> suggestions) {
- this.mSuggestionsV2 = suggestions;
+ this.mSuggestions = suggestions;
return this;
}
- public Builder setSuggestionConditionMode(@HeaderMode int mode) {
- this.mSuggestionConditionMode = mode;
+ public Builder setConditionExpanded(boolean expanded) {
+ this.mConditionExpanded = expanded;
return this;
}
static class Item {
// valid types in field type
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
- private static final int TYPE_SUGGESTION_CONDITION_CONTAINER =
- R.layout.suggestion_condition_container;
- private static final int TYPE_SUGGESTION_CONDITION_HEADER =
- R.layout.suggestion_condition_header;
- private static final int TYPE_SUGGESTION_CONDITION_FOOTER =
- R.layout.suggestion_condition_footer;
- private static final int TYPE_DASHBOARD_SPACER = R.layout.dashboard_spacer;
-
- @IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONDITION_CONTAINER,
- TYPE_SUGGESTION_CONDITION_HEADER, TYPE_SUGGESTION_CONDITION_FOOTER,
- TYPE_DASHBOARD_SPACER})
+ private static final int TYPE_SUGGESTION_CONTAINER =
+ R.layout.suggestion_container;
+ private static final int TYPE_CONDITION_CONTAINER =
+ R.layout.condition_container;
+ private static final int TYPE_CONDITION_HEADER =
+ R.layout.condition_header;
+ private static final int TYPE_CONDITION_FOOTER =
+ R.layout.condition_footer;
+ private static final int TYPE_SUGGESTION_CONDITION_DIVIDER = R.layout.horizontal_divider;
+
+ @IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONTAINER, TYPE_CONDITION_CONTAINER,
+ TYPE_CONDITION_HEADER, TYPE_CONDITION_FOOTER, TYPE_SUGGESTION_CONDITION_DIVIDER})
@Retention(RetentionPolicy.SOURCE)
public @interface ItemTypes {
}
// Only check title and summary for dashboard tile
return TextUtils.equals(localTile.title, targetTile.title)
- && TextUtils.equals(localTile.summary, targetTile.summary);
- case TYPE_SUGGESTION_CONDITION_CONTAINER:
+ && TextUtils.equals(localTile.summary, targetTile.summary);
+ case TYPE_SUGGESTION_CONTAINER:
+ case TYPE_CONDITION_CONTAINER:
// If entity is suggestion and contains remote view, force refresh
final List entities = (List) entity;
if (!entities.isEmpty()) {
* This class contains the data needed to build the suggestion/condition header. The data can
* also be used to check the diff in DiffUtil.Callback
*/
- public static class SuggestionConditionHeaderData {
+ public static class ConditionHeaderData {
public final List<Icon> conditionIcons;
public final CharSequence title;
public final int conditionCount;
- public final int hiddenSuggestionCount;
- public SuggestionConditionHeaderData(List<Condition> conditions,
- int hiddenSuggestionCount) {
+ public ConditionHeaderData(List<Condition> conditions) {
conditionCount = sizeOf(conditions);
- this.hiddenSuggestionCount = hiddenSuggestionCount;
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
conditionIcons = new ArrayList<>();
for (int i = 0; conditions != null && i < conditions.size(); i++) {
+++ /dev/null
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.dashboard;
-
-import android.annotation.IntDef;
-import android.graphics.drawable.Icon;
-import android.service.settings.suggestions.Suggestion;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.util.DiffUtil;
-import android.text.TextUtils;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.conditional.Condition;
-import com.android.settingslib.drawer.DashboardCategory;
-import com.android.settingslib.drawer.Tile;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Description about data list used in the DashboardAdapter. In the data list each item can be
- * Condition, suggestion or category tile.
- * <p>
- * ItemsData has inner class Item, which represents the Item in data list.
- */
-public class DashboardDataV2 {
- public static final int POSITION_NOT_FOUND = -1;
- public static final int MAX_SUGGESTION_COUNT = 4;
-
- // stable id for different type of items.
- @VisibleForTesting
- static final int STABLE_ID_SUGGESTION_CONTAINER = 0;
- static final int STABLE_ID_SUGGESTION_CONDITION_DIVIDER = 1;
- @VisibleForTesting
- static final int STABLE_ID_CONDITION_HEADER = 2;
- @VisibleForTesting
- static final int STABLE_ID_CONDITION_FOOTER = 3;
- @VisibleForTesting
- static final int STABLE_ID_CONDITION_CONTAINER = 4;
-
- private final List<Item> mItems;
- private final DashboardCategory mCategory;
- private final List<Condition> mConditions;
- private final List<Suggestion> mSuggestions;
- private final boolean mConditionExpanded;
-
- private DashboardDataV2(Builder builder) {
- mCategory = builder.mCategory;
- mConditions = builder.mConditions;
- mSuggestions = builder.mSuggestions;
- mConditionExpanded = builder.mConditionExpanded;
- mItems = new ArrayList<>();
-
- buildItemsData();
- }
-
- public int getItemIdByPosition(int position) {
- return mItems.get(position).id;
- }
-
- public int getItemTypeByPosition(int position) {
- return mItems.get(position).type;
- }
-
- public Object getItemEntityByPosition(int position) {
- return mItems.get(position).entity;
- }
-
- public List<Item> getItemList() {
- return mItems;
- }
-
- public int size() {
- return mItems.size();
- }
-
- public Object getItemEntityById(long id) {
- for (final Item item : mItems) {
- if (item.id == id) {
- return item.entity;
- }
- }
- return null;
- }
-
- public DashboardCategory getCategory() {
- return mCategory;
- }
-
- public List<Condition> getConditions() {
- return mConditions;
- }
-
- public List<Suggestion> getSuggestions() {
- return mSuggestions;
- }
-
- public boolean hasSuggestion() {
- return sizeOf(mSuggestions) > 0;
- }
-
- public boolean isConditionExpanded() {
- return mConditionExpanded;
- }
-
- /**
- * Find the position of the object in mItems list, using the equals method to compare
- *
- * @param entity the object that need to be found in list
- * @return position of the object, return POSITION_NOT_FOUND if object isn't in the list
- */
- public int getPositionByEntity(Object entity) {
- if (entity == null) return POSITION_NOT_FOUND;
-
- final int size = mItems.size();
- for (int i = 0; i < size; i++) {
- final Object item = mItems.get(i).entity;
- if (entity.equals(item)) {
- return i;
- }
- }
-
- return POSITION_NOT_FOUND;
- }
-
- /**
- * Find the position of the Tile object.
- * <p>
- * First, try to find the exact identical instance of the tile object, if not found,
- * then try to find a tile has the same title.
- *
- * @param tile tile that need to be found
- * @return position of the object, return INDEX_NOT_FOUND if object isn't in the list
- */
- public int getPositionByTile(Tile tile) {
- final int size = mItems.size();
- for (int i = 0; i < size; i++) {
- final Object entity = mItems.get(i).entity;
- if (entity == tile) {
- return i;
- } else if (entity instanceof Tile && tile.title.equals(((Tile) entity).title)) {
- return i;
- }
- }
-
- return POSITION_NOT_FOUND;
- }
-
- /**
- * Add item into list when {@paramref add} is true.
- *
- * @param item maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null
- * @param type type of the item, and value is the layout id
- * @param stableId The stable id for this item
- * @param add flag about whether to add item into list
- */
- private void addToItemList(Object item, int type, int stableId, boolean add) {
- if (add) {
- mItems.add(new Item(item, type, stableId));
- }
- }
-
- /**
- * Build the mItems list using mConditions, mSuggestions, mCategories data
- * and mIsShowingAll, mConditionExpanded flag.
- */
- private void buildItemsData() {
- final List<Condition> conditions = getConditionsToShow(mConditions);
- final boolean hasConditions = sizeOf(conditions) > 0;
-
- final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
- final boolean hasSuggestions = sizeOf(suggestions) > 0;
-
- /* Suggestion container. This is the card view that contains the list of suggestions.
- * This will be added whenever the suggestion list is not empty */
- addToItemList(suggestions, R.layout.suggestion_container,
- STABLE_ID_SUGGESTION_CONTAINER, hasSuggestions);
-
- /* Divider between suggestion and conditions if both are present. */
- addToItemList(suggestions, R.layout.horizontal_divider,
- STABLE_ID_SUGGESTION_CONDITION_DIVIDER, hasSuggestions && hasConditions);
-
- /* Condition header. This will be present when there is condition and it is collapsed */
- addToItemList(new ConditionHeaderData(conditions),
- R.layout.suggestion_condition_header,
- STABLE_ID_CONDITION_HEADER, hasConditions && !mConditionExpanded);
-
- /* Condition container. This is the card view that contains the list of conditions.
- * This will be added whenever the condition list is not empty and expanded */
- addToItemList(conditions, R.layout.condition_container,
- STABLE_ID_CONDITION_CONTAINER, hasConditions && mConditionExpanded);
-
- /* Condition footer. This will be present when there is condition and it is expanded */
- addToItemList(null /* item */, R.layout.suggestion_condition_footer,
- STABLE_ID_CONDITION_FOOTER, hasConditions && mConditionExpanded);
-
- if (mCategory != null) {
- final List<Tile> tiles = mCategory.getTiles();
- for (int i = 0; i < tiles.size(); i++) {
- final Tile tile = tiles.get(i);
- addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title),
- true /* add */);
- }
- }
- }
-
- private static int sizeOf(List<?> list) {
- return list == null ? 0 : list.size();
- }
-
- private List<Condition> getConditionsToShow(List<Condition> conditions) {
- if (conditions == null) {
- return null;
- }
- List<Condition> result = new ArrayList<>();
- final int size = conditions == null ? 0 : conditions.size();
- for (int i = 0; i < size; i++) {
- final Condition condition = conditions.get(i);
- if (condition.shouldShow()) {
- result.add(condition);
- }
- }
- return result;
- }
-
- private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
- if (suggestions == null) {
- return null;
- }
- if (suggestions.size() <= MAX_SUGGESTION_COUNT) {
- return suggestions;
- }
- return suggestions.subList(0, MAX_SUGGESTION_COUNT);
- }
-
- /**
- * Builder used to build the ItemsData
- */
- public static class Builder {
- private DashboardCategory mCategory;
- private List<Condition> mConditions;
- private List<Suggestion> mSuggestions;
- private boolean mConditionExpanded;
-
- public Builder() {
- }
-
- public Builder(DashboardDataV2 dashboardData) {
- mCategory = dashboardData.mCategory;
- mConditions = dashboardData.mConditions;
- mSuggestions = dashboardData.mSuggestions;
- mConditionExpanded = dashboardData.mConditionExpanded;
- }
-
- public Builder setCategory(DashboardCategory category) {
- this.mCategory = category;
- return this;
- }
-
- public Builder setConditions(List<Condition> conditions) {
- this.mConditions = conditions;
- return this;
- }
-
- public Builder setSuggestions(List<Suggestion> suggestions) {
- this.mSuggestions = suggestions;
- return this;
- }
-
- public Builder setConditionExpanded(boolean expanded) {
- this.mConditionExpanded = expanded;
- return this;
- }
-
- public DashboardDataV2 build() {
- return new DashboardDataV2(this);
- }
- }
-
- /**
- * A DiffCallback to calculate the difference between old and new Item
- * List in DashboardDataV2
- */
- public static class ItemsDataDiffCallback extends DiffUtil.Callback {
- final private List<Item> mOldItems;
- final private List<Item> mNewItems;
-
- public ItemsDataDiffCallback(List<Item> oldItems, List<Item> newItems) {
- mOldItems = oldItems;
- mNewItems = newItems;
- }
-
- @Override
- public int getOldListSize() {
- return mOldItems.size();
- }
-
- @Override
- public int getNewListSize() {
- return mNewItems.size();
- }
-
- @Override
- public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
- return mOldItems.get(oldItemPosition).id == mNewItems.get(newItemPosition).id;
- }
-
- @Override
- public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
- return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
- }
-
- }
-
- /**
- * An item contains the data needed in the DashboardDataV2.
- */
- static class Item {
- // valid types in field type
- private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
- private static final int TYPE_SUGGESTION_CONTAINER =
- R.layout.suggestion_container;
- private static final int TYPE_CONDITION_CONTAINER =
- R.layout.condition_container;
- private static final int TYPE_CONDITION_HEADER =
- R.layout.suggestion_condition_header;
- private static final int TYPE_CONDITION_FOOTER =
- R.layout.suggestion_condition_footer;
- private static final int TYPE_SUGGESTION_CONDITION_DIVIDER = R.layout.horizontal_divider;
-
- @IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONTAINER, TYPE_CONDITION_CONTAINER,
- TYPE_CONDITION_HEADER, TYPE_CONDITION_FOOTER, TYPE_SUGGESTION_CONDITION_DIVIDER})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ItemTypes {
- }
-
- /**
- * The main data object in item, usually is a {@link Tile}, {@link Condition}
- * object. This object can also be null when the
- * item is an divider line. Please refer to {@link #buildItemsData()} for
- * detail usage of the Item.
- */
- public final Object entity;
-
- /**
- * The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile)
- */
- @ItemTypes
- public final int type;
-
- /**
- * Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item.
- */
- public final int id;
-
- public Item(Object entity, @ItemTypes int type, int id) {
- this.entity = entity;
- this.type = type;
- this.id = id;
- }
-
- /**
- * Override it to make comparision in the {@link ItemsDataDiffCallback}
- *
- * @param obj object to compared with
- * @return true if the same object or has equal value.
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (!(obj instanceof Item)) {
- return false;
- }
-
- final Item targetItem = (Item) obj;
- if (type != targetItem.type || id != targetItem.id) {
- return false;
- }
-
- switch (type) {
- case TYPE_DASHBOARD_TILE:
- final Tile localTile = (Tile) entity;
- final Tile targetTile = (Tile) targetItem.entity;
-
- // Only check title and summary for dashboard tile
- return TextUtils.equals(localTile.title, targetTile.title)
- && TextUtils.equals(localTile.summary, targetTile.summary);
- case TYPE_SUGGESTION_CONTAINER:
- case TYPE_CONDITION_CONTAINER:
- // If entity is suggestion and contains remote view, force refresh
- final List entities = (List) entity;
- if (!entities.isEmpty()) {
- Object firstEntity = entities.get(0);
- if (firstEntity instanceof Tile
- && ((Tile) firstEntity).remoteViews != null) {
- return false;
- }
- }
- // Otherwise Fall through to default
- default:
- return entity == null ? targetItem.entity == null
- : entity.equals(targetItem.entity);
- }
- }
- }
-
- /**
- * This class contains the data needed to build the suggestion/condition header. The data can
- * also be used to check the diff in DiffUtil.Callback
- */
- public static class ConditionHeaderData {
- public final List<Icon> conditionIcons;
- public final CharSequence title;
- public final int conditionCount;
-
- public ConditionHeaderData(List<Condition> conditions) {
- conditionCount = sizeOf(conditions);
- title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
- conditionIcons = new ArrayList<>();
- for (int i = 0; conditions != null && i < conditions.size(); i++) {
- final Condition condition = conditions.get(i);
- conditionIcons.add(condition.getIcon());
- }
- }
- }
-
-}
\ No newline at end of file
*/
void openTileIntent(Activity activity, Tile tile);
- /**
- * Whether or not we should use the v2 of suggestions UI.
- */
- boolean useSuggestionUiV2();
-
}
launchIntentOrSelectProfile(activity, tile, intent, MetricsEvent.DASHBOARD_SUMMARY);
}
- @Override
- public boolean useSuggestionUiV2() {
- return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.SUGGESTION_UI_V2);
- }
-
private void bindSummary(Preference preference, Tile tile) {
if (tile.summary != null) {
preference.setSummary(tile.summary);
import com.android.settings.dashboard.conditional.ConditionManager.ConditionListener;
import com.android.settings.dashboard.conditional.FocusRecyclerView;
import com.android.settings.dashboard.conditional.FocusRecyclerView.FocusListener;
-import com.android.settings.dashboard.suggestions.SuggestionDismissController;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.ActionBarShadowController;
public class DashboardSummary extends InstrumentedFragment
implements CategoryListener, ConditionListener,
- FocusListener, SuggestionDismissController.Callback,
- SuggestionControllerMixin.SuggestionControllerHost {
+ FocusListener, SuggestionControllerMixin.SuggestionControllerHost {
public static final boolean DEBUG = false;
private static final boolean DEBUG_TIMING = false;
private static final int MAX_WAIT_MILLIS = 700;
private FocusRecyclerView mDashboard;
private DashboardAdapter mAdapter;
- private DashboardAdapterV2 mAdapterV2;
private SummaryLoader mSummaryLoader;
private ConditionManager mConditionManager;
private LinearLayoutManager mLayoutManager;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- if (mLayoutManager == null) return;
- outState.putInt(EXTRA_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
- if (!mDashboardFeatureProvider.useSuggestionUiV2()) {
- if (mAdapter != null) {
- mAdapter.onSaveInstanceState(outState);
- }
+ if (mLayoutManager == null) {
+ return;
}
+ outState.putInt(EXTRA_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
}
@Override
mDashboard.setHasFixedSize(true);
mDashboard.setListener(this);
mDashboard.setItemAnimator(new DashboardItemAnimator());
- if (mDashboardFeatureProvider.useSuggestionUiV2()) {
- mAdapterV2 = new DashboardAdapterV2(getContext(), bundle,
- mConditionManager.getConditions(), mSuggestionControllerMixin, getLifecycle());
- mDashboard.setAdapter(mAdapterV2);
- mSummaryLoader.setSummaryConsumer(mAdapterV2);
- } else {
- mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions(),
- mSuggestionControllerMixin, this /* SuggestionDismissController.Callback */);
- mDashboard.setAdapter(mAdapter);
- mSummaryLoader.setSummaryConsumer(mAdapter);
- }
+ mAdapter = new DashboardAdapter(getContext(), bundle,
+ mConditionManager.getConditions(), mSuggestionControllerMixin, getLifecycle());
+ mDashboard.setAdapter(mAdapter);
+ mSummaryLoader.setSummaryConsumer(mAdapter);
ActionBarShadowController.attachToRecyclerView(
getActivity().findViewById(R.id.search_bar_container), getLifecycle(), mDashboard);
rebuildUI();
if (mOnConditionsChangedCalled) {
final boolean scrollToTop =
mLayoutManager.findFirstCompletelyVisibleItemPosition() <= 1;
- if (mDashboardFeatureProvider.useSuggestionUiV2()) {
- mAdapterV2.setConditions(mConditionManager.getConditions());
- } else {
- mAdapter.setConditions(mConditionManager.getConditions());
- }
+ mAdapter.setConditions(mConditionManager.getConditions());
if (scrollToTop) {
mDashboard.scrollToPosition(0);
}
}
@Override
- public Suggestion getSuggestionAt(int position) {
- if (mDashboardFeatureProvider.useSuggestionUiV2()) {
- return mAdapterV2.getSuggestion(position);
- } else {
- return mAdapter.getSuggestion(position);
- }
- }
-
- @Override
- public void onSuggestionDismissed(Suggestion suggestion) {
- mAdapter.onSuggestionDismissed(suggestion);
- }
-
- @Override
public void onSuggestionReady(List<Suggestion> suggestions) {
mStagingSuggestions = suggestions;
- if (mDashboardFeatureProvider.useSuggestionUiV2()) {
- mAdapterV2.setSuggestions(suggestions);
- if (mStagingCategory != null) {
- Log.d(TAG, "Category has loaded, setting category from suggestionReady");
- mHandler.removeCallbacksAndMessages(null);
- mAdapterV2.setCategory(mStagingCategory);
- }
- } else {
- mAdapter.setSuggestions(suggestions);
- if (mStagingCategory != null) {
- Log.d(TAG, "Category has loaded, setting category from suggestionReady");
- mHandler.removeCallbacksAndMessages(null);
- mAdapter.setCategory(mStagingCategory);
- }
+ mAdapter.setSuggestions(suggestions);
+ if (mStagingCategory != null) {
+ Log.d(TAG, "Category has loaded, setting category from suggestionReady");
+ mHandler.removeCallbacksAndMessages(null);
+ mAdapter.setCategory(mStagingCategory);
}
}
if (mSuggestionControllerMixin.isSuggestionLoaded()) {
Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
ThreadUtils.postOnMainThread(() -> {
- if (mDashboardFeatureProvider.useSuggestionUiV2()) {
- if (mStagingSuggestions != null) {
- mAdapterV2.setSuggestions(mStagingSuggestions);
- }
- mAdapterV2.setCategory(mStagingCategory);
- } else {
- if (mStagingSuggestions != null) {
- mAdapter.setSuggestions(mStagingSuggestions);
- }
- mAdapter.setCategory(mStagingCategory);
+ if (mStagingSuggestions != null) {
+ mAdapter.setSuggestions(mStagingSuggestions);
}
+ mAdapter.setCategory(mStagingCategory);
});
} else {
Log.d(TAG, "Suggestion NOT loaded, delaying setCategory by " + MAX_WAIT_MILLIS + "ms");
- if (mDashboardFeatureProvider.useSuggestionUiV2()) {
- mHandler.postDelayed(()
- -> mAdapterV2.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
- } else {
- mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
- }
+ mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
}
}
}
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
-import com.android.settings.dashboard.DashboardAdapter;
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
-import com.android.settings.dashboard.DashboardData;
-import com.android.settings.dashboard.DashboardData.HeaderMode;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private List<Condition> mConditions;
- private @HeaderMode int mMode;
+ private boolean mExpanded;
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
}
};
- public ConditionAdapter(Context context, List<Condition> conditions, @HeaderMode int mode) {
+ public ConditionAdapter(Context context, List<Condition> conditions, boolean expanded) {
mContext = context;
mConditions = conditions;
- mMode = mode;
+ mExpanded = expanded;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
setHasStableIds(true);
@Override
public int getItemCount() {
- if (mMode == DashboardData.HEADER_MODE_FULLY_EXPANDED) {
+ if (mExpanded) {
return mConditions.size();
}
return 0;
}
private void bindViews(final Condition condition,
- DashboardAdapter.DashboardItemHolder view, boolean isLastItem,
+ DashboardItemHolder view, boolean isLastItem,
View.OnClickListener onClickListener) {
if (condition instanceof AirplaneModeCondition) {
Log.d(TAG, "Airplane mode condition has been bound with "
+++ /dev/null
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.dashboard.conditional;
-
-import android.content.Context;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
-import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.WirelessUtils;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-
-import java.util.List;
-import java.util.Objects;
-
-public class ConditionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> {
- public static final String TAG = "ConditionAdapter";
-
- private final Context mContext;
- private final MetricsFeatureProvider mMetricsFeatureProvider;
- private List<Condition> mConditions;
- private boolean mExpanded;
-
- private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- //TODO: get rid of setTag/getTag
- Condition condition = (Condition) v.getTag();
- mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
- condition.getMetricsConstant());
- condition.onPrimaryClick();
- }
- };
-
- @VisibleForTesting
- ItemTouchHelper.SimpleCallback mSwipeCallback = new ItemTouchHelper.SimpleCallback(0,
- ItemTouchHelper.START | ItemTouchHelper.END) {
- @Override
- public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
- RecyclerView.ViewHolder target) {
- return true;
- }
-
- @Override
- public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
- return viewHolder.getItemViewType() == R.layout.condition_tile
- ? super.getSwipeDirs(recyclerView, viewHolder) : 0;
- }
-
- @Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
- Object item = getItem(viewHolder.getItemId());
- // item can become null when running monkey
- if (item != null) {
- ((Condition) item).silence();
- }
- }
- };
-
- public ConditionAdapterV2(Context context, List<Condition> conditions, boolean expanded) {
- mContext = context;
- mConditions = conditions;
- mExpanded = expanded;
- mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
-
- setHasStableIds(true);
- }
-
- public Object getItem(long itemId) {
- for (Condition condition : mConditions) {
- if (Objects.hash(condition.getTitle()) == itemId) {
- return condition;
- }
- }
- return null;
- }
-
- @Override
- public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
- viewType, parent, false));
- }
-
- @Override
- public void onBindViewHolder(DashboardItemHolder holder, int position) {
- bindViews(mConditions.get(position), holder,
- position == mConditions.size() - 1, mConditionClickListener);
- }
-
- @Override
- public long getItemId(int position) {
- return Objects.hash(mConditions.get(position).getTitle());
- }
-
- @Override
- public int getItemViewType(int position) {
- return R.layout.condition_tile;
- }
-
- @Override
- public int getItemCount() {
- if (mExpanded) {
- return mConditions.size();
- }
- return 0;
- }
-
- public void addDismissHandling(final RecyclerView recyclerView) {
- final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mSwipeCallback);
- itemTouchHelper.attachToRecyclerView(recyclerView);
- }
-
- private void bindViews(final Condition condition,
- DashboardItemHolder view, boolean isLastItem,
- View.OnClickListener onClickListener) {
- if (condition instanceof AirplaneModeCondition) {
- Log.d(TAG, "Airplane mode condition has been bound with "
- + "isActive=" + condition.isActive() + ". Airplane mode is currently " +
- WirelessUtils.isAirplaneModeOn(condition.mManager.getContext()));
- }
- View card = view.itemView.findViewById(R.id.content);
- card.setTag(condition);
- card.setOnClickListener(onClickListener);
- view.icon.setImageIcon(condition.getIcon());
- view.title.setText(condition.getTitle());
-
- CharSequence[] actions = condition.getActions();
- final boolean hasButtons = actions.length > 0;
- setViewVisibility(view.itemView, R.id.buttonBar, hasButtons);
-
- view.summary.setText(condition.getSummary());
- for (int i = 0; i < 2; i++) {
- Button button = (Button) view.itemView.findViewById(i == 0
- ? R.id.first_action : R.id.second_action);
- if (actions.length > i) {
- button.setVisibility(View.VISIBLE);
- button.setText(actions[i]);
- final int index = i;
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Context context = v.getContext();
- FeatureFactory.getFactory(context).getMetricsFeatureProvider()
- .action(context, MetricsEvent.ACTION_SETTINGS_CONDITION_BUTTON,
- condition.getMetricsConstant());
- condition.onActionClick(index);
- }
- });
- } else {
- button.setVisibility(View.GONE);
- }
- }
- setViewVisibility(view.itemView, R.id.divider, !isLastItem);
- }
-
- private void setViewVisibility(View containerView, int viewId, boolean visible) {
- View view = containerView.findViewById(viewId);
- if (view != null) {
- view.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
- }
-}
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
import com.android.settings.dashboard.DashboardAdapter.IconCache;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.Utils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.suggestions.SuggestionControllerMixin;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> {
+public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> implements
+ LifecycleObserver, OnSaveInstanceState {
public static final String TAG = "SuggestionAdapter";
+ private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
+ private static final String STATE_SUGGESTION_LIST = "suggestion_list";
+
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
- private final List<Suggestion> mSuggestions;
private final IconCache mCache;
- private final List<String> mSuggestionsShownLogged;
+ private final ArrayList<String> mSuggestionsShownLogged;
+ private final SuggestionFeatureProvider mSuggestionFeatureProvider;
private final SuggestionControllerMixin mSuggestionControllerMixin;
+ private final Callback mCallback;
+ private final CardConfig mConfig;
+
+ private List<Suggestion> mSuggestions;
+
+ public interface Callback {
+ /**
+ * Called when the close button of the suggestion card is clicked.
+ */
+ void onSuggestionClosed(Suggestion suggestion);
+ }
public SuggestionAdapter(Context context, SuggestionControllerMixin suggestionControllerMixin,
- List<Suggestion> suggestions, List<String> suggestionsShownLogged) {
+ Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
mContext = context;
mSuggestionControllerMixin = suggestionControllerMixin;
- mSuggestions = suggestions;
- mSuggestionsShownLogged = suggestionsShownLogged;
mCache = new IconCache(context);
final FeatureFactory factory = FeatureFactory.getFactory(context);
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
+ mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
+ mCallback = callback;
+ if (savedInstanceState != null) {
+ mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
+ mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
+ STATE_SUGGESTIONS_SHOWN_LOGGED);
+ } else {
+ mSuggestionsShownLogged = new ArrayList<>();
+ }
+
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ mConfig = CardConfig.get(context);
setHasStableIds(true);
}
@Override
public void onBindViewHolder(DashboardItemHolder holder, int position) {
- bindSuggestion(holder, position);
- }
-
- private void bindSuggestion(DashboardItemHolder holder, int position) {
final Suggestion suggestion = mSuggestions.get(position);
final String id = suggestion.getId();
+ final int suggestionCount = mSuggestions.size();
if (!mSuggestionsShownLogged.contains(id)) {
mMetricsFeatureProvider.action(
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
mSuggestionsShownLogged.add(id);
}
-
- holder.icon.setImageDrawable(mCache.getIcon(suggestion.getIcon()));
+ mConfig.setCardLayout(holder, suggestionCount, position);
+ final Icon icon = suggestion.getIcon();
+ final Drawable drawable = mCache.getIcon(icon);
+ if ((suggestion.getFlags() & Suggestion.FLAG_ICON_TINTABLE) != 0) {
+ drawable.setTint(Utils.getColorAccent(mContext));
+ }
+ holder.icon.setImageDrawable(drawable);
holder.title.setText(suggestion.getTitle());
- final CharSequence summary = suggestion.getSummary();
- if (!TextUtils.isEmpty(summary)) {
- holder.summary.setText(summary);
- holder.summary.setVisibility(View.VISIBLE);
+ holder.title.setSingleLine(suggestionCount == 1);
+
+ if (suggestionCount == 1) {
+ final CharSequence summary = suggestion.getSummary();
+ if (!TextUtils.isEmpty(summary)) {
+ holder.summary.setText(summary);
+ holder.summary.setVisibility(View.VISIBLE);
+ } else {
+ holder.summary.setVisibility(View.GONE);
+ }
} else {
+ // Do not show summary if there are more than 1 suggestions
holder.summary.setVisibility(View.GONE);
+ holder.title.setMaxLines(3);
}
- final View divider = holder.itemView.findViewById(R.id.divider);
- if (divider != null) {
- divider.setVisibility(position < mSuggestions.size() - 1 ? View.VISIBLE : View.GONE);
+
+ final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
+ if (closeButton != null) {
+ closeButton.setOnClickListener(v -> {
+ mSuggestionFeatureProvider.dismissSuggestion(
+ mContext, mSuggestionControllerMixin, suggestion);
+ if (mCallback != null) {
+ mCallback.onSuggestionClosed(suggestion);
+ }
+ });
}
+
View clickHandler = holder.itemView;
// If a view with @android:id/primary is defined, use that as the click handler
// instead.
}
public void removeSuggestion(Suggestion suggestion) {
+ final int position = mSuggestions.indexOf(suggestion);
mSuggestions.remove(suggestion);
- notifyDataSetChanged();
+ notifyItemRemoved(position);
}
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ if (mSuggestions != null) {
+ outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
+ new ArrayList<>(mSuggestions));
+ }
+ outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
+ }
+
+ public void setSuggestions(List<Suggestion> suggestions) {
+ mSuggestions = suggestions;
+ }
+
+ public List<Suggestion> getSuggestions() {
+ return mSuggestions;
+ }
+
+ private static class CardConfig {
+ // Card start/end margin
+ private final int mMarginInner;
+ private final int mMarginOuter;
+ // Card width for different numbers of cards
+ private final int mWidthSingleCard;
+ private final int mWidthTwoCards;
+ private final int mWidthMultipleCards;
+ // padding between icon and title
+ private final int mPaddingTitleTopSingleCard;
+ private final int mPaddingTitleTopMultipleCards;
+
+ private static CardConfig sConfig;
+
+ private CardConfig(Context context) {
+ final Resources res = context.getResources();
+ mMarginInner =
+ res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
+ mMarginOuter =
+ res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
+ mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
+ mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
+ mWidthMultipleCards =
+ res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
+ mPaddingTitleTopSingleCard =
+ res.getDimensionPixelOffset(R.dimen.suggestion_card_title_padding_bottom_one_card);
+ mPaddingTitleTopMultipleCards = res.getDimensionPixelOffset(
+ R.dimen.suggestion_card_title_padding_bottom_multiple_cards);
+ }
+
+ public static CardConfig get(Context context) {
+ if (sConfig == null) {
+ sConfig = new CardConfig(context);
+ }
+ return sConfig;
+ }
+
+ private void setCardLayout(DashboardItemHolder holder, int suggestionCount,
+ int position) {
+ final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ suggestionCount == 1
+ ? mWidthSingleCard : suggestionCount == 2
+ ? mWidthTwoCards : mWidthMultipleCards,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ if (suggestionCount == 1) {
+ params.setMarginStart(mMarginOuter);
+ params.setMarginEnd(mMarginOuter);
+ } else {
+ params.setMarginStart(
+ position == 0 ? mMarginOuter : mMarginInner);
+ params.setMarginEnd(position == suggestionCount - 1 ? mMarginOuter : 0);
+ }
+ holder.itemView.setLayoutParams(params);
+ }
+
+ }
+
}
+++ /dev/null
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.dashboard.suggestions;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.service.settings.suggestions.Suggestion;
-import android.support.v7.widget.RecyclerView;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
-import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
-import com.android.settings.dashboard.DashboardAdapterV2.IconCache;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.Utils;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
-import com.android.settingslib.suggestions.SuggestionControllerMixin;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-public class SuggestionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> implements
- LifecycleObserver, OnSaveInstanceState {
- public static final String TAG = "SuggestionAdapterV2";
-
- private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
- private static final String STATE_SUGGESTION_LIST = "suggestion_list";
-
- private final Context mContext;
- private final MetricsFeatureProvider mMetricsFeatureProvider;
- private final IconCache mCache;
- private final ArrayList<String> mSuggestionsShownLogged;
- private final SuggestionFeatureProvider mSuggestionFeatureProvider;
- private final SuggestionControllerMixin mSuggestionControllerMixin;
- private final Callback mCallback;
- private final CardConfig mConfig;
-
- private List<Suggestion> mSuggestions;
-
- public interface Callback {
- /**
- * Called when the close button of the suggestion card is clicked.
- */
- void onSuggestionClosed(Suggestion suggestion);
- }
-
- public SuggestionAdapterV2(Context context, SuggestionControllerMixin suggestionControllerMixin,
- Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
- mContext = context;
- mSuggestionControllerMixin = suggestionControllerMixin;
- mCache = new IconCache(context);
- final FeatureFactory factory = FeatureFactory.getFactory(context);
- mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
- mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
- mCallback = callback;
- if (savedInstanceState != null) {
- mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
- mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
- STATE_SUGGESTIONS_SHOWN_LOGGED);
- } else {
- mSuggestionsShownLogged = new ArrayList<>();
- }
-
- if (lifecycle != null) {
- lifecycle.addObserver(this);
- }
- mConfig = CardConfig.get(context);
-
- setHasStableIds(true);
- }
-
- @Override
- public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
- viewType, parent, false));
- }
-
- @Override
- public void onBindViewHolder(DashboardItemHolder holder, int position) {
- final Suggestion suggestion = mSuggestions.get(position);
- final String id = suggestion.getId();
- final int suggestionCount = mSuggestions.size();
- if (!mSuggestionsShownLogged.contains(id)) {
- mMetricsFeatureProvider.action(
- mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
- mSuggestionsShownLogged.add(id);
- }
- mConfig.setCardLayout(holder, suggestionCount, position);
- final Icon icon = suggestion.getIcon();
- final Drawable drawable = mCache.getIcon(icon);
- if (drawable != null && TextUtils.equals(icon.getResPackage(), mContext.getPackageName())) {
- drawable.setTint(Utils.getColorAccent(mContext));
- }
- holder.icon.setImageDrawable(drawable);
- holder.title.setText(suggestion.getTitle());
- holder.title.setSingleLine(suggestionCount == 1);
-
- if (suggestionCount == 1) {
- final CharSequence summary = suggestion.getSummary();
- if (!TextUtils.isEmpty(summary)) {
- holder.summary.setText(summary);
- holder.summary.setVisibility(View.VISIBLE);
- } else {
- holder.summary.setVisibility(View.GONE);
- }
- } else {
- // Do not show summary if there are more than 1 suggestions
- holder.summary.setVisibility(View.GONE);
- holder.title.setMaxLines(3);
- }
-
- final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
- if (closeButton != null) {
- closeButton.setOnClickListener(v -> {
- mSuggestionFeatureProvider.dismissSuggestion(
- mContext, mSuggestionControllerMixin, suggestion);
- if (mCallback != null) {
- mCallback.onSuggestionClosed(suggestion);
- }
- });
- }
-
- View clickHandler = holder.itemView;
- // If a view with @android:id/primary is defined, use that as the click handler
- // instead.
- final View primaryAction = holder.itemView.findViewById(android.R.id.primary);
- if (primaryAction != null) {
- clickHandler = primaryAction;
- }
- clickHandler.setOnClickListener(v -> {
- mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_SUGGESTION, id);
- try {
- suggestion.getPendingIntent().send();
- mSuggestionControllerMixin.launchSuggestion(suggestion);
- } catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "Failed to start suggestion " + suggestion.getTitle());
- }
- });
- }
-
- @Override
- public long getItemId(int position) {
- return Objects.hash(mSuggestions.get(position).getId());
- }
-
- @Override
- public int getItemViewType(int position) {
- final Suggestion suggestion = getSuggestion(position);
- if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
- return R.layout.suggestion_tile_with_button_v2;
- } else {
- return R.layout.suggestion_tile_v2;
- }
- }
-
- @Override
- public int getItemCount() {
- return mSuggestions.size();
- }
-
- public Suggestion getSuggestion(int position) {
- final long itemId = getItemId(position);
- if (mSuggestions == null) {
- return null;
- }
- for (Suggestion suggestion : mSuggestions) {
- if (Objects.hash(suggestion.getId()) == itemId) {
- return suggestion;
- }
- }
- return null;
- }
-
- public void removeSuggestion(Suggestion suggestion) {
- final int position = mSuggestions.indexOf(suggestion);
- mSuggestions.remove(suggestion);
- notifyItemRemoved(position);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- if (mSuggestions != null) {
- outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
- new ArrayList<>(mSuggestions));
- }
- outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
- }
-
- public void setSuggestions(List<Suggestion> suggestions) {
- mSuggestions = suggestions;
- }
-
- public List<Suggestion> getSuggestions() {
- return mSuggestions;
- }
-
- private static class CardConfig {
- // Card start/end margin
- private final int mMarginInner;
- private final int mMarginOuter;
- // Card width for different numbers of cards
- private final int mWidthSingleCard;
- private final int mWidthTwoCards;
- private final int mWidthMultipleCards;
- // padding between icon and title
- private final int mPaddingTitleTopSingleCard;
- private final int mPaddingTitleTopMultipleCards;
-
- private static CardConfig sConfig;
-
- private CardConfig(Context context) {
- final Resources res = context.getResources();
- mMarginInner =
- res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
- mMarginOuter =
- res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
- mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
- mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
- mWidthMultipleCards =
- res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
- mPaddingTitleTopSingleCard =
- res.getDimensionPixelOffset(R.dimen.suggestion_card_title_padding_bottom_one_card);
- mPaddingTitleTopMultipleCards = res.getDimensionPixelOffset(
- R.dimen.suggestion_card_title_padding_bottom_multiple_cards);
- }
-
- public static CardConfig get(Context context) {
- if (sConfig == null) {
- sConfig = new CardConfig(context);
- }
- return sConfig;
- }
-
- private void setCardLayout(DashboardItemHolder holder, int suggestionCount,
- int position) {
- final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- suggestionCount == 1
- ? mWidthSingleCard : suggestionCount == 2
- ? mWidthTwoCards : mWidthMultipleCards,
- LinearLayout.LayoutParams.WRAP_CONTENT);
- if (suggestionCount == 1) {
- params.setMarginStart(mMarginOuter);
- params.setMarginEnd(mMarginOuter);
- } else {
- params.setMarginStart(
- position == 0 ? mMarginOuter : mMarginInner);
- params.setMarginEnd(position == suggestionCount - 1 ? mMarginOuter : 0);
- }
- holder.itemView.setLayoutParams(params);
- }
-
- }
-
-}
+++ /dev/null
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.dashboard.suggestions;
-
-import android.content.Context;
-import android.service.settings.suggestions.Suggestion;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
-
-import com.android.settings.R;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.suggestions.SuggestionControllerMixin;
-
-/**
- * Deprecated as a close button is provided to dismiss the suggestion.
- */
-@Deprecated
-public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback {
-
- public interface Callback {
- /**
- * Returns suggestion tile data from the callback
- */
- Suggestion getSuggestionAt(int position);
-
- /**
- * Called when a suggestion is dismissed.
- */
- void onSuggestionDismissed(Suggestion suggestion);
- }
-
- private final Context mContext;
- private final SuggestionFeatureProvider mSuggestionFeatureProvider;
- private final SuggestionControllerMixin mSuggestionMixin;
- private final Callback mCallback;
-
- public SuggestionDismissController(Context context, RecyclerView recyclerView,
- SuggestionControllerMixin suggestionMixin, Callback callback) {
- super(0, ItemTouchHelper.START | ItemTouchHelper.END);
- mSuggestionMixin = suggestionMixin;
- mContext = context;
- mSuggestionFeatureProvider = FeatureFactory.getFactory(context)
- .getSuggestionFeatureProvider(context);
- mCallback = callback;
- final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
- itemTouchHelper.attachToRecyclerView(recyclerView);
- }
-
- @Override
- public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
- RecyclerView.ViewHolder target) {
- return true;
- }
-
- @Override
- public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
- final int layoutId = viewHolder.getItemViewType();
- if (layoutId == R.layout.suggestion_tile
- || layoutId == R.layout.suggestion_tile_with_button) {
- // Only return swipe direction for suggestion tiles. All other types are not swipeable.
- return super.getSwipeDirs(recyclerView, viewHolder);
- }
- return 0;
- }
-
- @Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
- if (mCallback == null) {
- return;
- }
- final int position = viewHolder.getAdapterPosition();
- final Suggestion suggestionV2 = mCallback.getSuggestionAt(position);
- mSuggestionFeatureProvider.dismissSuggestion(mContext, mSuggestionMixin, suggestionV2);
- mCallback.onSuggestionDismissed(suggestionV2);
- }
-}
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.connecteddevice.usb.UsbBackend;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
private final String[] mListValues;
private final String[] mListSummaries;
private final UsbManager mUsbManager;
+ @VisibleForTesting
+ UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
private BroadcastReceiver mUsbReceiver;
private ListPreference mPreference;
mListValues = context.getResources().getStringArray(R.array.usb_configuration_values);
mListSummaries = context.getResources().getStringArray(R.array.usb_configuration_titles);
mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+ mUsbManagerPassThrough = new UsbBackend.UsbManagerPassThrough(mUsbManager);
mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
return false;
}
- writeUsbConfigurationOption(newValue.toString());
+ writeUsbConfigurationOption(mUsbManagerPassThrough
+ .usbFunctionsFromString(newValue.toString()));
updateUsbConfigurationValues();
return true;
}
}
@VisibleForTesting
- void setCurrentFunction(String newValue, boolean usbDataUnlocked) {
- mUsbManager.setCurrentFunction(newValue, usbDataUnlocked);
+ void setCurrentFunctions(long functions) {
+ mUsbManager.setCurrentFunctions(functions);
}
private void updateUsbConfigurationValues() {
+ long functions = mUsbManagerPassThrough.getCurrentFunctions();
int index = 0;
for (int i = 0; i < mListValues.length; i++) {
- if (mUsbManager.isFunctionEnabled(mListValues[i])) {
+ if (functions == mUsbManagerPassThrough.usbFunctionsFromString(mListValues[i])) {
index = i;
break;
}
mPreference.setSummary(mListSummaries[index]);
}
- private void writeUsbConfigurationOption(String newValue) {
- if (TextUtils.equals(newValue, "none")) {
- setCurrentFunction(newValue, false);
- } else {
- setCurrentFunction(newValue, true);
- }
+ private void writeUsbConfigurationOption(long newValue) {
+ setCurrentFunctions(newValue);
}
}
@Override
public boolean isAvailable() {
- return true;
+ return mTelephonyManager.isVoiceCapable();
}
@Override
@Override
public void onPause() {
- mIm.registerInputDeviceListener(this, null);
+ mIm.unregisterInputDeviceListener(this);
}
@Override
public void onResume() {
- mIm.unregisterInputDeviceListener(this);
+ mIm.registerInputDeviceListener(this, null);
}
@Override
import com.android.settings.DateTimeSettings;
import com.android.settings.DisplaySettings;
import com.android.settings.LegalSettings;
+import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
+import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
import com.android.settings.accessibility.MagnificationPreferenceFragment;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
-import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
+import com.android.settings.connecteddevice.usb.UsbDetailsFragment;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
import com.android.settings.development.DevelopmentSettingsDashboardFragment;
addIndex(PowerUsageSummary.class);
addIndex(BatterySaverSettings.class);
addIndex(LockscreenDashboardFragment.class);
+ addIndex(UsbDetailsFragment.class);
addIndex(WifiDisplaySettings.class);
addIndex(ZenModeBehaviorSettings.class);
addIndex(ZenModeAutomationSettings.class);
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.system;
import android.content.Context;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
public class AdditionalSystemUpdatePreferenceController extends BasePreferenceController {
import com.android.settings.R;
import com.android.settings.backup.BackupSettingsActivityPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.deviceinfo.AdditionalSystemUpdatePreferenceController;
-import com.android.settings.deviceinfo.SystemUpdatePreferenceController;
import com.android.settings.gestures.GesturesSettingPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.system;
import static android.content.Context.CARRIER_CONFIG_SERVICE;
+import static android.content.Context.SYSTEM_UPDATE_SERVICE;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
+import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.SystemUpdateManager;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
private final UserManager mUm;
+ private final SystemUpdateManager mUpdateManager;
public SystemUpdatePreferenceController(Context context) {
super(context, KEY_SYSTEM_UPDATE_SETTINGS);
mUm = UserManager.get(context);
+ mUpdateManager = (SystemUpdateManager) context.getSystemService(SYSTEM_UPDATE_SERVICE);
}
@Override
@Override
public String getSummary() {
- return mContext.getString(R.string.about_summary, Build.VERSION.RELEASE);
+ final Bundle updateInfo = mUpdateManager.retrieveSystemUpdateInfo();
+ String summary = mContext.getString(R.string.android_version_summary,
+ Build.VERSION.RELEASE);
+ switch (updateInfo.getInt(SystemUpdateManager.KEY_STATUS)) {
+ case SystemUpdateManager.STATUS_WAITING_DOWNLOAD:
+ case SystemUpdateManager.STATUS_IN_PROGRESS:
+ case SystemUpdateManager.STATUS_WAITING_INSTALL:
+ case SystemUpdateManager.STATUS_WAITING_REBOOT:
+ summary = mContext.getString(R.string.android_version_pending_update_summary);
+ break;
+ case SystemUpdateManager.STATUS_UNKNOWN:
+ Log.d(TAG, "Update statue unknown");
+ // fall through to next branch
+ case SystemUpdateManager.STATUS_IDLE:
+ final String version = updateInfo.getString(SystemUpdateManager.KEY_TITLE);
+ if (!TextUtils.isEmpty(version)) {
+ summary = mContext.getString(R.string.android_version_summary, version);
+ }
+ break;
+ }
+ return summary;
}
/**
boolean mCanAddGuest;
boolean mDisallowAddUser;
boolean mDisallowAddUserSetByAdmin;
+ boolean mDisallowSwitchUser;
RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
private UserCapabilities() {}
final boolean canAddUsersWhenLocked = mIsAdmin || Settings.Global.getInt(
context.getContentResolver(), Settings.Global.ADD_USERS_WHEN_LOCKED, 0) == 1;
mCanAddGuest = !mIsGuest && !mDisallowAddUser && canAddUsersWhenLocked;
+
+ UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mDisallowSwitchUser = userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
}
public boolean isAdmin() {
", mCanAddGuest=" + mCanAddGuest +
", mDisallowAddUser=" + mDisallowAddUser +
", mEnforcedAdmin=" + mEnforcedAdmin +
+ ", mDisallowSwitchUser=" + mDisallowSwitchUser +
'}';
}
}
mAddUser.useAdminDisabledSummary(false);
// Determine if add user/profile button should be visible
if (mUserCaps.mCanAddUser && Utils.isDeviceProvisioned(getActivity())) {
+ mAddUser.setVisible(true);
mAddUser.setOnPreferenceClickListener(this);
// change label to only mention user, if restricted profiles are not supported
if (!mUserCaps.mCanAddRestrictedProfile) {
mAddUser.setTitle(R.string.user_add_user_menu);
}
+ } else {
+ mAddUser.setVisible(false);
}
final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
public void onDestroy() {
super.onDestroy();
- if (!mUserCaps.mEnabled) {
+ if (mUserCaps == null || !mUserCaps.mEnabled) {
return;
}
synchronized (mUserLock) {
if (userType == USER_TYPE_USER) {
mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
- mHandler.sendMessage(mHandler.obtainMessage(
- MESSAGE_SETUP_USER, user.id, user.serialNumber));
+ // Skip setting up user which results in user switching when the
+ // restriction is set.
+ if (!mUserCaps.mDisallowSwitchUser) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MESSAGE_SETUP_USER, user.id, user.serialNumber));
+ }
} else {
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_CONFIG_USER, user.id, user.serialNumber));
} else {
pref.setSummary(R.string.user_summary_not_set_up);
}
- pref.setOnPreferenceClickListener(this);
- pref.setSelectable(true);
+ // Disallow setting up user which results in user switching when the restriction is
+ // set.
+ if (!mUserCaps.mDisallowSwitchUser) {
+ pref.setOnPreferenceClickListener(this);
+ pref.setSelectable(true);
+ }
} else if (user.isRestricted()) {
pref.setSummary(R.string.user_summary_restricted_profile);
}
pref.setTitle(R.string.user_guest);
pref.setIcon(getEncircledDefaultIcon());
userPreferences.add(pref);
- pref.setDisabledByAdmin(
- mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null);
+ if (mUserCaps.mDisallowAddUser) {
+ pref.setDisabledByAdmin(mUserCaps.mEnforcedAdmin);
+ } else if (mUserCaps.mDisallowSwitchUser) {
+ pref.setDisabledByAdmin(RestrictedLockUtils.getDeviceOwner(context));
+ } else {
+ pref.setDisabledByAdmin(null);
+ }
int finalGuestId = guestId;
pref.setOnPreferenceClickListener(preference -> {
int id = finalGuestId;
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.widget;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceGroupAdapter;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.View;
+
+import com.android.settings.R;
+
+public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
+
+ @VisibleForTesting
+ static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 600L;
+ private static final long HIGHLIGHT_DURATION = 5000L;
+
+ private final int mHighlightColor;
+ private final int mNormalBackgroundRes;
+ private final String mHighlightKey;
+
+ private boolean mHighlightRequested;
+ private int mHighlightPosition = RecyclerView.NO_POSITION;
+
+ public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup, String key,
+ boolean highlightRequested) {
+ super(preferenceGroup);
+ mHighlightKey = key;
+ mHighlightRequested = highlightRequested;
+ final Context context = preferenceGroup.getContext();
+ final TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ outValue, true /* resolveRefs */);
+ mNormalBackgroundRes = outValue.resourceId;
+ mHighlightColor = context.getColor(R.color.preference_highligh_color);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder, int position) {
+ super.onBindViewHolder(holder, position);
+ updateBackground(holder, position);
+ }
+
+ @VisibleForTesting
+ void updateBackground(PreferenceViewHolder holder, int position) {
+ View v = holder.itemView;
+ if (position == mHighlightPosition) {
+ v.setBackgroundColor(mHighlightColor);
+ v.setTag(R.id.preference_highlighted, true);
+ v.postDelayed(() -> {
+ mHighlightPosition = RecyclerView.NO_POSITION;
+ removeHighlightBackground(v);
+ }, HIGHLIGHT_DURATION);
+ } else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) {
+ removeHighlightBackground(v);
+ }
+ }
+
+ public void requestHighlight(View root, RecyclerView recyclerView) {
+ if (mHighlightRequested || recyclerView == null || TextUtils.isEmpty(mHighlightKey)) {
+ return;
+ }
+ root.postDelayed(() -> {
+ final int position = getPreferenceAdapterPosition(mHighlightKey);
+ if (position < 0) {
+ return;
+ }
+ mHighlightRequested = true;
+ recyclerView.getLayoutManager().scrollToPosition(position);
+ mHighlightPosition = position;
+ notifyItemChanged(position);
+ }, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ }
+
+ public boolean isHighlightRequested() {
+ return mHighlightRequested;
+ }
+
+ private void removeHighlightBackground(View v) {
+ v.setBackgroundResource(mNormalBackgroundRes);
+ v.setTag(R.id.preference_highlighted, false);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 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 android.hardware.usb;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
+
+/**
+ * Definitions that were added to UsbManager in P.
+ *
+ * Copied partially from frameworks/base/core/java/android/hardware/usb/UsbManager to
+ * fix issues with roboelectric during test.
+ */
+@SystemService(Context.USB_SERVICE)
+public class UsbManagerExtras {
+ public static final long NONE = 0;
+ public static final long MTP = GadgetFunction.MTP;
+ public static final long PTP = GadgetFunction.PTP;
+ public static final long RNDIS = GadgetFunction.RNDIS;
+ public static final long MIDI = GadgetFunction.MIDI;
+ public static final long ACCESSORY = GadgetFunction.ACCESSORY;
+ public static final long AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
+ public static final long ADB = GadgetFunction.ADB;
+
+ private static final long SETTABLE_FUNCTIONS = MTP | PTP | RNDIS | MIDI;
+
+ private static final Map<String, Long> STR_MAP = new HashMap<>();
+
+ static {
+ STR_MAP.put(UsbManager.USB_FUNCTION_MTP, MTP);
+ STR_MAP.put(UsbManager.USB_FUNCTION_PTP, PTP);
+ STR_MAP.put(UsbManager.USB_FUNCTION_RNDIS, RNDIS);
+ STR_MAP.put(UsbManager.USB_FUNCTION_MIDI, MIDI);
+ STR_MAP.put(UsbManager.USB_FUNCTION_ACCESSORY, ACCESSORY);
+ STR_MAP.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, AUDIO_SOURCE);
+ STR_MAP.put(UsbManager.USB_FUNCTION_ADB, ADB);
+ }
+
+ /**
+ * Returns whether the given functions are valid inputs to UsbManager.
+ * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+ */
+ public static boolean isSettableFunctions(long functions) {
+ return (~SETTABLE_FUNCTIONS & functions) == 0;
+ }
+
+ /**
+ * Returns the string representation of the given functions.
+ */
+ public static String usbFunctionsToString(long functions) {
+ StringJoiner joiner = new StringJoiner(",");
+ if ((functions | MTP) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_MTP);
+ }
+ if ((functions | PTP) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_PTP);
+ }
+ if ((functions | RNDIS) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_RNDIS);
+ }
+ if ((functions | MIDI) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_MIDI);
+ }
+ if ((functions | ACCESSORY) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_ACCESSORY);
+ }
+ if ((functions | AUDIO_SOURCE) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
+ }
+ if ((functions | ADB) != 0) {
+ joiner.add(UsbManager.USB_FUNCTION_ADB);
+ }
+ return joiner.toString();
+ }
+
+ /**
+ * Parses a string of usb functions and returns a mask of the same functions.
+ */
+ public static long usbFunctionsFromString(String functions) {
+ if (functions == null) {
+ return 0;
+ }
+ long ret = 0;
+ for (String function : functions.split(",")) {
+ if (STR_MAP.containsKey(function)) {
+ ret |= STR_MAP.get(function);
+ }
+ }
+ return ret;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Duplicate class for platform SystemUpdateManager to get around Robolectric sdk problem.
+ */
+public class SystemUpdateManager {
+
+ public static final String KEY_STATUS = "status";
+ public static final String KEY_TITLE = "title";
+
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_IDLE = 1;
+ public static final int STATUS_WAITING_DOWNLOAD = 2;
+ public static final int STATUS_IN_PROGRESS = 3;
+ public static final int STATUS_WAITING_INSTALL = 4;
+ public static final int STATUS_WAITING_REBOOT = 5;
+
+ public Bundle retrieveSystemUpdateInfo() {
+ return null;
+ }
+}
import android.provider.SearchIndexableResource;
import android.text.TextUtils;
+import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settingslib.accounts.AuthenticatorHelper;
import com.android.settingslib.drawer.CategoryKey;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowApplication;
import java.util.List;
mFragment = new AccountDashboardFragment();
}
+ @After
+ public void tearDown() {
+ ShadowAuthenticationHelper.reset();
+ }
+
@Test
public void testCategory_isAccount() {
assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_ACCOUNT);
@Config(shadows = {
ShadowAuthenticationHelper.class
})
- public void updateSummary_shouldDisplayUpTo3AccountTypes() {
+ public void updateSummary_hasAccount_shouldDisplayUpTo3AccountTypes() {
+ ShadowAuthenticationHelper.setHasAccount(true);
final SummaryLoader loader = mock(SummaryLoader.class);
final Activity activity = Robolectric.buildActivity(Activity.class).setup().get();
}
@Test
+ @Config(shadows = {
+ ShadowAuthenticationHelper.class
+ })
+ public void updateSummary_noAccount_shouldDisplayDefaultSummary() {
+ ShadowAuthenticationHelper.setHasAccount(false);
+ final SummaryLoader loader = mock(SummaryLoader.class);
+ final Activity activity = Robolectric.buildActivity(Activity.class).setup().get();
+
+ final SummaryLoader.SummaryProvider provider = mFragment.SUMMARY_PROVIDER_FACTORY
+ .createSummaryProvider(activity, loader);
+ provider.setListening(true);
+
+ verify(loader).setSummary(provider,
+ activity.getString(R.string.account_dashboard_default_summary));
+ }
+
+ @Test
public void testSearchIndexProvider_shouldIndexResource() {
final List<SearchIndexableResource> indexRes =
AccountDashboardFragment.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(
static final String[] TYPES = new String[] {"type1", "type2", "type3", "type4"};
static final String[] LABELS = new String[] {"LABEL1", "LABEL2",
"LABEL3", "LABEL4"};
+ private static boolean sHasAccount = true;
public void __constructor__(Context context, UserHandle userHandle,
AuthenticatorHelper.OnAccountsUpdateListener listener) {
+ }
+
+ public static void setHasAccount(boolean hasAccount) {
+ sHasAccount = hasAccount;
+ }
+ @Resetter
+ public static void reset() {
+ sHasAccount = true;
}
@Implementation
public String[] getEnabledAccountTypes() {
- return TYPES;
+ return sHasAccount ? TYPES : null;
}
@Implementation
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
}
@Test
+ public void onActivityResult_packageUninstalled_shouldFinishAndRemoveTask() {
+ doReturn(false).when(mFragment).refreshUi();
+
+ mFragment.onActivityResult(mFragment.REQUEST_UNINSTALL, 0, mock(Intent.class));
+
+ verify(mActivity).finishAndRemoveTask();
+ }
+
+ @Test
+ public void getPreferenceControllers_noPackageInfo_shouldReturnNull() {
+ doNothing().when(mFragment).retrieveAppEntry();
+
+ assertThat(mFragment.getPreferenceControllers(mShadowContext)).isNull();
+ }
+
+ @Test
public void getNumberOfUserWithPackageInstalled_twoUsersInstalled_shouldReturnTwo()
throws PackageManager.NameNotFoundException{
final String packageName = "Package1";
import com.android.settings.TestConfig;
import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.TestConfig;
-import com.android.settings.deviceinfo.UsbBackend;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
private ConnectedUsbDeviceUpdater mDeviceUpdater;
@Mock
+ private DashboardFragment mFragment;
+ @Mock
private UsbConnectionBroadcastReceiver mUsbReceiver;
@Mock
private DevicePreferenceCallback mDevicePreferenceCallback;
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mDeviceUpdater = new ConnectedUsbDeviceUpdater(mContext, mDevicePreferenceCallback,
+ when(mFragment.getContext()).thenReturn(mContext);
+ mDeviceUpdater = new ConnectedUsbDeviceUpdater(mFragment, mDevicePreferenceCallback,
mUsbBackend);
mDeviceUpdater.mUsbReceiver = mUsbReceiver;
}
@Test
public void testInitUsbPreference_usbConnected_preferenceAdded() {
- doReturn(true).when(mUsbReceiver).isConnected();
-
mDeviceUpdater.initUsbPreference(mContext);
+ mDeviceUpdater.mUsbConnectionListener.onUsbConnectionChanged(true /* connected */,
+ UsbBackend.MODE_DATA_NONE);
verify(mDevicePreferenceCallback).onDeviceAdded(mDeviceUpdater.mUsbPreference);
}
@Test
public void testInitUsbPreference_usbDisconnected_preferenceRemoved() {
- doReturn(false).when(mUsbReceiver).isConnected();
-
mDeviceUpdater.initUsbPreference(mContext);
+ mDeviceUpdater.mUsbConnectionListener.onUsbConnectionChanged(false /* connected */,
+ UsbBackend.MODE_DATA_NONE);
verify(mDevicePreferenceCallback).onDeviceRemoved(mDeviceUpdater.mUsbPreference);
}
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.connecteddevice.usb;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.argThat;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
+import com.android.settings.connecteddevice.usb.UsbBackend;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
private UsbManager mUsbManager;
@Mock
private UsbBackend.UserRestrictionUtil mUserRestrictionUtil;
+ @Mock
+ private ConnectivityManager mConnectivityManager;
@Before
public void setUp() {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI))
.thenReturn(true);
when((Object)mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
+ when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+ .thenReturn((Object) mConnectivityManager);
}
@Test
public void constructor_noUsbPort_shouldNotCrash() {
- UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil);
+ UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil, null);
// Should not crash
}
-
- @Test
- public void getCurrentMode_shouldRegisterReceiverToGetUsbState() {
- UsbBackend usbBackend = new UsbBackend(mContext, mUserRestrictionUtil);
-
- usbBackend.getCurrentMode();
-
- verify(mContext).registerReceiver(eq(null),
- argThat(intentFilter -> intentFilter != null &&
- UsbManager.ACTION_USB_STATE.equals(intentFilter.getAction(0))));
- }
}
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@Mock
private UsbConnectionBroadcastReceiver.UsbConnectionListener mListener;
+ @Mock
+ private UsbBackend mUsbBackend;
@Before
public void setUp() {
mShadowApplication = ShadowApplication.getInstance();
mContext = RuntimeEnvironment.application;
- mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener);
+ mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener, mUsbBackend);
}
@Test
public void testOnReceive_usbConnected_invokeCallback() {
final Intent intent = new Intent();
+ intent.setAction(UsbManager.ACTION_USB_STATE);
intent.putExtra(UsbManager.USB_CONNECTED, true);
mReceiver.onReceive(mContext, intent);
- verify(mListener).onUsbConnectionChanged(true);
+ verify(mListener).onUsbConnectionChanged(true /* connected */, UsbBackend.MODE_DATA_NONE);
}
@Test
public void testOnReceive_usbDisconnected_invokeCallback() {
final Intent intent = new Intent();
+ intent.setAction(UsbManager.ACTION_USB_STATE);
intent.putExtra(UsbManager.USB_CONNECTED, false);
mReceiver.onReceive(mContext, intent);
- verify(mListener).onUsbConnectionChanged(false);
+ verify(mListener).onUsbConnectionChanged(false /* connected */, UsbBackend.MODE_DATA_NONE);
+ }
+
+ @Test
+ public void testOnReceive_usbConnectedMtpEnabled_invokeCallback() {
+ final Intent intent = new Intent();
+ intent.setAction(UsbManager.ACTION_USB_STATE);
+ intent.putExtra(UsbManager.USB_CONNECTED, true);
+ intent.putExtra(UsbManager.USB_FUNCTION_MTP, true);
+ intent.putExtra(UsbManager.USB_DATA_UNLOCKED, true);
+
+ mReceiver.onReceive(mContext, intent);
+
+ verify(mListener).onUsbConnectionChanged(true /* connected */, UsbBackend.MODE_DATA_MTP);
}
@Test
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.usb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.Context;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v14.preference.PreferenceFragment;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.After;
+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.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+ shadows = {ShadowEntityHeaderController.class, SettingsShadowResources.class})
+public class UsbDetailsHeaderControllerTest {
+
+ private UsbDetailsHeaderController mDetailsHeaderController;
+ private Context mContext;
+ private Lifecycle mLifecycle;
+ private LifecycleOwner mLifecycleOwner;
+ private LayoutPreference mPreference;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceScreen mScreen;
+
+ @Mock
+ private UsbBackend mUsbBackend;
+ @Mock
+ private PreferenceFragment mFragment;
+ @Mock
+ private Activity mActivity;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private EntityHeaderController mHeaderController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ mPreferenceManager = new PreferenceManager(mContext);
+ mScreen = mPreferenceManager.createPreferenceScreen(mContext);
+
+ when(mFragment.getActivity()).thenReturn(mActivity);
+ when(mActivity.getApplicationContext()).thenReturn(mContext);
+ when(mFragment.getContext()).thenReturn(mContext);
+ when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
+ when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+ ShadowEntityHeaderController.setUseMock(mHeaderController);
+ mDetailsHeaderController = new UsbDetailsHeaderController(mContext, mFragment, mUsbBackend);
+ mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
+ mPreference.setKey(mDetailsHeaderController.getPreferenceKey());
+ mScreen.addPreference(mPreference);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowEntityHeaderController.reset();
+ }
+
+ @Test
+ public void displayRefresh_charging_shouldSetHeader() {
+ mDetailsHeaderController.displayPreference(mScreen);
+ mDetailsHeaderController.refresh(UsbBackend.MODE_DATA_NONE);
+ verify(mHeaderController).setLabel(mContext.getString(R.string.usb_pref));
+ verify(mHeaderController).setIcon(mContext.getDrawable(R.drawable.ic_usb));
+ verify(mHeaderController).setSummary(
+ mContext.getString(R.string.usb_summary_charging_only));
+ verify(mHeaderController).done(mActivity, true);
+ }
+
+ @Test
+ public void displayRefresh_mtp_shouldSetHeader() {
+ mDetailsHeaderController.displayPreference(mScreen);
+ mDetailsHeaderController.refresh(UsbBackend.MODE_DATA_MTP);
+ verify(mHeaderController).setLabel(mContext.getString(R.string.usb_pref));
+ verify(mHeaderController).setIcon(mContext.getDrawable(R.drawable.ic_usb));
+ verify(mHeaderController).setSummary(
+ mContext.getString(R.string.usb_summary_file_transfers));
+ verify(mHeaderController).done(mActivity, true);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.usb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import com.google.android.collect.Lists;
+
+import org.junit.After;
+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.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UsbDetailsProfilesControllerTest {
+
+ private UsbDetailsProfilesController mDetailsProfilesController;
+ private Context mContext;
+ private Lifecycle mLifecycle;
+ private PreferenceCategory mPreference;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceScreen mScreen;
+ private List<String> mOptions;
+
+ @Mock
+ private UsbBackend mUsbBackend;
+ @Mock
+ private PreferenceFragment mFragment;
+ @Mock
+ private Activity mActivity;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mLifecycle = new Lifecycle(() -> mLifecycle);
+ mPreferenceManager = new PreferenceManager(mContext);
+ mScreen = mPreferenceManager.createPreferenceScreen(mContext);
+
+ when(mFragment.getActivity()).thenReturn(mActivity);
+ when(mActivity.getApplicationContext()).thenReturn(mContext);
+ when(mFragment.getContext()).thenReturn(mContext);
+ when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
+ when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+ mOptions = Lists.newArrayList(UsbManager.USB_FUNCTION_MTP, UsbManager.USB_FUNCTION_PTP,
+ UsbManager.USB_FUNCTION_MIDI, UsbDetailsProfilesController.KEY_POWER);
+ mDetailsProfilesController = new UsbDetailsProfilesController(mContext, mFragment,
+ mUsbBackend, mOptions, "usb_options");
+ mPreference = new PreferenceCategory(mContext);
+ mPreference.setKey(mDetailsProfilesController.getPreferenceKey());
+ mScreen.addPreference(mPreference);
+ }
+
+ @Test
+ public void testDisplayRefresh_allAllowed_shouldCreateSwitches() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_NONE);
+ List<SwitchPreference> switches = getProfileSwitches();
+
+ for (int i = 0; i < switches.size(); i++) {
+ assertThat(switches.get(i).getKey().equals(mOptions.get(i)));
+ }
+ }
+
+ @Test
+ public void testDisplayRefresh_onlyMidiAllowed_shouldCreateOnlyMidiSwitch() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_DATA_MIDI)).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_DATA_MTP)).thenReturn(true);
+ when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_DATA_PTP)).thenReturn(true);
+ when(mUsbBackend.isModeDisallowedBySystem(UsbBackend.MODE_POWER_SOURCE)).thenReturn(true);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_NONE);
+ List<SwitchPreference> switches = getProfileSwitches();
+ assertThat(switches.size()).isEqualTo(1);
+ assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MIDI);
+ }
+
+ @Test
+ public void testDisplayRefresh_mtpEnabled_shouldCheckSwitches() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_MTP);
+ List<SwitchPreference> switches = getProfileSwitches();
+
+ assertThat(switches.get(0).getKey().equals(UsbManager.USB_FUNCTION_MTP));
+ assertThat(switches.get(0).isChecked());
+ }
+
+ @Test
+ public void testDisplayRefresh_mtpSupplyPowerEnabled_shouldCheckSwitches() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_MTP | UsbBackend.MODE_POWER_SOURCE);
+ List<SwitchPreference> switches = getProfileSwitches();
+
+ assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+ assertThat(switches.get(0).isChecked());
+ assertThat(switches.get(3).getKey()).isEqualTo(UsbDetailsProfilesController.KEY_POWER);
+ assertThat(switches.get(3).isChecked());
+ }
+
+ @Test
+ public void testOnClickMtp_noneEnabled_shouldEnableMtp() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_NONE);
+ List<SwitchPreference> switches = getProfileSwitches();
+ switches.get(0).performClick();
+
+ assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+ verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_MTP);
+ assertThat(switches.get(0).isChecked());
+ }
+
+ @Test
+ public void testOnClickMtp_supplyingPowerEnabled_shouldEnableBoth() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_POWER_SOURCE);
+ when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SOURCE);
+ List<SwitchPreference> switches = getProfileSwitches();
+ switches.get(0).performClick();
+
+ assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+ verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_MTP | UsbBackend.MODE_POWER_SOURCE);
+ assertThat(switches.get(0).isChecked());
+ assertThat(switches.get(3).getKey()).isEqualTo(UsbDetailsProfilesController.KEY_POWER);
+ assertThat(switches.get(3).isChecked());
+ }
+
+ @Test
+ public void testOnClickMtp_ptpEnabled_shouldEnableMtpOnly() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_PTP);
+ when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_DATA_PTP);
+ List<SwitchPreference> switches = getProfileSwitches();
+ switches.get(0).performClick();
+
+ assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+ verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_MTP);
+ assertThat(switches.get(0).isChecked());
+ assertThat(switches.get(1).getKey()).isEqualTo(UsbManager.USB_FUNCTION_PTP);
+ assertThat(!switches.get(1).isChecked());
+ }
+
+ @Test
+ public void testOnClickMtp_mtpEnabled_shouldDisableMtp() {
+ when(mUsbBackend.isModeSupported(anyInt())).thenReturn(true);
+ when(mUsbBackend.isModeDisallowed(anyInt())).thenReturn(false);
+ when(mUsbBackend.isModeDisallowedBySystem(anyInt())).thenReturn(false);
+
+ mDetailsProfilesController.displayPreference(mScreen);
+ mDetailsProfilesController.refresh(UsbBackend.MODE_DATA_MTP);
+ when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_DATA_MTP);
+ List<SwitchPreference> switches = getProfileSwitches();
+ switches.get(0).performClick();
+
+ assertThat(switches.get(0).getKey()).isEqualTo(UsbManager.USB_FUNCTION_MTP);
+ verify(mUsbBackend).setMode(UsbBackend.MODE_DATA_NONE);
+ assertThat(!switches.get(0).isChecked());
+ }
+
+ private List<SwitchPreference> getProfileSwitches() {
+ ArrayList<SwitchPreference> result = new ArrayList<>();
+ for (int i = 0; i < mPreference.getPreferenceCount(); i++) {
+ result.add((SwitchPreference) mPreference.getPreference(i));
+ }
+ return result;
+ }
+}
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.connecteddevice.usb;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
import android.widget.TextView;
import com.android.settings.R;
+import com.android.settings.connecteddevice.usb.UsbModeChooserActivity;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import org.junit.Before;
-package com.android.settings.connecteddevice;
+package com.android.settings.connecteddevice.usb;
import android.content.Context;
-import android.content.Intent;
-import android.hardware.usb.UsbManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
-import com.android.settings.deviceinfo.UsbBackend;
-import com.android.settings.deviceinfo.UsbModeChooserActivity;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
private UsbBackend mUsbBackend;
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
+ @Mock
+ private UsbConnectionBroadcastReceiver mUsbConnectionBroadcastReceiver;
private Context mContext;
private UsbModePreferenceController mController;
MockitoAnnotations.initMocks(this);
mContext = ShadowApplication.getInstance().getApplicationContext();
mController = new UsbModePreferenceController(mContext, mUsbBackend);
+ mController.mUsbReceiver = mUsbConnectionBroadcastReceiver;
}
@Test
public void testGetSummary_chargeDevice() {
- assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[0]))
+ assertThat(mController.getSummary(0))
.isEqualTo(R.string.usb_summary_charging_only);
}
@Test
public void testGetSummary_supplyPower() {
- assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[1]))
+ assertThat(mController.getSummary(UsbBackend.MODE_POWER_SOURCE))
.isEqualTo(R.string.usb_summary_power_only);
}
@Test
public void testGetSummary_TransferFiles() {
- assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[2]))
+ assertThat(mController.getSummary(UsbBackend.MODE_DATA_MTP))
.isEqualTo(R.string.usb_summary_file_transfers);
}
@Test
public void testGetSummary_TransferPhoto() {
- assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[3]))
+ assertThat(mController.getSummary(UsbBackend.MODE_DATA_PTP))
.isEqualTo(R.string.usb_summary_photo_transfers);
}
@Test
public void testGetSummary_MIDI() {
- assertThat(mController.getSummary(UsbModeChooserActivity.DEFAULT_MODES[4]))
+ assertThat(mController.getSummary(UsbBackend.MODE_DATA_MIDI))
.isEqualTo(R.string.usb_summary_MIDI);
}
@Test
+ public void testGetSummary_Tethering() {
+ assertThat(mController.getSummary(UsbBackend.MODE_DATA_TETHER))
+ .isEqualTo(R.string.usb_summary_tether);
+ }
+
+ @Test
public void testPreferenceSummary_usbDisconnected() {
final Preference preference = new Preference(mContext);
preference.setKey("usb_mode");
preference.setEnabled(true);
+ when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SINK);
+ when(mUsbConnectionBroadcastReceiver.isConnected()).thenReturn(false);
mController.updateState(preference);
+
+ assertThat(preference.getKey()).isEqualTo("usb_mode");
assertThat(preference.getSummary()).isEqualTo(
mContext.getString(R.string.disconnected));
}
@Test
- public void testUsbBoradcastReceiver_usbConnected_shouldUpdateSummary() {
+ public void testUsbBroadcastReceiver_usbConnected_shouldUpdateSummary() {
final Preference preference = new Preference(mContext);
preference.setKey("usb_mode");
preference.setEnabled(true);
- when(mUsbBackend.getCurrentMode()).thenReturn(UsbModeChooserActivity.DEFAULT_MODES[0]);
- when(mScreen.findPreference("usb_mode")).thenReturn(preference);
-
- mController.displayPreference(mScreen);
- mController.onResume();
- final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
- intent.putExtra(UsbManager.USB_CONNECTED, true);
- mContext.sendStickyBroadcast(intent);
+ when(mUsbBackend.getCurrentMode()).thenReturn(UsbBackend.MODE_POWER_SINK);
+ when(mUsbConnectionBroadcastReceiver.isConnected()).thenReturn(true);
+ mController.updateState(preference);
assertThat(preference.getSummary()).isEqualTo(
mContext.getString(R.string.usb_summary_charging_only));
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package com.android.settings.dashboard;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import org.junit.Before;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
private Resources mResources;
private FakeFeatureFactory mFactory;
private DashboardAdapter mDashboardAdapter;
- private DashboardAdapter.SuggestionAndConditionHeaderHolder mSuggestionHolder;
- private DashboardData.SuggestionConditionHeaderData mSuggestionHeaderData;
private List<Condition> mConditionList;
@Before
mConditionList = new ArrayList<>();
mConditionList.add(mCondition);
when(mCondition.shouldShow()).thenReturn(true);
- mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null);
- mSuggestionHeaderData = new DashboardData.SuggestionConditionHeaderData(mConditionList, 1);
+ mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
+ mConditionList, null /* suggestionControllerMixin */, null /* lifecycle */);
when(mView.getTag()).thenReturn(mCondition);
}
@Test
- public void testSuggestionsLogs_nullSuggestionsList_shouldNotCrash() {
- setupSuggestions(makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4", "pkg5"));
- mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
-
- // set suggestions to null
- final DashboardData prevData = mDashboardAdapter.mDashboardData;
- mDashboardAdapter.mDashboardData = new DashboardData.Builder(prevData)
- .setSuggestions(null)
- .build();
-
- mSuggestionHolder.itemView.callOnClick();
- // no crash
- }
-
- @Test
public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
final DashboardAdapter adapter =
- spy(new DashboardAdapter(mContext, null, null, null, null));
+ spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */, null /*
+ lifecycle */));
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
adapter.setSuggestions(suggestions);
when(data.getContext()).thenReturn(mContext);
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
final View itemView = mock(View.class);
- when(itemView.findViewById(R.id.data)).thenReturn(data);
- final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
- new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
+ when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
+ when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
+ final DashboardAdapter.SuggestionContainerHolder holder =
+ new DashboardAdapter.SuggestionContainerHolder(itemView);
- adapter.onBindConditionAndSuggestion(
- holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
+ adapter.onBindSuggestion(holder, 0);
final DashboardData dashboardData = adapter.mDashboardData;
reset(adapter); // clear interactions tracking
final Suggestion suggestionToRemove = suggestions.get(1);
- adapter.onSuggestionDismissed(suggestionToRemove);
+ adapter.onSuggestionClosed(suggestionToRemove);
assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
assertThat(suggestions.size()).isEqualTo(2);
}
@Test
- public void testSuggestionDismissed_moreThanTwoSuggestions_defaultMode_shouldNotCrash() {
+ public void testSuggestionDismissed_moreThanTwoSuggestions_shouldNotCrash() {
final RecyclerView data = new RecyclerView(RuntimeEnvironment.application);
final View itemView = mock(View.class);
- when(itemView.findViewById(R.id.data)).thenReturn(data);
- final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
- new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
+ when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
+ when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
+ final DashboardAdapter.SuggestionContainerHolder holder =
+ new DashboardAdapter.SuggestionContainerHolder(itemView);
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4");
- final DashboardAdapter adapter = spy(new DashboardAdapter(mContext, null /*savedInstance */,
- null /* conditions */,
- null /* suggestionControllerMixin */, null /* callback */));
+ final DashboardAdapter adapter = spy(new DashboardAdapter(mContext,
+ null /*savedInstance */, null /* conditions */,
+ null /* suggestionControllerMixin */,
+ null /* lifecycle */));
adapter.setSuggestions(suggestions);
- adapter.onBindConditionAndSuggestion(
- holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
- // default mode, only displaying 2 suggestions
+ adapter.onBindSuggestion(holder, 0);
- adapter.onSuggestionDismissed(suggestions.get(1));
+ adapter.onSuggestionClosed(suggestions.get(1));
// verify operations that access the lists will not cause ConcurrentModificationException
- assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(1);
+ assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(3);
adapter.setSuggestions(suggestions);
// should not crash
}
@Test
public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
DashboardAdapter adapter =
- spy(new DashboardAdapter(mContext, null, null, null, null));
+ spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */, null /*
+ lifecycle */));
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
adapter.setSuggestions(suggestions);
final DashboardData dashboardData = adapter.mDashboardData;
reset(adapter); // clear interactions tracking
- adapter.onSuggestionDismissed(suggestions.get(0));
+ adapter.onSuggestionClosed(suggestions.get(0));
assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
verify(adapter).notifyDashboardDataChanged(any());
}
@Test
- public void testSetCategories_iconTinted() {
- TypedArray mockTypedArray = mock(TypedArray.class);
- doReturn(mockTypedArray).when(mContext).obtainStyledAttributes(any(int[].class));
- doReturn(0x89000000).when(mockTypedArray).getColor(anyInt(), anyInt());
+ public void testBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
+ mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
+ final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
- final DashboardCategory category = new DashboardCategory();
- final Icon mockIcon = mock(Icon.class);
- final Tile tile = new Tile();
- tile.isIconTintable = true;
- tile.icon = mockIcon;
- category.addTile(tile);
+ mDashboardAdapter.setSuggestions(suggestions);
+
+ final RecyclerView data = mock(RecyclerView.class);
+ when(data.getResources()).thenReturn(mResources);
+ when(data.getContext()).thenReturn(mContext);
+ when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
+ final View itemView = mock(View.class);
+ when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
+ when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
+ final DashboardAdapter.SuggestionContainerHolder holder =
+ new DashboardAdapter.SuggestionContainerHolder(itemView);
- mDashboardAdapter.setCategory(category);
+ mDashboardAdapter.onBindSuggestion(holder, 0);
- verify(mockIcon).setTint(eq(0x89000000));
+ verify(data).setAdapter(any(SuggestionAdapter.class));
+ // should not crash
}
@Test
- public void testBindConditionAndSuggestion_v2_shouldSetSuggestionAdapterAndNoCrash() {
- mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null);
+ public void testBindSuggestion_shouldSetSummary() {
+ mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
- final DashboardCategory category = new DashboardCategory();
- category.addTile(mock(Tile.class));
mDashboardAdapter.setSuggestions(suggestions);
when(data.getContext()).thenReturn(mContext);
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
final View itemView = mock(View.class);
- when(itemView.findViewById(R.id.data)).thenReturn(data);
- final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
- new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
+ when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
+ final TextView summary = mock(TextView.class);
+ when(itemView.findViewById(android.R.id.summary)).thenReturn(summary);
+ final DashboardAdapter.SuggestionContainerHolder holder =
+ new DashboardAdapter.SuggestionContainerHolder(itemView);
- mDashboardAdapter.onBindConditionAndSuggestion(
- holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
+ mDashboardAdapter.onBindSuggestion(holder, 0);
- verify(data).setAdapter(any(SuggestionAdapter.class));
- // should not crash
+ verify(summary).setText("1");
+
+ suggestions.addAll(makeSuggestionsV2("pkg2", "pkg3", "pkg4"));
+ mDashboardAdapter.setSuggestions(suggestions);
+
+ mDashboardAdapter.onBindSuggestion(holder, 0);
+
+ verify(summary).setText("4");
+ }
+
+ @Test
+ public void onBindTile_internalTile_shouldNotUseGenericBackgroundIcon() {
+ final Context context = RuntimeEnvironment.application;
+ final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
+ final DashboardAdapter.DashboardItemHolder holder =
+ new DashboardAdapter.DashboardItemHolder(view);
+ final Tile tile = new Tile();
+ tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
+ final DashboardAdapter.IconCache iconCache = mock(DashboardAdapter.IconCache.class);
+ when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
+
+ mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
+ ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
+ mDashboardAdapter.onBindTile(holder, tile);
+
+ verify(iconCache, never()).updateIcon(any(Icon.class), any(Drawable.class));
+ }
+
+ @Test
+ public void onBindTile_externalTile_shouldNotUseGenericBackgroundIcon() {
+ final Context context = RuntimeEnvironment.application;
+ final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
+ final DashboardAdapter.DashboardItemHolder holder =
+ new DashboardAdapter.DashboardItemHolder(view);
+ final Tile tile = new Tile();
+ tile.icon = mock(Icon.class);
+ when(tile.icon.getResPackage()).thenReturn("another.package");
+
+ final DashboardAdapter.IconCache iconCache = mock(DashboardAdapter.IconCache.class);
+ when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
+
+ mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
+ null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
+ ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
+ mDashboardAdapter.onBindTile(holder, tile);
+
+ verify(iconCache).updateIcon(eq(tile.icon), any(RoundedHomepageIcon.class));
}
private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
private void setupSuggestions(List<Suggestion> suggestions) {
final Context context = RuntimeEnvironment.application;
mDashboardAdapter.setSuggestions(suggestions);
- mSuggestionHolder = new DashboardAdapter.SuggestionAndConditionHeaderHolder(
- LayoutInflater.from(context).inflate(
- R.layout.suggestion_condition_header, new RelativeLayout(context), true));
}
}
+++ /dev/null
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.dashboard;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.service.settings.suggestions.Suggestion;
-import android.support.v7.widget.RecyclerView;
-import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.TestConfig;
-import com.android.settings.dashboard.conditional.Condition;
-import com.android.settings.dashboard.suggestions.SuggestionAdapterV2;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settingslib.drawer.Tile;
-
-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.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH,
- sdk = TestConfig.SDK_VERSION,
- shadows = {
- SettingsShadowResources.class,
- SettingsShadowResources.SettingsShadowTheme.class,
- })
-public class DashboardAdapterV2Test {
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private SettingsActivity mContext;
- @Mock
- private View mView;
- @Mock
- private Condition mCondition;
- @Mock
- private Resources mResources;
- private FakeFeatureFactory mFactory;
- private DashboardAdapterV2 mDashboardAdapter;
- private List<Condition> mConditionList;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mFactory = FakeFeatureFactory.setupForTest();
- when(mFactory.dashboardFeatureProvider.shouldTintIcon()).thenReturn(true);
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getQuantityString(any(int.class), any(int.class), any()))
- .thenReturn("");
-
- mConditionList = new ArrayList<>();
- mConditionList.add(mCondition);
- when(mCondition.shouldShow()).thenReturn(true);
- mDashboardAdapter = new DashboardAdapterV2(mContext, null /* savedInstanceState */,
- mConditionList, null /* suggestionControllerMixin */, null /* lifecycle */);
- when(mView.getTag()).thenReturn(mCondition);
- }
-
- @Test
- public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
- final DashboardAdapterV2 adapter =
- spy(new DashboardAdapterV2(mContext, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /*
- lifecycle */));
- final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
- adapter.setSuggestions(suggestions);
-
- final RecyclerView data = mock(RecyclerView.class);
- when(data.getResources()).thenReturn(mResources);
- when(data.getContext()).thenReturn(mContext);
- when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
- final View itemView = mock(View.class);
- when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
- when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
- final DashboardAdapterV2.SuggestionContainerHolder holder =
- new DashboardAdapterV2.SuggestionContainerHolder(itemView);
-
- adapter.onBindSuggestion(holder, 0);
-
- final DashboardDataV2 dashboardData = adapter.mDashboardData;
- reset(adapter); // clear interactions tracking
-
- final Suggestion suggestionToRemove = suggestions.get(1);
- adapter.onSuggestionClosed(suggestionToRemove);
-
- assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
- assertThat(suggestions.size()).isEqualTo(2);
- assertThat(suggestions.contains(suggestionToRemove)).isFalse();
- verify(adapter, never()).notifyDashboardDataChanged(any());
- }
-
- @Test
- public void testSuggestionDismissed_moreThanTwoSuggestions_shouldNotCrash() {
- final RecyclerView data = new RecyclerView(RuntimeEnvironment.application);
- final View itemView = mock(View.class);
- when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
- when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
- final DashboardAdapterV2.SuggestionContainerHolder holder =
- new DashboardAdapterV2.SuggestionContainerHolder(itemView);
- final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4");
- final DashboardAdapterV2 adapter = spy(new DashboardAdapterV2(mContext,
- null /*savedInstance */, null /* conditions */,
- null /* suggestionControllerMixin */,
- null /* lifecycle */));
- adapter.setSuggestions(suggestions);
- adapter.onBindSuggestion(holder, 0);
-
- adapter.onSuggestionClosed(suggestions.get(1));
-
- // verify operations that access the lists will not cause ConcurrentModificationException
- assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(3);
- adapter.setSuggestions(suggestions);
- // should not crash
- }
-
- @Test
- public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
- DashboardAdapterV2 adapter =
- spy(new DashboardAdapterV2(mContext, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /*
- lifecycle */));
- final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
- adapter.setSuggestions(suggestions);
- final DashboardDataV2 dashboardData = adapter.mDashboardData;
- reset(adapter); // clear interactions tracking
-
- adapter.onSuggestionClosed(suggestions.get(0));
-
- assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
- verify(adapter).notifyDashboardDataChanged(any());
- }
-
- @Test
- public void testBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
- mDashboardAdapter = new DashboardAdapterV2(mContext, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
- final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
-
- mDashboardAdapter.setSuggestions(suggestions);
-
- final RecyclerView data = mock(RecyclerView.class);
- when(data.getResources()).thenReturn(mResources);
- when(data.getContext()).thenReturn(mContext);
- when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
- final View itemView = mock(View.class);
- when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
- when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
- final DashboardAdapterV2.SuggestionContainerHolder holder =
- new DashboardAdapterV2.SuggestionContainerHolder(itemView);
-
- mDashboardAdapter.onBindSuggestion(holder, 0);
-
- verify(data).setAdapter(any(SuggestionAdapterV2.class));
- // should not crash
- }
-
- @Test
- public void testBindSuggestion_shouldSetSummary() {
- mDashboardAdapter = new DashboardAdapterV2(mContext, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
- final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
-
- mDashboardAdapter.setSuggestions(suggestions);
-
- final RecyclerView data = mock(RecyclerView.class);
- when(data.getResources()).thenReturn(mResources);
- when(data.getContext()).thenReturn(mContext);
- when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
- final View itemView = mock(View.class);
- when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
- final TextView summary = mock(TextView.class);
- when(itemView.findViewById(android.R.id.summary)).thenReturn(summary);
- final DashboardAdapterV2.SuggestionContainerHolder holder =
- new DashboardAdapterV2.SuggestionContainerHolder(itemView);
-
- mDashboardAdapter.onBindSuggestion(holder, 0);
-
- verify(summary).setText("1");
-
- suggestions.addAll(makeSuggestionsV2("pkg2", "pkg3", "pkg4"));
- mDashboardAdapter.setSuggestions(suggestions);
-
- mDashboardAdapter.onBindSuggestion(holder, 0);
-
- verify(summary).setText("4");
- }
-
- @Test
- public void onBindTile_internalTile_shouldNotUseGenericBackgroundIcon() {
- final Context context = RuntimeEnvironment.application;
- final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
- final DashboardAdapterV2.DashboardItemHolder holder =
- new DashboardAdapterV2.DashboardItemHolder(view);
- final Tile tile = new Tile();
- tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
- final DashboardAdapterV2.IconCache iconCache = mock(DashboardAdapterV2.IconCache.class);
- when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
-
- mDashboardAdapter = new DashboardAdapterV2(context, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
- ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
- mDashboardAdapter.onBindTile(holder, tile);
-
- verify(iconCache, never()).updateIcon(any(Icon.class), any(Drawable.class));
- }
-
- @Test
- public void onBindTile_externalTile_shouldNotUseGenericBackgroundIcon() {
- final Context context = RuntimeEnvironment.application;
- final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
- final DashboardAdapterV2.DashboardItemHolder holder =
- new DashboardAdapterV2.DashboardItemHolder(view);
- final Tile tile = new Tile();
- tile.icon = mock(Icon.class);
- when(tile.icon.getResPackage()).thenReturn("another.package");
-
- final DashboardAdapterV2.IconCache iconCache = mock(DashboardAdapterV2.IconCache.class);
- when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
-
- mDashboardAdapter = new DashboardAdapterV2(context, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
- ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
- mDashboardAdapter.onBindTile(holder, tile);
-
- verify(iconCache).updateIcon(eq(tile.icon), any(RoundedHomepageIcon.class));
- }
-
- private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
- final List<Suggestion> suggestions = new ArrayList<>();
- for (String pkgName : pkgNames) {
- final Suggestion suggestion = new Suggestion.Builder(pkgName)
- .setPendingIntent(mock(PendingIntent.class))
- .build();
- suggestions.add(suggestion);
- }
- return suggestions;
- }
-
- private void setupSuggestions(List<Suggestion> suggestions) {
- final Context context = RuntimeEnvironment.application;
- mDashboardAdapter.setSuggestions(suggestions);
- }
-}
package com.android.settings.dashboard;
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_CONTAINER;
-import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_FOOTER;
+import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_FOOTER;
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONTAINER;
+import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_DIVIDER;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
.setConditions(oneItemConditions)
.setCategory(mDashboardCategory)
.setSuggestions(suggestions)
- .setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
+ .setConditionExpanded(true)
.build();
mDashboardDataWithTwoConditions = new DashboardData.Builder()
.setConditions(twoItemsConditions)
.setCategory(mDashboardCategory)
.setSuggestions(suggestions)
- .setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
+ .setConditionExpanded(true)
.build();
mDashboardDataWithNoItems = new DashboardData.Builder()
public void testBuildItemsData_shouldSetstableId() {
final List<DashboardData.Item> items = mDashboardDataWithOneConditions.getItemList();
- // Header, suggestion, condition, footer, 1 tile
- assertThat(items).hasSize(4);
+ // suggestion, seperator, condition, footer, 1 tile
+ assertThat(items).hasSize(5);
assertThat(items.get(0).id).isEqualTo(STABLE_ID_SUGGESTION_CONTAINER);
- assertThat(items.get(1).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER);
- assertThat(items.get(2).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_FOOTER);
- assertThat(items.get(3).id).isEqualTo(Objects.hash(mTestCategoryTile.title));
+ assertThat(items.get(1).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_DIVIDER);
+ assertThat(items.get(2).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER);
+ assertThat(items.get(3).id).isEqualTo(STABLE_ID_CONDITION_FOOTER);
+ assertThat(items.get(4).id).isEqualTo(Objects.hash(mTestCategoryTile.title));
}
@Test
public void testBuildItemsData_containsAllData() {
final Object[] expectedObjects = {
mDashboardDataWithOneConditions.getSuggestions(),
+ null /* divider */,
mDashboardDataWithOneConditions.getConditions(),
- null, mTestCategoryTile};
+ null /* footer */, mTestCategoryTile};
final int expectedSize = expectedObjects.length;
assertThat(mDashboardDataWithOneConditions.getItemList()).hasSize(expectedSize);
final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
if (item instanceof List) {
assertThat(item).isEqualTo(expectedObjects[i]);
- } else if (item instanceof DashboardData.SuggestionConditionHeaderData) {
- DashboardData.SuggestionConditionHeaderData i1 =
- (DashboardData.SuggestionConditionHeaderData) item;
- DashboardData.SuggestionConditionHeaderData i2 =
- (DashboardData.SuggestionConditionHeaderData) expectedObjects[i];
+ } else if (item instanceof DashboardData.ConditionHeaderData) {
+ DashboardData.ConditionHeaderData i1 =
+ (DashboardData.ConditionHeaderData) item;
+ DashboardData.ConditionHeaderData i2 =
+ (DashboardData.ConditionHeaderData) expectedObjects[i];
assertThat(i1.title).isEqualTo(i2.title);
assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
- assertThat(i1.hiddenSuggestionCount).isEqualTo(i2.hiddenSuggestionCount);
} else {
assertThat(item).isSameAs(expectedObjects[i]);
}
public void testDiffUtil_InsertOneCondition_ResultDataOneChanged() {
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
- // Item in position 2 is the condition container containing the list of conditions, which
+ // Item in position 3 is the condition container containing the list of conditions, which
// gets 1 more item
testResultData.add(new ListUpdateResult.ResultData(
- ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 1, 1));
+ ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
testDiffUtil(mDashboardDataWithOneConditions,
mDashboardDataWithTwoConditions, testResultData);
public void testDiffUtil_RemoveOneSuggestion_causeItemRemoveAndChange() {
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
+ // removed suggestion and the divider
testResultData.add(new ListUpdateResult.ResultData(
- ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 1));
+ ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 2));
testResultData.add(new ListUpdateResult.ResultData(
- ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 1, 1));
+ ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
// Build DashboardData
final List<Condition> oneItemConditions = new ArrayList<>();
when(mTestCondition.shouldShow()).thenReturn(true);
.setConditions(oneItemConditions)
.setCategory(mDashboardCategory)
.setSuggestions(suggestions)
- .setSuggestionConditionMode(DashboardData.HEADER_MODE_DEFAULT)
+ .setConditionExpanded(false)
.build();
final DashboardData newData = new DashboardData.Builder()
.setConditions(oneItemConditions)
.setSuggestions(null)
.setCategory(mDashboardCategory)
- .setSuggestionConditionMode(DashboardData.HEADER_MODE_DEFAULT)
+ .setConditionExpanded(false)
.build();
testDiffUtil(oldData, newData, testResultData);
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
testResultData.add(new ListUpdateResult.ResultData(
- ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 4));
+ ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 5));
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
}
mSummary.onCategoriesChanged();
verify(mSummary).rebuildUI();
}
-
- @Test
- public void onSuggestionDismissed_shouldNotRebuildUI() {
- mSummary.onSuggestionDismissed(mock(Suggestion.class));
- verify(mSummary, never()).rebuildUI();
- }
}
\ No newline at end of file
*/
package com.android.settings.dashboard.conditional;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.DashboardAdapter;
-import com.android.settings.dashboard.DashboardData;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import java.util.ArrayList;
+import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
}
@Test
- public void getItemCount_notFullyExpanded_shouldReturn0() {
- mConditionAdapter = new ConditionAdapter(
- mContext, mOneCondition, DashboardData.HEADER_MODE_DEFAULT);
- assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
-
- mConditionAdapter = new ConditionAdapter(
- mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
- assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
-
- mConditionAdapter = new ConditionAdapter(
- mContext, mOneCondition, DashboardData.HEADER_MODE_COLLAPSED);
+ public void getItemCount_notExpanded_shouldReturn0() {
+ mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, false);
assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
}
@Test
- public void getItemCount_fullyExpanded_shouldReturnListSize() {
- mConditionAdapter = new ConditionAdapter(
- mContext, mOneCondition, DashboardData.HEADER_MODE_FULLY_EXPANDED);
+ public void getItemCount_expanded_shouldReturnListSize() {
+ mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
assertThat(mConditionAdapter.getItemCount()).isEqualTo(1);
- mConditionAdapter = new ConditionAdapter(
- mContext, mTwoConditions, DashboardData.HEADER_MODE_FULLY_EXPANDED);
+ mConditionAdapter = new ConditionAdapter(mContext, mTwoConditions, true);
assertThat(mConditionAdapter.getItemCount()).isEqualTo(2);
}
@Test
public void getItemViewType_shouldReturnConditionTile() {
- mConditionAdapter = new ConditionAdapter(
- mContext, mTwoConditions, DashboardData.HEADER_MODE_FULLY_EXPANDED);
+ mConditionAdapter = new ConditionAdapter(mContext, mTwoConditions, true);
assertThat(mConditionAdapter.getItemViewType(0)).isEqualTo(R.layout.condition_tile);
}
R.layout.condition_tile, new LinearLayout(mContext), true);
final DashboardAdapter.DashboardItemHolder viewHolder =
new DashboardAdapter.DashboardItemHolder(view);
- mConditionAdapter = new ConditionAdapter(
- mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
+ mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
mConditionAdapter.onBindViewHolder(viewHolder, 0);
final View card = view.findViewById(R.id.content);
R.layout.condition_tile, new LinearLayout(mContext), true);
final DashboardAdapter.DashboardItemHolder viewHolder =
new DashboardAdapter.DashboardItemHolder(view);
- mConditionAdapter = new ConditionAdapter(
- mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
+ mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
mConditionAdapter.onBindViewHolder(viewHolder, 0);
final View card = view.findViewById(R.id.content);
final View view = LayoutInflater.from(mContext).inflate(
R.layout.condition_tile, new LinearLayout(mContext), true);
final DashboardAdapter.DashboardItemHolder viewHolder =
- new DashboardAdapter.DashboardItemHolder(view);
- mConditionAdapter = new ConditionAdapter(
- mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
+ new DashboardAdapter.DashboardItemHolder(view);
+ mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
mConditionAdapter.addDismissHandling(recyclerView);
// do not bind viewholder to simulate the null condition scenario
+++ /dev/null
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.dashboard.conditional;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.settings.R;
-import com.android.settings.TestConfig;
-import com.android.settings.dashboard.DashboardAdapterV2;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class ConditionAdapterV2Test {
- @Mock
- private Condition mCondition1;
- @Mock
- private Condition mCondition2;
-
- private Context mContext;
- private ConditionAdapterV2 mConditionAdapter;
- private List<Condition> mOneCondition;
- private List<Condition> mTwoConditions;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- final CharSequence[] actions = new CharSequence[2];
- when(mCondition1.getActions()).thenReturn(actions);
- when(mCondition1.shouldShow()).thenReturn(true);
- mOneCondition = new ArrayList<>();
- mOneCondition.add(mCondition1);
- mTwoConditions = new ArrayList<>();
- mTwoConditions.add(mCondition1);
- mTwoConditions.add(mCondition2);
- }
-
- @Test
- public void getItemCount_notExpanded_shouldReturn0() {
- mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, false);
- assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
- }
-
- @Test
- public void getItemCount_expanded_shouldReturnListSize() {
- mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
- assertThat(mConditionAdapter.getItemCount()).isEqualTo(1);
-
- mConditionAdapter = new ConditionAdapterV2(mContext, mTwoConditions, true);
- assertThat(mConditionAdapter.getItemCount()).isEqualTo(2);
- }
-
- @Test
- public void getItemViewType_shouldReturnConditionTile() {
- mConditionAdapter = new ConditionAdapterV2(mContext, mTwoConditions, true);
- assertThat(mConditionAdapter.getItemViewType(0)).isEqualTo(R.layout.condition_tile);
- }
-
- @Test
- public void onBindViewHolder_shouldSetListener() {
- final View view = LayoutInflater.from(mContext).inflate(
- R.layout.condition_tile, new LinearLayout(mContext), true);
- final DashboardAdapterV2.DashboardItemHolder viewHolder =
- new DashboardAdapterV2.DashboardItemHolder(view);
- mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
-
- mConditionAdapter.onBindViewHolder(viewHolder, 0);
- final View card = view.findViewById(R.id.content);
- assertThat(card.hasOnClickListeners()).isTrue();
- }
-
- @Test
- public void viewClick_shouldInvokeConditionPrimaryClick() {
- final View view = LayoutInflater.from(mContext).inflate(
- R.layout.condition_tile, new LinearLayout(mContext), true);
- final DashboardAdapterV2.DashboardItemHolder viewHolder =
- new DashboardAdapterV2.DashboardItemHolder(view);
- mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
-
- mConditionAdapter.onBindViewHolder(viewHolder, 0);
- final View card = view.findViewById(R.id.content);
- card.performClick();
- verify(mCondition1).onPrimaryClick();
- }
-
- @Test
- public void onSwiped_nullCondition_shouldNotCrash() {
- final RecyclerView recyclerView = new RecyclerView(mContext);
- final View view = LayoutInflater.from(mContext).inflate(
- R.layout.condition_tile, new LinearLayout(mContext), true);
- final DashboardAdapterV2.DashboardItemHolder viewHolder =
- new DashboardAdapterV2.DashboardItemHolder(view);
- mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
- mConditionAdapter.addDismissHandling(recyclerView);
-
- // do not bind viewholder to simulate the null condition scenario
- mConditionAdapter.mSwipeCallback.onSwiped(viewHolder, 0);
- // no crash
- }
-
-}
package com.android.settings.dashboard.suggestions;
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.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.service.settings.suggestions.Suggestion;
import android.view.LayoutInflater;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@Test
public void getItemCount_shouldReturnListSize() {
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
- mOneSuggestion, new ArrayList<>());
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(mOneSuggestion);
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
- mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
- mTwoSuggestions, new ArrayList<>());
+ mSuggestionAdapter.setSuggestions(mTwoSuggestions);
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
}
@Test
public void getItemViewType_shouldReturnSuggestionTile() {
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
- mOneSuggestion, new ArrayList<>());
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(mOneSuggestion);
assertThat(mSuggestionAdapter.getItemViewType(0))
- .isEqualTo(R.layout.suggestion_tile);
+ .isEqualTo(R.layout.suggestion_tile);
}
@Test
.setSummary("456")
.build());
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
- suggestions, new ArrayList<>());
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(suggestions);
assertThat(mSuggestionAdapter.getItemViewType(0))
- .isEqualTo(R.layout.suggestion_tile_with_button);
+ .isEqualTo(R.layout.suggestion_tile_with_button);
}
@Test
public void onBindViewHolder_shouldLog() {
final View view = spy(LayoutInflater.from(mContext).inflate(
- R.layout.suggestion_tile, new LinearLayout(mContext), true));
+ R.layout.suggestion_tile, new LinearLayout(mContext), true));
mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
- mOneSuggestion, new ArrayList<>());
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(mOneSuggestion);
// Bind twice
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
}
@Test
+ public void onBindViewHolder_hasButton_buttonShouldHandleClick()
+ throws PendingIntent.CanceledException {
+ final List<Suggestion> suggestions = new ArrayList<>();
+ final PendingIntent pendingIntent = mock(PendingIntent.class);
+ suggestions.add(new Suggestion.Builder("id")
+ .setFlags(Suggestion.FLAG_HAS_BUTTON)
+ .setTitle("123")
+ .setSummary("456")
+ .setPendingIntent(pendingIntent)
+ .build());
+ mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(suggestions);
+ mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
+ new FrameLayout(RuntimeEnvironment.application),
+ mSuggestionAdapter.getItemViewType(0));
+
+ mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
+ mSuggestionHolder.itemView.findViewById(android.R.id.primary).performClick();
+
+ verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
+ verify(pendingIntent).send();
+ }
+
+ @Test
public void getSuggestions_shouldReturnSuggestionWhenMatch() {
final List<Suggestion> suggestions = makeSuggestions("pkg1");
setupSuggestions(mActivity, suggestions);
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
}
+ @Test
+ public void onBindViewHolder_closeButtonShouldHandleClick()
+ throws PendingIntent.CanceledException {
+ final List<Suggestion> suggestions = makeSuggestions("pkg1");
+ final SuggestionAdapter.Callback callback = mock(SuggestionAdapter.Callback.class);
+ mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
+ null /* savedInstanceState */, callback, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(suggestions);
+ mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
+ new FrameLayout(RuntimeEnvironment.application),
+ mSuggestionAdapter.getItemViewType(0));
+
+ mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
+ mSuggestionHolder.itemView.findViewById(R.id.close_button).performClick();
+
+ final Suggestion suggestion = suggestions.get(0);
+ verify(mFeatureFactory.suggestionsFeatureProvider).dismissSuggestion(
+ mActivity, mSuggestionControllerMixin, suggestion);
+ verify(callback).onSuggestionClosed(suggestion);
+ }
+
+ @Test
+ public void onBindViewHolder_iconNotTintable_shouldNotTintIcon()
+ throws PendingIntent.CanceledException {
+ final Icon icon = mock(Icon.class);
+ final Suggestion suggestion = new Suggestion.Builder("pkg1")
+ .setPendingIntent(mock(PendingIntent.class))
+ .setIcon(icon)
+ .build();
+ final List<Suggestion> suggestions = new ArrayList<>();
+ suggestions.add(suggestion);
+ mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(suggestions);
+ mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
+ new FrameLayout(RuntimeEnvironment.application),
+ mSuggestionAdapter.getItemViewType(0));
+ DashboardAdapter.IconCache cache = mock(DashboardAdapter.IconCache.class);
+ final Drawable drawable = mock(Drawable.class);
+ when(cache.getIcon(icon)).thenReturn(drawable);
+ ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
+
+ mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
+
+ verify(drawable, never()).setTint(anyInt());
+ }
+
+ @Test
+ public void onBindViewHolder_iconTintable_shouldTintIcon()
+ throws PendingIntent.CanceledException {
+ final Icon icon = mock(Icon.class);
+ final int FLAG_ICON_TINTABLE = 1 << 1;
+ final Suggestion suggestion = new Suggestion.Builder("pkg1")
+ .setPendingIntent(mock(PendingIntent.class))
+ .setIcon(icon)
+ .setFlags(FLAG_ICON_TINTABLE)
+ .build();
+ final List<Suggestion> suggestions = new ArrayList<>();
+ suggestions.add(suggestion);
+ mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(suggestions);
+ mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
+ new FrameLayout(RuntimeEnvironment.application),
+ mSuggestionAdapter.getItemViewType(0));
+ DashboardAdapter.IconCache cache = mock(DashboardAdapter.IconCache.class);
+ final Drawable drawable = mock(Drawable.class);
+ when(cache.getIcon(icon)).thenReturn(drawable);
+ ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
+ TypedArray typedArray = mock(TypedArray.class);
+ final int colorAccent = 1234;
+ when(mActivity.obtainStyledAttributes(any())).thenReturn(typedArray);
+ when(typedArray.getColor(anyInt(), anyInt())).thenReturn(colorAccent);
+
+ mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
+
+ verify(drawable).setTint(colorAccent);
+ }
+
private void setupSuggestions(Context context, List<Suggestion> suggestions) {
mSuggestionAdapter = new SuggestionAdapter(context, mSuggestionControllerMixin,
- suggestions, new ArrayList<>());
+ null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
+ mSuggestionAdapter.setSuggestions(suggestions);
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
new FrameLayout(RuntimeEnvironment.application),
mSuggestionAdapter.getItemViewType(0));
+++ /dev/null
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.dashboard.suggestions;
-
-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.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.service.settings.suggestions.Suggestion;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.TestConfig;
-import com.android.settings.dashboard.DashboardAdapterV2;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settingslib.suggestions.SuggestionControllerMixin;
-
-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.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class SuggestionAdapterV2Test {
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private SettingsActivity mActivity;
- @Mock
- private SuggestionControllerMixin mSuggestionControllerMixin;
- private FakeFeatureFactory mFeatureFactory;
- private Context mContext;
- private SuggestionAdapterV2 mSuggestionAdapter;
- private DashboardAdapterV2.DashboardItemHolder mSuggestionHolder;
- private List<Suggestion> mOneSuggestion;
- private List<Suggestion> mTwoSuggestions;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mFeatureFactory = FakeFeatureFactory.setupForTest();
-
- final Suggestion suggestion1 = new Suggestion.Builder("id1")
- .setTitle("Test suggestion 1")
- .build();
- final Suggestion suggestion2 = new Suggestion.Builder("id2")
- .setTitle("Test suggestion 2")
- .build();
- mOneSuggestion = new ArrayList<>();
- mOneSuggestion.add(suggestion1);
- mTwoSuggestions = new ArrayList<>();
- mTwoSuggestions.add(suggestion1);
- mTwoSuggestions.add(suggestion2);
- }
-
- @Test
- public void getItemCount_shouldReturnListSize() {
- mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(mOneSuggestion);
- assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
-
- mSuggestionAdapter.setSuggestions(mTwoSuggestions);
- assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
- }
-
- @Test
- public void getItemViewType_shouldReturnSuggestionTile() {
- mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(mOneSuggestion);
- assertThat(mSuggestionAdapter.getItemViewType(0))
- .isEqualTo(R.layout.suggestion_tile_v2);
- }
-
- @Test
- public void getItemType_hasButton_shouldReturnSuggestionWithButton() {
- final List<Suggestion> suggestions = new ArrayList<>();
- suggestions.add(new Suggestion.Builder("id")
- .setFlags(Suggestion.FLAG_HAS_BUTTON)
- .setTitle("123")
- .setSummary("456")
- .build());
- mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(suggestions);
-
- assertThat(mSuggestionAdapter.getItemViewType(0))
- .isEqualTo(R.layout.suggestion_tile_with_button_v2);
- }
-
- @Test
- public void onBindViewHolder_shouldLog() {
- final View view = spy(LayoutInflater.from(mContext).inflate(
- R.layout.suggestion_tile, new LinearLayout(mContext), true));
- mSuggestionHolder = new DashboardAdapterV2.DashboardItemHolder(view);
- mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(mOneSuggestion);
-
- // Bind twice
- mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
- mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
-
- // Log once
- verify(mFeatureFactory.metricsFeatureProvider).action(
- mContext, MetricsProto.MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
- mOneSuggestion.get(0).getId());
- }
-
- @Test
- public void onBindViewHolder_itemViewShouldHandleClick()
- throws PendingIntent.CanceledException {
- final List<Suggestion> suggestions = makeSuggestions("pkg1");
- setupSuggestions(mActivity, suggestions);
-
- mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
- mSuggestionHolder.itemView.performClick();
-
- verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
- verify(suggestions.get(0).getPendingIntent()).send();
- }
-
- @Test
- public void onBindViewHolder_hasButton_buttonShouldHandleClick()
- throws PendingIntent.CanceledException {
- final List<Suggestion> suggestions = new ArrayList<>();
- final PendingIntent pendingIntent = mock(PendingIntent.class);
- suggestions.add(new Suggestion.Builder("id")
- .setFlags(Suggestion.FLAG_HAS_BUTTON)
- .setTitle("123")
- .setSummary("456")
- .setPendingIntent(pendingIntent)
- .build());
- mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(suggestions);
- mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
- new FrameLayout(RuntimeEnvironment.application),
- mSuggestionAdapter.getItemViewType(0));
-
- mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
- mSuggestionHolder.itemView.findViewById(android.R.id.primary).performClick();
-
- verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
- verify(pendingIntent).send();
- }
-
- @Test
- public void getSuggestions_shouldReturnSuggestionWhenMatch() {
- final List<Suggestion> suggestions = makeSuggestions("pkg1");
- setupSuggestions(mActivity, suggestions);
-
- assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
- }
-
- @Test
- public void onBindViewHolder_closeButtonShouldHandleClick()
- throws PendingIntent.CanceledException {
- final List<Suggestion> suggestions = makeSuggestions("pkg1");
- final SuggestionAdapterV2.Callback callback = mock(SuggestionAdapterV2.Callback.class);
- mSuggestionAdapter = new SuggestionAdapterV2(mActivity, mSuggestionControllerMixin,
- null /* savedInstanceState */, callback, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(suggestions);
- mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
- new FrameLayout(RuntimeEnvironment.application),
- mSuggestionAdapter.getItemViewType(0));
-
- mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
- mSuggestionHolder.itemView.findViewById(R.id.close_button).performClick();
-
- final Suggestion suggestion = suggestions.get(0);
- verify(mFeatureFactory.suggestionsFeatureProvider).dismissSuggestion(
- mActivity, mSuggestionControllerMixin, suggestion);
- verify(callback).onSuggestionClosed(suggestion);
- }
-
- @Test
- public void onBindViewHolder_differentPackage_shouldNotTintIcon()
- throws PendingIntent.CanceledException {
- final Icon icon = mock(Icon.class);
- when(icon.getResPackage()).thenReturn("pkg1");
- when(mActivity.getPackageName()).thenReturn("pkg2");
- final Suggestion suggestion = new Suggestion.Builder("pkg1")
- .setPendingIntent(mock(PendingIntent.class))
- .setIcon(icon)
- .build();
- final List<Suggestion> suggestions = new ArrayList<>();
- suggestions.add(suggestion);
- mSuggestionAdapter = new SuggestionAdapterV2(mActivity, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(suggestions);
- mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
- new FrameLayout(RuntimeEnvironment.application),
- mSuggestionAdapter.getItemViewType(0));
- DashboardAdapterV2.IconCache cache = mock(DashboardAdapterV2.IconCache.class);
- final Drawable drawable = mock(Drawable.class);
- when(cache.getIcon(icon)).thenReturn(drawable);
- ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
-
- mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
-
- verify(drawable, never()).setTint(anyInt());
- }
-
- @Test
- public void onBindViewHolder_samePackage_shouldTintIcon()
- throws PendingIntent.CanceledException {
- final Icon icon = mock(Icon.class);
- final String packageName = "pkg1";
- when(icon.getResPackage()).thenReturn(packageName);
- when(mActivity.getPackageName()).thenReturn(packageName);
- final Suggestion suggestion = new Suggestion.Builder(packageName)
- .setPendingIntent(mock(PendingIntent.class))
- .setIcon(icon)
- .build();
- final List<Suggestion> suggestions = new ArrayList<>();
- suggestions.add(suggestion);
- mSuggestionAdapter = new SuggestionAdapterV2(mActivity, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(suggestions);
- mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
- new FrameLayout(RuntimeEnvironment.application),
- mSuggestionAdapter.getItemViewType(0));
- DashboardAdapterV2.IconCache cache = mock(DashboardAdapterV2.IconCache.class);
- final Drawable drawable = mock(Drawable.class);
- when(cache.getIcon(icon)).thenReturn(drawable);
- ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
- TypedArray typedArray = mock(TypedArray.class);
- final int colorAccent = 1234;
- when(mActivity.obtainStyledAttributes(any())).thenReturn(typedArray);
- when(typedArray.getColor(anyInt(), anyInt())).thenReturn(colorAccent);
-
- mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
-
- verify(drawable).setTint(colorAccent);
- }
-
- private void setupSuggestions(Context context, List<Suggestion> suggestions) {
- mSuggestionAdapter = new SuggestionAdapterV2(context, mSuggestionControllerMixin,
- null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
- mSuggestionAdapter.setSuggestions(suggestions);
- mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
- new FrameLayout(RuntimeEnvironment.application),
- mSuggestionAdapter.getItemViewType(0));
- }
-
- private List<Suggestion> makeSuggestions(String... pkgNames) {
- final List<Suggestion> suggestions = new ArrayList<>();
- for (String pkgName : pkgNames) {
- final Suggestion suggestion = new Suggestion.Builder(pkgName)
- .setPendingIntent(mock(PendingIntent.class))
- .build();
- suggestions.add(suggestion);
- }
- return suggestions;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.dashboard.suggestions;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.service.settings.suggestions.Suggestion;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
-
-import com.android.settings.R;
-import com.android.settings.TestConfig;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settingslib.suggestions.SuggestionControllerMixin;
-
-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.annotation.Config;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class SuggestionDismissControllerTest {
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Context mContext;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private RecyclerView mRecyclerView;
- @Mock
- private SuggestionControllerMixin mSuggestionControllerMixin;
- @Mock
- private SuggestionDismissController.Callback mCallback;
-
- private FakeFeatureFactory mFactory;
- private SuggestionDismissController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mFactory = FakeFeatureFactory.setupForTest();
-
- when(mRecyclerView.getResources().getDimension(anyInt())).thenReturn(50F);
-
- mController = new SuggestionDismissController(mContext, mRecyclerView,
- mSuggestionControllerMixin, mCallback);
- }
-
- @Test
- public void onMove_alwaysReturnTrue() {
- assertThat(mController.onMove(null, null, null)).isTrue();
- }
-
- @Test
- public void getSwipeDirs_isSuggestionTile_shouldReturnDirection() {
- final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
- when(vh.getItemViewType()).thenReturn(R.layout.suggestion_tile);
-
- assertThat(mController.getSwipeDirs(mRecyclerView, vh))
- .isEqualTo(ItemTouchHelper.START | ItemTouchHelper.END);
- }
-
- @Test
- public void getSwipeDirs_isSuggestionTileCard_shouldReturnDirection() {
- final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
- when(vh.getItemViewType()).thenReturn(R.layout.suggestion_tile_with_button);
-
- assertThat(mController.getSwipeDirs(mRecyclerView, vh))
- .isEqualTo(ItemTouchHelper.START | ItemTouchHelper.END);
- }
-
- @Test
- public void getSwipeDirs_isNotSuggestionTile_shouldReturn0() {
- final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
- when(vh.getItemViewType()).thenReturn(R.layout.condition_tile);
-
- assertThat(mController.getSwipeDirs(mRecyclerView, vh))
- .isEqualTo(0);
- }
-
- @Test
- public void onSwiped_shouldTriggerDismissSuggestion() {
- final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
- when(mCallback.getSuggestionAt(anyInt())).thenReturn(
- new Suggestion.Builder("id").build());
-
- mController.onSwiped(vh, ItemTouchHelper.START);
-
- verify(mFactory.suggestionsFeatureProvider).dismissSuggestion(
- eq(mContext), eq(mSuggestionControllerMixin), nullable(Suggestion.class));
- verify(mCallback).onSuggestionDismissed(nullable(Suggestion.class));
- }
-}
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbManagerExtras;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.TestConfig;
+import com.android.settings.connecteddevice.usb.UsbBackend;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.core.lifecycle.Lifecycle;
private UsbManager mUsbManager;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
mController = spy(new SelectUsbConfigPreferenceController(mContext, mLifecycle));
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
+ mController.mUsbManagerPassThrough = mUsbManagerPassThrough;
+
+ when(mUsbManagerPassThrough.usbFunctionsFromString("mtp")).thenReturn(UsbManagerExtras.MTP);
+ when(mUsbManagerPassThrough.usbFunctionsFromString("rndis"))
+ .thenReturn(UsbManagerExtras.RNDIS);
+ when(mUsbManagerPassThrough.usbFunctionsFromString("none"))
+ .thenReturn(UsbManagerExtras.NONE);
}
@Test
public void onPreferenceChange_setCharging_shouldEnableCharging() {
- when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true);
- doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean());
+ when(mUsbManagerPassThrough.getCurrentFunctions()).thenReturn(
+ UsbManagerExtras.usbFunctionsFromString(mValues[0]));
+ doNothing().when(mController).setCurrentFunctions(anyLong());
mController.onPreferenceChange(mPreference, mValues[0]);
- verify(mController).setCurrentFunction(mValues[0], false /* usb data unlock */);
+ verify(mController).setCurrentFunctions(
+ UsbManagerExtras.usbFunctionsFromString(mValues[0]));
}
@Test
@Test
public void onPreferenceChange_setMtp_shouldEnableMtp() {
- when(mUsbManager.isFunctionEnabled(mValues[1])).thenReturn(true);
- doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean());
+ when(mUsbManagerPassThrough.getCurrentFunctions())
+ .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[1]));
+ doNothing().when(mController).setCurrentFunctions(anyLong());
mController.onPreferenceChange(mPreference, mValues[1]);
- verify(mController).setCurrentFunction(mValues[1], true /* usb data unlock */);
+ verify(mController).setCurrentFunctions(
+ UsbManagerExtras.usbFunctionsFromString(mValues[1]));
}
@Test
public void onPreferenceChange_monkeyUser_shouldReturnFalse() {
- when(mUsbManager.isFunctionEnabled(mValues[1])).thenReturn(true);
+ when(mUsbManagerPassThrough.getCurrentFunctions())
+ .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[1]));
ShadowUtils.setIsUserAMonkey(true);
- doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean());
+ doNothing().when(mController).setCurrentFunctions(anyLong());
final boolean isHandled = mController.onPreferenceChange(mPreference, mValues[1]);
assertThat(isHandled).isFalse();
- verify(mController, never()).setCurrentFunction(any(), anyBoolean());
+ verify(mController, never()).setCurrentFunctions(anyLong());
}
@Test
public void updateState_chargingEnabled_shouldSetPreferenceToCharging() {
- when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true);
+ when(mUsbManagerPassThrough.getCurrentFunctions())
+ .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[0]));
mController.updateState(mPreference);
@Test
public void updateState_RndisEnabled_shouldEnableRndis() {
- when(mUsbManager.isFunctionEnabled(mValues[3])).thenReturn(true);
+ when(mUsbManagerPassThrough.getCurrentFunctions())
+ .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[3]));
mController.updateState(mPreference);
@Test
public void updateState_noValueSet_shouldEnableChargingAsDefault() {
+ when(mUsbManagerPassThrough.getCurrentFunctions()).thenReturn(UsbManagerExtras.NONE);
mController.updateState(mPreference);
verify(mPreference).setValue(mValues[0]);
package com.android.settings.deviceinfo;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
}
@Test
+ public void isAvailable_shouldBeTrueIfCallCapable() {
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_shouldBeFalseIfNotCallCapable() {
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
+
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
public void displayPreference_multiSim_shouldAddSecondPreference() {
when(mTelephonyManager.getPhoneCount()).thenReturn(2);
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.system;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
-import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.deviceinfo;
+package com.android.settings.system;
+import static android.os.SystemUpdateManager.KEY_STATUS;
+import static android.os.SystemUpdateManager.KEY_TITLE;
+import static android.os.SystemUpdateManager.STATUS_IDLE;
+import static android.os.SystemUpdateManager.STATUS_UNKNOWN;
+import static android.os.SystemUpdateManager.STATUS_WAITING_DOWNLOAD;
import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Build;
-import android.os.UserManager;
+import android.os.Bundle;
+import android.os.SystemUpdateManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
@Mock
private PreferenceScreen mScreen;
+ @Mock
+ private SystemUpdateManager mSystemUpdateManager;
private Context mContext;
private SystemUpdatePreferenceController mController;
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
-
+ ShadowApplication.getInstance().setSystemService(Context.SYSTEM_UPDATE_SERVICE,
+ mSystemUpdateManager);
mController = new SystemUpdatePreferenceController(mContext);
mPreference = new Preference(RuntimeEnvironment.application);
mPreference.setKey(mController.getPreferenceKey());
}
@Test
- public void updateState_shouldSetToAndroidVersion() {
+ public void updateState_systemUpdateStatusUnknown_shouldSetToAndroidVersion() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt(KEY_STATUS, STATUS_UNKNOWN);
+ when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getString(R.string.android_version_summary, Build.VERSION.RELEASE));
+ }
+
+ @Test
+ public void updateState_systemUpdateStatusIdle_shouldSetToAndroidVersion() {
+ final String testReleaseName = "ANDROID TEST VERSION";
+
+ final Bundle bundle = new Bundle();
+ bundle.putInt(KEY_STATUS, STATUS_IDLE);
+ bundle.putString(KEY_TITLE, testReleaseName);
+ when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
+
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getString(R.string.android_version_summary, testReleaseName));
+ }
+
+ @Test
+ public void updateState_systemUpdateInProgress_shouldSetToUpdatePending() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt(KEY_STATUS, STATUS_WAITING_DOWNLOAD);
+ when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
+
mController.updateState(mPreference);
- assertThat(mPreference.getSummary())
- .isEqualTo(RuntimeEnvironment.application.getString(R.string.about_summary,
- Build.VERSION.RELEASE));
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getString(R.string.android_version_pending_update_summary));
}
}
\ No newline at end of file
public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowConnectivityManager {
private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
+ private boolean mTetheringSupported = false;
public void setNetworkSupported(int networkType, boolean supported) {
mSupportedNetworkTypes.put(networkType, supported);
public boolean isNetworkSupported(int networkType) {
return mSupportedNetworkTypes.get(networkType);
}
+
+ public void setTetheringSupported(boolean supported) {
+ mTetheringSupported = supported;
+ }
+
+ @Implementation
+ public boolean isTetheringSupported() {
+ return mTetheringSupported;
+ }
}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.users;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.UserManager;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UserCapabilitiesTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private UserManager mUserManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ }
+
+ @Test
+ public void disallowUserSwitchWhenRestrictionIsSet() {
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(true);
+
+ UserCapabilities userCapabilities = UserCapabilities.create(mContext);
+ userCapabilities.updateAddUserCapabilities(mContext);
+
+ assertThat(userCapabilities.mDisallowSwitchUser).isTrue();
+ }
+
+ @Test
+ public void allowUserSwitchWhenRestrictionIsNotSet() {
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(false);
+
+ UserCapabilities userCapabilities = UserCapabilities.create(mContext);
+ userCapabilities.updateAddUserCapabilities(mContext);
+
+ assertThat(userCapabilities.mDisallowSwitchUser).isFalse();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.widget;
+
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class HighlightablePreferenceGroupAdapterTest {
+
+ private static final String TEST_KEY = "key";
+
+ @Mock
+ private View mRoot;
+ @Mock
+ private PreferenceCategory mPreferenceCatetory;
+ private Context mContext;
+ private HighlightablePreferenceGroupAdapter mAdapter;
+ private PreferenceViewHolder mViewHolder;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ when(mPreferenceCatetory.getContext()).thenReturn(mContext);
+ mAdapter = new HighlightablePreferenceGroupAdapter(mPreferenceCatetory, TEST_KEY,
+ false /* highlighted*/);
+ mViewHolder = PreferenceViewHolder.createInstanceForTests(
+ View.inflate(mContext, R.layout.app_preference_item, null));
+ }
+
+ @Test
+ public void requestHighlight_hasKey_notHighlightedBefore_shouldRequest() {
+ mAdapter.requestHighlight(mRoot, mock(RecyclerView.class));
+
+ verify(mRoot).postDelayed(any(),
+ eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS));
+ }
+
+ @Test
+ public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightKey", null);
+ ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false);
+ mAdapter.requestHighlight(mRoot, mock(RecyclerView.class));
+
+ ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY);
+ ReflectionHelpers.setField(mAdapter, "mHighlightRequested", true);
+ mAdapter.requestHighlight(mRoot, mock(RecyclerView.class));
+
+ ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY);
+ ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false);
+ mAdapter.requestHighlight(mRoot, null /* recyclerView */);
+
+ verifyZeroInteractions(mRoot);
+ }
+
+ @Test
+ public void updateBackground_notHighlightedRow_shouldNotSetHighlightedTag() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+
+ mAdapter.updateBackground(mViewHolder, 0);
+
+ assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull();
+ }
+
+ @Test
+ public void updateBackground_highlight_shouldChangeBackgroundAndSetHighlightedTag() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+
+ mAdapter.updateBackground(mViewHolder, 10);
+ assertThat(mViewHolder.itemView.getBackground()).isInstanceOf(ColorDrawable.class);
+ assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isEqualTo(true);
+ }
+
+ @Test
+ public void updateBackground_reuseHightlightedRowForNormalRow_shouldResetBackgroundAndTag() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+ mViewHolder.itemView.setTag(R.id.preference_highlighted, true);
+
+ mAdapter.updateBackground(mViewHolder, 0);
+
+ assertThat(mViewHolder.itemView.getBackground()).isNotInstanceOf(ColorDrawable.class);
+ assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isEqualTo(false);
+ }
+
+}
+++ /dev/null
-package com.android.settings;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroupAdapter;
-
-import com.android.settings.accessibility.AccessibilitySettings;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class SettingsPreferenceFragmentTest {
-
- private Instrumentation mInstrumentation;
- private Context mTargetContext;
-
- @Before
- public void setUp() throws Exception {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mTargetContext = mInstrumentation.getTargetContext();
- }
-
- @Test
- public void testHighlightCaptions() throws InterruptedException {
- final String prefKey = "captioning_preference_screen";
- Bundle args = new Bundle();
- args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, prefKey);
-
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClass(mTargetContext, SubSettings.class);
- intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
- "com.android.settings.accessibility.AccessibilitySettings");
- intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
-
- SettingsActivity activity = (SettingsActivity) mInstrumentation.startActivitySync(intent);
- AccessibilitySettings fragment = (AccessibilitySettings)
- activity.getFragmentManager().getFragments().get(0);
-
- // Allow time for highlight from post-delay.
- Thread.sleep(SettingsPreferenceFragment.DELAY_HIGHLIGHT_DURATION_MILLIS);
- if (!fragment.mPreferenceHighlighted) {
- Thread.sleep(SettingsPreferenceFragment.DELAY_HIGHLIGHT_DURATION_MILLIS);
- }
-
- int prefPosition = -1;
- PreferenceGroupAdapter adapter = (PreferenceGroupAdapter)
- fragment.getListView().getAdapter();
- for (int n = 0, count = adapter.getItemCount(); n < count; n++) {
- final Preference preference = adapter.getItem(n);
- final String preferenceKey = preference.getKey();
- if (preferenceKey.equals(prefKey)) {
- prefPosition = n;
- break;
- }
- }
-
- assertThat(fragment.mAdapter.initialHighlightedPosition).isEqualTo(prefPosition);
- }
-}