OSDN Git Service

Merge "Zen Condition text and primary click changes"
authorBeverly Tai <beverlyt@google.com>
Fri, 2 Feb 2018 15:27:04 +0000 (15:27 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Fri, 2 Feb 2018 15:27:04 +0000 (15:27 +0000)
87 files changed:
AndroidManifest.xml
res/color/preference_highligh_color.xml [new file with mode: 0644]
res/layout/condition_footer.xml [moved from res/layout/suggestion_condition_footer.xml with 100% similarity]
res/layout/condition_header.xml [moved from res/layout/suggestion_condition_header.xml with 100% similarity]
res/layout/suggestion_condition_container.xml [deleted file]
res/layout/suggestion_tile.xml
res/layout/suggestion_tile_v2.xml [deleted file]
res/layout/suggestion_tile_with_button.xml
res/layout/suggestion_tile_with_button_v2.xml [deleted file]
res/values/dimens.xml
res/values/ids.xml
res/values/strings.xml
res/xml/connected_devices_advanced.xml
res/xml/connected_devices_old.xml
res/xml/system_dashboard_fragment.xml
res/xml/usb_details_fragment.xml [new file with mode: 0644]
src/com/android/settings/Settings.java
src/com/android/settings/SettingsPreferenceFragment.java
src/com/android/settings/accounts/AccountDashboardFragment.java
src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentOld.java
src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java
src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java [deleted file]
src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdater.java [moved from src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java with 65% similarity]
src/com/android/settings/connecteddevice/usb/OWNERS [new file with mode: 0644]
src/com/android/settings/connecteddevice/usb/UsbBackend.java [moved from src/com/android/settings/deviceinfo/UsbBackend.java with 56% similarity]
src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java [new file with mode: 0644]
src/com/android/settings/connecteddevice/usb/UsbDetailsController.java [new file with mode: 0644]
src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java [new file with mode: 0644]
src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java [new file with mode: 0644]
src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesController.java [new file with mode: 0644]
src/com/android/settings/connecteddevice/usb/UsbModeChooserActivity.java [moved from src/com/android/settings/deviceinfo/UsbModeChooserActivity.java with 99% similarity]
src/com/android/settings/connecteddevice/usb/UsbModePreferenceController.java [moved from src/com/android/settings/connecteddevice/UsbModePreferenceController.java with 72% similarity]
src/com/android/settings/core/FeatureFlags.java
src/com/android/settings/core/gateway/SettingsGateway.java
src/com/android/settings/dashboard/DashboardAdapter.java
src/com/android/settings/dashboard/DashboardAdapterV2.java [deleted file]
src/com/android/settings/dashboard/DashboardData.java
src/com/android/settings/dashboard/DashboardDataV2.java [deleted file]
src/com/android/settings/dashboard/DashboardFeatureProvider.java
src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
src/com/android/settings/dashboard/DashboardSummary.java
src/com/android/settings/dashboard/conditional/ConditionAdapter.java
src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java [deleted file]
src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java
src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java [deleted file]
src/com/android/settings/dashboard/suggestions/SuggestionDismissController.java [deleted file]
src/com/android/settings/development/SelectUsbConfigPreferenceController.java
src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java
src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java
src/com/android/settings/search/SearchIndexableResourcesImpl.java
src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java [moved from src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java with 88% similarity]
src/com/android/settings/system/SystemDashboardFragment.java
src/com/android/settings/system/SystemUpdatePreferenceController.java [moved from src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java with 73% similarity]
src/com/android/settings/users/UserCapabilities.java
src/com/android/settings/users/UserSettings.java
src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java [new file with mode: 0644]
tests/robotests/src/android/hardware/usb/UsbManagerExtras.java [new file with mode: 0644]
tests/robotests/src/android/os/SystemUpdateManager.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/accounts/AccountDashboardFragmentTest.java
tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java
tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java
tests/robotests/src/com/android/settings/connecteddevice/usb/ConnectedUsbDeviceUpdaterTest.java [moved from tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java with 79% similarity]
tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java [moved from tests/robotests/src/com/android/settings/deviceinfo/UsbBackendTest.java with 81% similarity]
tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java [moved from tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java with 82% similarity]
tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesControllerTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModeChooserActivityTest.java [moved from tests/robotests/src/com/android/settings/deviceinfo/UsbModeChooserActivityTest.java with 96% similarity]
tests/robotests/src/com/android/settings/connecteddevice/usb/UsbModePreferenceControllerTest.java [moved from tests/robotests/src/com/android/settings/connecteddevice/UsbModePreferenceControllerTest.java with 67% similarity]
tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
tests/robotests/src/com/android/settings/dashboard/DashboardAdapterV2Test.java [deleted file]
tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
tests/robotests/src/com/android/settings/dashboard/conditional/ConditionAdapterTest.java
tests/robotests/src/com/android/settings/dashboard/conditional/ConditionAdapterV2Test.java [deleted file]
tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterTest.java
tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2Test.java [deleted file]
tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionDismissControllerTest.java [deleted file]
tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java
tests/robotests/src/com/android/settings/deviceinfo/PhoneNumberPreferenceControllerTest.java
tests/robotests/src/com/android/settings/system/AdditionalSystemUpdatePreferenceControllerTest.java [moved from tests/robotests/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceControllerTest.java with 94% similarity]
tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java [moved from tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java with 64% similarity]
tests/robotests/src/com/android/settings/testutils/shadow/ShadowConnectivityManager.java
tests/robotests/src/com/android/settings/users/UserCapabilitiesTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java [new file with mode: 0644]
tests/unit/src/com/android/settings/SettingsPreferenceFragmentTest.java [deleted file]

index 1766ffb..c526d6f 100644 (file)
                   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"
diff --git a/res/color/preference_highligh_color.xml b/res/color/preference_highligh_color.xml
new file mode 100644 (file)
index 0000000..0a8f770
--- /dev/null
@@ -0,0 +1,20 @@
+<?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
diff --git a/res/layout/suggestion_condition_container.xml b/res/layout/suggestion_condition_container.xml
deleted file mode 100644 (file)
index a4c60ad..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?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>
index b947452..79f6dca 100644 (file)
      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>
diff --git a/res/layout/suggestion_tile_v2.xml b/res/layout/suggestion_tile_v2.xml
deleted file mode 100644 (file)
index a7717b7..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<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>
index 081df5b..a674bcb 100644 (file)
      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>
diff --git a/res/layout/suggestion_tile_with_button_v2.xml b/res/layout/suggestion_tile_with_button_v2.xml
deleted file mode 100644 (file)
index 7042341..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<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>
index d6f3cb5..12193c4 100755 (executable)
     <!-- 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>
index dcf279a..66af163 100644 (file)
@@ -17,7 +17,7 @@
 */
 -->
 <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" />
index 7ba3d28..4b2c673 100644 (file)
     <!-- 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 &amp; 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>
index 8ca6b81..0b75abf 100644 (file)
         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" />
index 3eb62ea..cc7b5b4 100644 (file)
@@ -53,7 +53,7 @@
         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
index 73aab4b..d8459dd 100644 (file)
@@ -55,7 +55,7 @@
         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>
 
@@ -63,7 +63,7 @@
         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" />
diff --git a/res/xml/usb_details_fragment.xml b/res/xml/usb_details_fragment.xml
new file mode 100644 (file)
index 0000000..30ca993
--- /dev/null
@@ -0,0 +1,35 @@
+<?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>
index 7bd85cd..741bfda 100644 (file)
@@ -96,6 +96,7 @@ public class Settings extends SettingsActivity {
     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 */ }
index c5d477a..2a593c2 100644 (file)
@@ -30,9 +30,7 @@ import android.support.annotation.VisibleForTesting;
 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;
@@ -49,6 +47,7 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 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;
@@ -65,9 +64,6 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
 
     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 =
@@ -75,14 +71,11 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
 
 
     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 =
@@ -146,8 +139,9 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
 
         // 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);
@@ -205,7 +199,9 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
     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
@@ -217,10 +213,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
     @Override
     public void onResume() {
         super.onResume();
-
-        if (mPreferenceKey != null) {
-            highlightPreferenceIfNeeded();
-        }
+        highlightPreferenceIfNeeded();
     }
 
     @Override
@@ -263,13 +256,11 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
     }
 
     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());
         }
     }
 
@@ -340,24 +331,6 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
         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());
@@ -366,7 +339,9 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
 
     @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;
     }
 
@@ -375,7 +350,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
     }
 
     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);
@@ -401,29 +376,6 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
         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);
     }
@@ -692,11 +644,11 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
     }
 
     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() {
@@ -741,45 +693,10 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
         } 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;
-                });
-            }
-        }
-    }
 }
index 65a5ff0..0a4c343 100644 (file)
@@ -91,17 +91,20 @@ public class AccountDashboardFragment extends DashboardFragment {
                 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);
index a99ba65..7cc5d1a 100755 (executable)
@@ -162,13 +162,12 @@ public class AppInfoDashboardFragment extends DashboardFragment
         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());
@@ -205,6 +204,10 @@ public class AppInfoDashboardFragment extends DashboardFragment
 
     @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();
@@ -262,9 +265,6 @@ public class AppInfoDashboardFragment extends DashboardFragment
     }
 
     ApplicationsState.AppEntry getAppEntry() {
-        if (mAppEntry == null) {
-            retrieveAppEntry();
-        }
         return mAppEntry;
     }
 
@@ -273,9 +273,6 @@ public class AppInfoDashboardFragment extends DashboardFragment
     }
 
     PackageInfo getPackageInfo() {
-        if (mAppEntry == null) {
-            retrieveAppEntry();
-        }
         return mPackageInfo;
     }
 
@@ -361,7 +358,12 @@ public class AppInfoDashboardFragment extends DashboardFragment
                             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);
@@ -622,7 +624,8 @@ public class AppInfoDashboardFragment extends DashboardFragment
         return mPackageName;
     }
 
-    private void retrieveAppEntry() {
+    @VisibleForTesting
+    void retrieveAppEntry() {
         final Activity activity = getActivity();
         if (activity == null) {
             return;
index 2a136bc..9ac6ebd 100644 (file)
@@ -24,8 +24,9 @@ import com.android.settings.R;
 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;
index 7097b36..bde5e81 100644 (file)
@@ -26,9 +26,10 @@ import com.android.settings.R;
 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;
index 3cccc15..3d5d0e5 100644 (file)
@@ -20,6 +20,7 @@ import android.support.v7.preference.Preference;
 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;
@@ -48,7 +49,7 @@ public class ConnectedDeviceGroupController extends AbstractPreferenceController
     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
diff --git a/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java b/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java
deleted file mode 100644 (file)
index 07a7691..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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
@@ -36,8 +38,9 @@ public class ConnectedUsbDeviceUpdater {
     @VisibleForTesting
     UsbConnectionBroadcastReceiver mUsbReceiver;
 
-    private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
-            (connected) -> {
+    @VisibleForTesting
+    UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =
+            (connected, newMode) -> {
                 if (connected) {
                     mUsbPreference.setSummary(
                             UsbModePreferenceController.getSummary(mUsbBackend.getCurrentMode()));
@@ -47,18 +50,19 @@ public class ConnectedUsbDeviceUpdater {
                 }
             };
 
-    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() {
@@ -76,8 +80,12 @@ public class ConnectedUsbDeviceUpdater {
         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();
@@ -87,6 +95,5 @@ public class ConnectedUsbDeviceUpdater {
         // 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());
     }
 }
diff --git a/src/com/android/settings/connecteddevice/usb/OWNERS b/src/com/android/settings/connecteddevice/usb/OWNERS
new file mode 100644 (file)
index 0000000..add985c
--- /dev/null
@@ -0,0 +1,3 @@
+# 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;
@@ -32,34 +31,52 @@ public class UsbBackend {
     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) {
@@ -81,6 +98,7 @@ public class UsbBackend {
     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();
         }
@@ -88,38 +106,35 @@ public class UsbBackend {
     }
 
     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;
         }
     }
@@ -144,28 +159,32 @@ public class UsbBackend {
     }
 
     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) {
@@ -194,9 +213,35 @@ public class UsbBackend {
             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);
+        }
     }
 }
diff --git a/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java b/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java
new file mode 100644 (file)
index 0000000..91d22dc
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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);
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java
new file mode 100644 (file)
index 0000000..09c7554
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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);
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
new file mode 100644 (file)
index 0000000..c861188
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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;
+                }
+            };
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java
new file mode 100644 (file)
index 0000000..7ac0235
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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;
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesController.java
new file mode 100644 (file)
index 0000000..1375b4c
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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;
+    }
+}
@@ -14,7 +14,7 @@
  * 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;
@@ -33,27 +33,27 @@ public class UsbModePreferenceController extends AbstractPreferenceController
     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
@@ -88,17 +88,24 @@ public class UsbModePreferenceController extends AbstractPreferenceController
                 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 {
@@ -107,5 +114,4 @@ public class UsbModePreferenceController extends AbstractPreferenceController
             }
         }
     }
-
 }
index 687442e..8fa1bb3 100644 (file)
@@ -24,7 +24,6 @@ public class FeatureFlags {
     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";
 }
index af85ac9..f43c3c8 100644 (file)
@@ -58,6 +58,7 @@ 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.DataPlanUsageSummary;
 import com.android.settings.datausage.DataUsageList;
 import com.android.settings.datausage.DataUsageSummary;
@@ -242,6 +243,7 @@ public class SettingsGateway {
             NetworkDashboardFragment.class.getName(),
             ConnectedDeviceDashboardFragment.class.getName(),
             ConnectedDeviceDashboardFragmentOld.class.getName(),
+            UsbDetailsFragment.class.getName(),
             AppAndNotificationDashboardFragment.class.getName(),
             AccountDashboardFragment.class.getName(),
             EnterprisePrivacySettings.class.getName(),
index 9b23417..8646683 100644 (file)
@@ -17,9 +17,6 @@ package com.android.settings.dashboard;
 
 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;
@@ -41,44 +38,37 @@ 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.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;
@@ -92,57 +82,54 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
     };
 
     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);
     }
 
@@ -150,12 +137,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
         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;
@@ -163,13 +151,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
         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);
         }
     }
 
@@ -186,11 +171,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
     @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);
     }
@@ -205,24 +193,25 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
                 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;
         }
@@ -263,7 +252,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
     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;
@@ -272,120 +261,66 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
     }
 
     @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);
@@ -395,45 +330,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
         }
     }
 
-    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) {
@@ -452,6 +355,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
         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<>();
@@ -467,10 +374,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
             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 {
@@ -486,24 +397,33 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
         }
     }
 
-    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);
+        }
+    }
+
 }
diff --git a/src/com/android/settings/dashboard/DashboardAdapterV2.java b/src/com/android/settings/dashboard/DashboardAdapterV2.java
deleted file mode 100644 (file)
index f98b440..0000000
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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);
-        }
-    }
-
-}
index da2a526..49de94d 100644 (file)
@@ -40,29 +40,17 @@ import java.util.Objects;
  * 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;
 
@@ -70,15 +58,13 @@ public class DashboardData {
     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();
@@ -125,8 +111,12 @@ public class DashboardData {
         return mSuggestions;
     }
 
-    public int getSuggestionConditionMode() {
-        return mSuggestionConditionMode;
+    public boolean hasSuggestion() {
+        return sizeOf(mSuggestions) > 0;
+    }
+
+    public boolean isConditionExpanded() {
+        return mConditionExpanded;
     }
 
     /**
@@ -188,69 +178,42 @@ public class DashboardData {
 
     /**
      * 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 */);
             }
@@ -277,28 +240,23 @@ public class DashboardData {
     }
 
     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() {
         }
@@ -306,8 +264,8 @@ public class DashboardData {
         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) {
@@ -321,12 +279,12 @@ public class DashboardData {
         }
 
         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;
         }
 
@@ -376,17 +334,18 @@ public class DashboardData {
     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 {
         }
@@ -444,8 +403,9 @@ public class DashboardData {
 
                     // 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()) {
@@ -467,16 +427,13 @@ public class DashboardData {
      * 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++) {
diff --git a/src/com/android/settings/dashboard/DashboardDataV2.java b/src/com/android/settings/dashboard/DashboardDataV2.java
deleted file mode 100644 (file)
index e25ee05..0000000
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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
index e0873f5..6c3f9cd 100644 (file)
@@ -90,9 +90,4 @@ public interface DashboardFeatureProvider {
      */
     void openTileIntent(Activity activity, Tile tile);
 
-    /**
-     * Whether or not we should use the v2 of suggestions UI.
-     */
-    boolean useSuggestionUiV2();
-
 }
index a14d9e9..086a131 100644 (file)
@@ -217,11 +217,6 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
         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);
index f86867b..4f045a2 100644 (file)
@@ -38,7 +38,6 @@ import com.android.settings.dashboard.conditional.ConditionManager;
 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;
@@ -53,8 +52,7 @@ import java.util.List;
 
 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;
@@ -66,7 +64,6 @@ public class DashboardSummary extends InstrumentedFragment
 
     private FocusRecyclerView mDashboard;
     private DashboardAdapter mAdapter;
-    private DashboardAdapterV2 mAdapterV2;
     private SummaryLoader mSummaryLoader;
     private ConditionManager mConditionManager;
     private LinearLayoutManager mLayoutManager;
@@ -181,13 +178,10 @@ public class DashboardSummary extends InstrumentedFragment
     @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
@@ -205,17 +199,10 @@ public class DashboardSummary extends InstrumentedFragment
         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();
@@ -254,11 +241,7 @@ public class DashboardSummary extends InstrumentedFragment
         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);
             }
@@ -268,36 +251,13 @@ public class DashboardSummary extends InstrumentedFragment
     }
 
     @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);
         }
     }
 
@@ -313,26 +273,14 @@ public class DashboardSummary extends InstrumentedFragment
         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);
         }
     }
 }
index d84aa7c..a540b3f 100644 (file)
@@ -27,10 +27,7 @@ import android.widget.Button;
 
 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;
@@ -44,7 +41,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
     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() {
 
@@ -84,10 +81,10 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
         }
     };
 
-    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);
@@ -126,7 +123,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
 
     @Override
     public int getItemCount() {
-        if (mMode == DashboardData.HEADER_MODE_FULLY_EXPANDED) {
+        if (mExpanded) {
             return mConditions.size();
         }
         return 0;
@@ -138,7 +135,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
     }
 
     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 "
diff --git a/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java b/src/com/android/settings/dashboard/conditional/ConditionAdapterV2.java
deleted file mode 100644 (file)
index 8db57f7..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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);
-        }
-    }
-}
index d1d7db2..9bcf2a2 100644 (file)
@@ -17,6 +17,10 @@ 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;
@@ -24,37 +28,71 @@ 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.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);
     }
@@ -67,31 +105,49 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
 
     @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.
@@ -144,7 +200,83 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
     }
 
     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);
+        }
+
+    }
+
 }
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2.java
deleted file mode 100644 (file)
index e29ca5f..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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);
-        }
-
-    }
-
-}
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionDismissController.java b/src/com/android/settings/dashboard/suggestions/SuggestionDismissController.java
deleted file mode 100644 (file)
index 6a8db89..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.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);
-    }
-}
index 77a9a75..63eb24c 100644 (file)
@@ -27,11 +27,11 @@ import android.support.annotation.VisibleForTesting;
 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;
@@ -48,6 +48,8 @@ public class SelectUsbConfigPreferenceController extends
     private final String[] mListValues;
     private final String[] mListSummaries;
     private final UsbManager mUsbManager;
+    @VisibleForTesting
+    UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
     private BroadcastReceiver mUsbReceiver;
     private ListPreference mPreference;
 
@@ -57,6 +59,7 @@ public class SelectUsbConfigPreferenceController extends
         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) {
@@ -95,7 +98,8 @@ public class SelectUsbConfigPreferenceController extends
             return false;
         }
 
-        writeUsbConfigurationOption(newValue.toString());
+        writeUsbConfigurationOption(mUsbManagerPassThrough
+                .usbFunctionsFromString(newValue.toString()));
         updateUsbConfigurationValues();
         return true;
     }
@@ -129,14 +133,15 @@ public class SelectUsbConfigPreferenceController extends
     }
 
     @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;
             }
@@ -145,11 +150,7 @@ public class SelectUsbConfigPreferenceController extends
         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);
     }
 }
index 93f75bf..4eb2ddd 100644 (file)
@@ -58,7 +58,7 @@ public class PhoneNumberPreferenceController extends AbstractPreferenceControlle
 
     @Override
     public boolean isAvailable() {
-        return true;
+        return mTelephonyManager.isVoiceCapable();
     }
 
     @Override
index ec0aecd..f80b68f 100644 (file)
@@ -66,12 +66,12 @@ public class PhysicalKeyboardPreferenceController extends AbstractPreferenceCont
 
     @Override
     public void onPause() {
-        mIm.registerInputDeviceListener(this, null);
+        mIm.unregisterInputDeviceListener(this);
     }
 
     @Override
     public void onResume() {
-        mIm.unregisterInputDeviceListener(this);
+        mIm.registerInputDeviceListener(this, null);
     }
 
     @Override
index 38dc15d..1edc2de 100644 (file)
@@ -21,6 +21,8 @@ import android.support.annotation.VisibleForTesting;
 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;
@@ -34,7 +36,7 @@ import com.android.settings.backup.BackupSettingsFragment;
 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;
@@ -167,6 +169,7 @@ public class SearchIndexableResourcesImpl implements SearchIndexableResources {
         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 {
 
index 88cafb0..deabf54 100644 (file)
@@ -26,8 +26,6 @@ import com.android.internal.logging.nano.MetricsProto;
 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;
@@ -39,10 +42,12 @@ public class SystemUpdatePreferenceController extends BasePreferenceController {
     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
@@ -84,7 +89,27 @@ public class SystemUpdatePreferenceController extends BasePreferenceController {
 
     @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;
     }
 
     /**
index 084a5db..f1bfae9 100644 (file)
@@ -34,6 +34,7 @@ public class UserCapabilities {
     boolean mCanAddGuest;
     boolean mDisallowAddUser;
     boolean mDisallowAddUserSetByAdmin;
+    boolean mDisallowSwitchUser;
     RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
 
     private UserCapabilities() {}
@@ -79,6 +80,9 @@ public class 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() {
@@ -109,6 +113,7 @@ public class UserCapabilities {
                 ", mCanAddGuest=" + mCanAddGuest +
                 ", mDisallowAddUser=" + mDisallowAddUser +
                 ", mEnforcedAdmin=" + mEnforcedAdmin +
+                ", mDisallowSwitchUser=" + mDisallowSwitchUser +
                 '}';
     }
 }
index a8fab13..f6bacd8 100644 (file)
@@ -249,11 +249,14 @@ public class UserSettings extends SettingsPreferenceFragment
         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);
@@ -306,7 +309,7 @@ public class UserSettings extends SettingsPreferenceFragment
     public void onDestroy() {
         super.onDestroy();
 
-        if (!mUserCaps.mEnabled) {
+        if (mUserCaps == null || !mUserCaps.mEnabled) {
             return;
         }
 
@@ -754,8 +757,12 @@ public class UserSettings extends SettingsPreferenceFragment
                     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));
@@ -842,8 +849,12 @@ public class UserSettings extends SettingsPreferenceFragment
                 } 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);
             }
@@ -882,8 +893,13 @@ public class UserSettings extends SettingsPreferenceFragment
             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;
diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
new file mode 100644 (file)
index 0000000..e1999ef
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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);
+    }
+}
diff --git a/tests/robotests/src/android/hardware/usb/UsbManagerExtras.java b/tests/robotests/src/android/hardware/usb/UsbManagerExtras.java
new file mode 100644 (file)
index 0000000..b9bccd2
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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;
+    }
+}
diff --git a/tests/robotests/src/android/os/SystemUpdateManager.java b/tests/robotests/src/android/os/SystemUpdateManager.java
new file mode 100644 (file)
index 0000000..f81df36
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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;
+    }
+}
index aeffd20..9371019 100644 (file)
@@ -27,11 +27,13 @@ import android.os.UserHandle;
 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;
@@ -41,6 +43,7 @@ import org.robolectric.RobolectricTestRunner;
 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;
@@ -57,6 +60,11 @@ public class AccountDashboardFragmentTest {
         mFragment = new AccountDashboardFragment();
     }
 
+    @After
+    public void tearDown() {
+        ShadowAuthenticationHelper.reset();
+    }
+
     @Test
     public void testCategory_isAccount() {
         assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_ACCOUNT);
@@ -66,7 +74,8 @@ public class AccountDashboardFragmentTest {
     @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();
 
@@ -78,6 +87,23 @@ public class AccountDashboardFragmentTest {
     }
 
     @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(
@@ -94,15 +120,24 @@ public class AccountDashboardFragmentTest {
         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
index 87b82ad..fabf9ae 100644 (file)
@@ -19,6 +19,7 @@ package com.android.settings.applications.appinfo;
 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;
@@ -191,6 +192,22 @@ public final class AppInfoDashboardFragmentTest {
     }
 
     @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";
index 78be742..b478c4e 100644 (file)
@@ -31,6 +31,7 @@ import android.support.v7.preference.PreferenceScreen;
 
 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;
@@ -42,6 +44,8 @@ public class ConnectedUsbDeviceUpdaterTest {
     private ConnectedUsbDeviceUpdater mDeviceUpdater;
 
     @Mock
+    private DashboardFragment mFragment;
+    @Mock
     private UsbConnectionBroadcastReceiver mUsbReceiver;
     @Mock
     private DevicePreferenceCallback mDevicePreferenceCallback;
@@ -53,7 +57,8 @@ public class ConnectedUsbDeviceUpdaterTest {
         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;
     }
@@ -70,18 +75,18 @@ public class ConnectedUsbDeviceUpdaterTest {
 
     @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);
     }
@@ -14,7 +14,7 @@
  * 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;
@@ -25,7 +25,9 @@ import static org.mockito.Mockito.when;
 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;
 
@@ -46,6 +48,8 @@ public class UsbBackendTest {
     private UsbManager mUsbManager;
     @Mock
     private UsbBackend.UserRestrictionUtil mUserRestrictionUtil;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
 
     @Before
     public void setUp() {
@@ -53,22 +57,13 @@ public class UsbBackendTest {
         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))));
-    }
 }
@@ -13,7 +13,7 @@
  * 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;
@@ -52,6 +52,8 @@ public class UsbConnectionBroadcastReceiverTest {
 
     @Mock
     private UsbConnectionBroadcastReceiver.UsbConnectionListener mListener;
+    @Mock
+    private UsbBackend mUsbBackend;
 
     @Before
     public void setUp() {
@@ -59,27 +61,42 @@ public class UsbConnectionBroadcastReceiverTest {
 
         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
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java
new file mode 100644 (file)
index 0000000..e1f9078
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsProfilesControllerTest.java
new file mode 100644 (file)
index 0000000..557d836
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * 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;
+    }
+}
@@ -13,7 +13,7 @@
  * 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;
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.verify;
 
 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;
@@ -1,16 +1,12 @@
-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;
@@ -24,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
 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)
@@ -33,6 +30,8 @@ public class UsbModePreferenceControllerTest {
     private UsbBackend mUsbBackend;
     @Mock(answer = RETURNS_DEEP_STUBS)
     private PreferenceScreen mScreen;
+    @Mock
+    private UsbConnectionBroadcastReceiver mUsbConnectionBroadcastReceiver;
 
     private Context mContext;
     private UsbModePreferenceController mController;
@@ -42,61 +41,67 @@ public class UsbModePreferenceControllerTest {
         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));
index f9ef424..2d22e04 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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;
@@ -30,14 +28,14 @@ import static org.mockito.Mockito.when;
 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;
@@ -47,7 +45,6 @@ import com.android.settings.dashboard.suggestions.SuggestionAdapter;
 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;
@@ -58,6 +55,7 @@ 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;
@@ -81,8 +79,6 @@ public class DashboardAdapterTest {
     private Resources mResources;
     private FakeFeatureFactory mFactory;
     private DashboardAdapter mDashboardAdapter;
-    private DashboardAdapter.SuggestionAndConditionHeaderHolder mSuggestionHolder;
-    private DashboardData.SuggestionConditionHeaderData mSuggestionHeaderData;
     private List<Condition> mConditionList;
 
     @Before
@@ -98,30 +94,17 @@ public class DashboardAdapterTest {
         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);
 
@@ -130,18 +113,18 @@ public class DashboardAdapterTest {
         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);
@@ -150,25 +133,25 @@ public class DashboardAdapterTest {
     }
 
     @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
     }
@@ -176,42 +159,49 @@ public class DashboardAdapterTest {
     @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);
 
@@ -220,15 +210,62 @@ public class DashboardAdapterTest {
         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) {
@@ -245,8 +282,5 @@ public class DashboardAdapterTest {
     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));
     }
 }
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterV2Test.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterV2Test.java
deleted file mode 100644 (file)
index 40150cb..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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);
-    }
-}
index 1116d7d..35f525e 100644 (file)
@@ -17,8 +17,9 @@
 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;
@@ -103,14 +104,14 @@ public class DashboardDataTest {
                 .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()
@@ -124,21 +125,23 @@ public class DashboardDataTest {
     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);
@@ -147,14 +150,13 @@ public class DashboardDataTest {
             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]);
             }
@@ -209,10 +211,10 @@ public class DashboardDataTest {
     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);
@@ -222,10 +224,11 @@ public class DashboardDataTest {
     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);
@@ -237,13 +240,13 @@ public class DashboardDataTest {
                 .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);
@@ -254,7 +257,7 @@ public class DashboardDataTest {
         //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);
     }
index 2a7b829..a1c8d67 100644 (file)
@@ -164,10 +164,4 @@ public class DashboardSummaryTest {
         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
index d943fe3..e33db6e 100644 (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;
@@ -22,13 +27,9 @@ import android.view.View;
 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;
@@ -38,9 +39,8 @@ import org.mockito.MockitoAnnotations;
 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)
@@ -70,35 +70,23 @@ public class ConditionAdapterTest {
     }
 
     @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);
     }
 
@@ -108,8 +96,7 @@ public class ConditionAdapterTest {
             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);
@@ -122,8 +109,7 @@ public class ConditionAdapterTest {
             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);
@@ -137,9 +123,8 @@ public class ConditionAdapterTest {
         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
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionAdapterV2Test.java b/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionAdapterV2Test.java
deleted file mode 100644 (file)
index 5e0ecec..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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
-    }
-
-}
index 49e82e4..825aee9 100644 (file)
 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;
@@ -46,6 +53,7 @@ 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;
@@ -87,20 +95,21 @@ public class SuggestionAdapterTest {
     @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
@@ -112,19 +121,21 @@ public class SuggestionAdapterTest {
                 .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);
@@ -150,6 +161,31 @@ public class SuggestionAdapterTest {
     }
 
     @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);
@@ -157,9 +193,89 @@ public class SuggestionAdapterTest {
         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));
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2Test.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionAdapterV2Test.java
deleted file mode 100644 (file)
index 181c878..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.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;
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionDismissControllerTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionDismissControllerTest.java
deleted file mode 100644 (file)
index f10f5ec..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.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));
-    }
-}
index 8719bb4..67a6d6b 100644 (file)
@@ -25,6 +25,7 @@ import static junit.framework.Assert.assertTrue;
 
 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;
@@ -37,11 +38,13 @@ import android.arch.lifecycle.LifecycleOwner;
 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;
@@ -69,6 +72,8 @@ public class SelectUsbConfigPreferenceControllerTest {
     private UsbManager mUsbManager;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private UsbBackend.UsbManagerPassThrough mUsbManagerPassThrough;
 
     private Context mContext;
     private LifecycleOwner mLifecycleOwner;
@@ -101,6 +106,13 @@ public class SelectUsbConfigPreferenceControllerTest {
         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);
 
     }
 
@@ -111,11 +123,13 @@ public class SelectUsbConfigPreferenceControllerTest {
 
     @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
@@ -144,28 +158,32 @@ public class SelectUsbConfigPreferenceControllerTest {
 
     @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);
 
@@ -175,7 +193,8 @@ public class SelectUsbConfigPreferenceControllerTest {
 
     @Test
     public void updateState_RndisEnabled_shouldEnableRndis() {
-        when(mUsbManager.isFunctionEnabled(mValues[3])).thenReturn(true);
+        when(mUsbManagerPassThrough.getCurrentFunctions())
+                .thenReturn(UsbManagerExtras.usbFunctionsFromString(mValues[3]));
 
         mController.updateState(mPreference);
 
@@ -185,6 +204,7 @@ public class SelectUsbConfigPreferenceControllerTest {
 
     @Test
     public void updateState_noValueSet_shouldEnableChargingAsDefault() {
+        when(mUsbManagerPassThrough.getCurrentFunctions()).thenReturn(UsbManagerExtras.NONE);
         mController.updateState(mPreference);
 
         verify(mPreference).setValue(mValues[0]);
index f30425b..0b83359 100644 (file)
@@ -16,6 +16,9 @@
 
 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;
@@ -74,6 +77,20 @@ public class PhoneNumberPreferenceControllerTest {
     }
 
     @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;
 
@@ -55,6 +58,8 @@ public class SystemUpdatePreferenceControllerTest {
 
     @Mock
     private PreferenceScreen mScreen;
+    @Mock
+    private SystemUpdateManager mSystemUpdateManager;
 
     private Context mContext;
     private SystemUpdatePreferenceController mController;
@@ -64,7 +69,8 @@ public class SystemUpdatePreferenceControllerTest {
     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());
@@ -118,11 +124,41 @@ public class SystemUpdatePreferenceControllerTest {
     }
 
     @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
index 742fbf8..fc19b44 100644 (file)
@@ -26,6 +26,7 @@ import org.robolectric.annotation.Implements;
 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);
@@ -35,4 +36,13 @@ public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowCon
     public boolean isNetworkSupported(int networkType) {
         return mSupportedNetworkTypes.get(networkType);
     }
+
+    public void setTetheringSupported(boolean supported) {
+        mTetheringSupported = supported;
+    }
+
+    @Implementation
+    public boolean isTetheringSupported() {
+        return mTetheringSupported;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/users/UserCapabilitiesTest.java b/tests/robotests/src/com/android/settings/users/UserCapabilitiesTest.java
new file mode 100644 (file)
index 0000000..4228ca0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
new file mode 100644 (file)
index 0000000..e2fb6c1
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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);
+    }
+
+}
diff --git a/tests/unit/src/com/android/settings/SettingsPreferenceFragmentTest.java b/tests/unit/src/com/android/settings/SettingsPreferenceFragmentTest.java
deleted file mode 100644 (file)
index 0e12e79..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-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);
-    }
-}