Merge "Change the text "Network & Internet""
diff --git a/res/drawable/ic_bt_laptop.xml b/res/drawable/ic_bt_laptop.xml
deleted file mode 100644
index 029e4d9..0000000
--- a/res/drawable/ic_bt_laptop.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorControlNormal">
- <path
- android:fillColor="#FF000000"
- android:pathData="M20,18c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0
- -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/>
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_settings_bluetooth.xml b/res/drawable/ic_settings_bluetooth.xml
deleted file mode 100644
index 6e32e1a..0000000
--- a/res/drawable/ic_settings_bluetooth.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorControlNormal">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/>
-</vector>
diff --git a/res/drawable/ic_settings_print.xml b/res/drawable/ic_settings_print.xml
deleted file mode 100644
index 0eab402..0000000
--- a/res/drawable/ic_settings_print.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorControlNormal">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M19,8H5c-1.66,0-3,1.34-3,3v5c0,0.55,0.45,1,1,1h3v3c0,0.55,0.45,1,1,1h10c0.55,0,1-0.45,1-1v-3h3c0.55,0,1-0.45,1-1v-5
-C22,9.34,20.66,8,19,8z M16,19H8v-5h8V19z
-M19,12c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S19.55,12,19,12z M17,3H7
-C6.45,3,6,3.45,6,4v3h12V4C18,3.45,17.55,3,17,3z"/>
-</vector>
diff --git a/res/layout/app_header.xml b/res/layout/app_header.xml
deleted file mode 100644
index 2d8a099..0000000
--- a/res/layout/app_header.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize"
- android:background="@drawable/switchbar_background"
- android:gravity="center_vertical"
- android:theme="?attr/switchBarTheme" >
-
- <ImageView android:id="@+id/app_icon"
- android:layout_width="@dimen/switchbar_subsettings_margin_start"
- android:layout_height="40dp"
- android:gravity="end"
- android:layout_centerVertical="true" />
-
- <TextView
- android:id="@+id/app_name"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:layout_toStartOf="@+id/app_settings"
- android:layout_marginStart="@dimen/switchbar_subsettings_margin_start"
- android:layout_alignWithParentIfMissing="true"
- android:layout_centerVertical="true"
- android:textAppearance="@style/TextAppearance.Switch"
- android:textColor="@android:color/white"
- android:textAlignment="viewStart" />
-
- <ImageView
- android:id="@id/app_settings"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:layout_alignParentEnd="true"
- android:layout_marginEnd="@dimen/switchbar_subsettings_margin_end"
- android:layout_centerVertical="true"
- android:minHeight="0dp"
- android:minWidth="0dp"
- android:contentDescription="@string/notification_app_settings_button"
- android:scaleType="center"
- android:src="@drawable/ic_settings_24dp"
- android:visibility="gone"
- style="?android:attr/borderlessButtonStyle" />
-
-</RelativeLayout>
-
diff --git a/res/layout/app_item.xml b/res/layout/app_item.xml
deleted file mode 100644
index d53afc9..0000000
--- a/res/layout/app_item.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="72dp"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
- android:gravity="top"
- android:columnCount="3"
- android:duplicateParentState="true">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_gravity="center"
- android:scaleType="fitXY"
- android:layout_marginEnd="16dip"
- android:contentDescription="@null"
- android:duplicateParentState="true" />
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toEndOf="@android:id/icon"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="?android:attr/textColorPrimary"
- android:textAlignment="viewStart"
- android:duplicateParentState="true" />
-
- <TextView
- android:id="@android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toEndOf="@android:id/title"
- android:layout_alignParentEnd="true"
- android:layout_marginStart="6dip"
- android:gravity="end"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="?android:attr/textColorSecondary"
- android:duplicateParentState="true" />
-
- <Switch
- android:id="@android:id/switch_widget"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:background="@null"
- android:visibility="gone"/>
-
- <FrameLayout
- android:id="@android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toEndOf="@android:id/icon"
- android:layout_below="@android:id/title"
- android:layout_alignParentEnd="true"
- android:layout_gravity="fill_horizontal|top"
- android:duplicateParentState="true" />
-
-</RelativeLayout>
diff --git a/res/layout/data_usage_item.xml b/res/layout/data_usage_item.xml
deleted file mode 100644
index 5e931f9..0000000
--- a/res/layout/data_usage_item.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <include layout="@layout/app_item" />
-
-</FrameLayout>
diff --git a/res/layout/device_admin_item.xml b/res/layout/device_admin_item.xml
index 0420bb5..875c734 100644
--- a/res/layout/device_admin_item.xml
+++ b/res/layout/device_admin_item.xml
@@ -29,13 +29,17 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="horizontal">
+ android:gravity="start|center_vertical"
+ android:minWidth="60dp"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
<ImageView
android:id="@+id/icon"
- android:layout_width="@android:dimen/app_icon_size"
- android:layout_height="@android:dimen/app_icon_size"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dip"
android:contentDescription="@null" />
@@ -70,7 +74,7 @@
</RelativeLayout>
- <CheckBox
+ <Switch
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/res/layout/location_list_no_item.xml b/res/layout/location_list_no_item.xml
deleted file mode 100644
index c0465cf..0000000
--- a/res/layout/location_list_no_item.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<!-- text that appears when the recent app list is empty -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:background="?android:attr/selectableItemBackground" >
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="15dip"
- android:layout_marginEnd="6dip"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_weight="1">
-
- <TextView android:id="@android:id/title"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary" />
-
- </RelativeLayout>
-
-</LinearLayout>
-
diff --git a/res/layout/preference_app.xml b/res/layout/preference_app.xml
index 526d5af..8cbb6f2 100644
--- a/res/layout/preference_app.xml
+++ b/res/layout/preference_app.xml
@@ -13,13 +13,87 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
- <include layout="@layout/app_item" />
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="60dp"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <android.support.v7.internal.widget.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.TileTitle"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@style/TextAppearance.Small"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <TextView android:id="@+id/appendix"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Small"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:ellipsize="end" />
+ </LinearLayout>
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:max="100"
+ android:visibility="gone" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingStart="16dp"
+ android:orientation="vertical" />
</LinearLayout>
diff --git a/res/layout/widget_progress_bar.xml b/res/layout/widget_progress_bar.xml
deleted file mode 100644
index a2a5154..0000000
--- a/res/layout/widget_progress_bar.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-
-<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:max="100"
- style="?android:attr/progressBarStyleHorizontal" />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index c0d92c1..2ba7919 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -133,11 +133,6 @@
<attr name="forWork" format="boolean" />
</declare-styleable>
- <declare-styleable name="DividerPreference">
- <attr name="allowDividerAbove" format="boolean" />
- <attr name="allowDividerBelow" format="boolean" />
- </declare-styleable>
-
<declare-styleable name="VideoPreference">
<attr name="animation" format="reference" />
<attr name="preview" format="reference" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 8c59999..4ba73cc 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -73,6 +73,9 @@
<!-- If the support features are enabled. -->
<bool name="config_support_enabled">false</bool>
+ <!-- Whether to enable "show operator name in the status bar" setting -->
+ <bool name="config_showOperatorNameInStatusBar">false</bool>
+
<!-- List containing the component names of pre-installed screen reader services. -->
<string-array name="config_preinstalled_screen_reader_services" translatable="false">
<!--
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index fdb9c32..0d4289a 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,6 +22,7 @@
<dimen name="action_bar_switch_padding">16dip</dimen>
<dimen name="app_icon_size">40dip</dimen>
+ <dimen name="secondary_app_icon_size">24dp</dimen>
<dimen name="min_tap_target_size">48dp</dimen>
<dimen name="screen_margin_sides">64dip</dimen>
<dimen name="screen_margin_top">72dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4c42905..8b84849 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8658,27 +8658,6 @@
<!-- Estimated wait time range for real time supports -->
<string name="support_estimated_wait_time">~<xliff:g id="ESTIMATE" example="2 minutes">%1$s</xliff:g> wait</string>
- <!-- Message for telling the user the kind of BT device being displayed in list. -->
- <string name="bluetooth_talkback_computer">Computer</string>
-
- <!-- Message for telling the user the kind of BT device being displayed in list. -->
- <string name="bluetooth_talkback_headset">Headset</string>
-
- <!-- Message for telling the user the kind of BT device being displayed in list. -->
- <string name="bluetooth_talkback_phone">Phone</string>
-
- <!-- Message for telling the user the kind of BT device being displayed in list. -->
- <string name="bluetooth_talkback_imaging">Imaging</string>
-
- <!-- Message for telling the user the kind of BT device being displayed in list. -->
- <string name="bluetooth_talkback_headphone">Headphone</string>
-
- <!-- Message for telling the user the kind of BT device being displayed in list. -->
- <string name="bluetooth_talkback_input_peripheral">Input Peripheral</string>
-
- <!-- Message for telling the user the kind of BT device being displayed in list. -->
- <string name="bluetooth_talkback_bluetooth">Bluetooth</string>
-
<!-- Used as title on the automatic storage manager settings. [CHAR LIMIT=60] -->
<string name="automatic_storage_manager_settings">Manage storage</string>
@@ -8801,7 +8780,8 @@
<string name="oem_unlock_enable_disabled_summary_connectivity_or_locked">Connect to the Internet or contact your carrier</string>
<!-- setting enable OEM unlock Checkbox's summary to explain this Checkbox is disabled because this setting is unavailable on sim-locked devices. [CHAR_LIMIT=60] -->
<string name="oem_unlock_enable_disabled_summary_sim_locked_device">Unavailable on carrier-locked devices</string>
-
+ <!-- Information displayed after user locks OEM lock [Char Limit=None]-->
+ <string name="oem_lock_info_message">Please restart the device to enable device protection feature.</string>
<string name="automatic_storage_manager_freed_bytes"><xliff:g id="size" example="3.25MB">%1$s</xliff:g> total made available\n\nLast ran on <xliff:g id="date" example="Jan 12">%2$s</xliff:g></string>
<!-- Title text for enabling web actions. [CHAR_LIMIT=60] -->
@@ -9036,6 +9016,11 @@
<!-- Temporary reboot string, will be removed -->
<string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string>
+ <!-- Switch label to show operator name in the status bar [CHAR LIMIT=60] -->
+ <string name="show_operator_name_title">Network name</string>
+ <!-- Switch summary to show operator name in the status bar [CHAR LIMIT=NONE] -->
+ <string name="show_operator_name_summary">Display network name in status bar</string>
+
<!-- Indicates if the automatic storage manager is enabled or not. [CHAR_LIMIT=40] -->
<string name="storage_manager_indicator">Storage Manager: <xliff:g id="status" example="on">^1</xliff:g></string>
diff --git a/res/xml/app_default_settings.xml b/res/xml/app_default_settings.xml
index 3a8c997..0204c64 100644
--- a/res/xml/app_default_settings.xml
+++ b/res/xml/app_default_settings.xml
@@ -21,56 +21,56 @@
android:key="app_default_settings_screen"
android:title="@string/app_default_dashboard_title">
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="assist_and_voice_input"
android:title="@string/assist_and_voice_input_title"
android:fragment="com.android.settings.applications.assist.ManageAssist" />
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="default_browser"
android:title="@string/default_browser_title"
android:fragment="com.android.settings.applications.defaultapps.DefaultBrowserPicker">
<extra android:name="for_work" android:value="false" />
- </Preference>
+ </com.android.settings.widget.AppPreference>
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="default_home"
android:title="@string/home_app"
android:fragment="com.android.settings.applications.defaultapps.DefaultHomePicker"
settings:keywords="@string/keywords_home" />
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="default_phone_app"
android:title="@string/default_phone_title"
android:fragment="com.android.settings.applications.defaultapps.DefaultPhonePicker"
settings:keywords="@string/keywords_default_phone_app" />
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="default_sms_app"
android:title="@string/sms_application_title"
android:fragment="com.android.settings.applications.defaultapps.DefaultSmsPicker"
settings:keywords="@string/keywords_more_default_sms_app" />
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="default_payment_app"
android:title="@string/nfc_payment_settings_title"
android:summary="@string/summary_placeholder"
android:fragment="com.android.settings.nfc.PaymentSettings" />
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="default_emergency_app"
android:title="@string/default_emergency_app"
settings:keywords="@string/keywords_emergency_app" />
<!--
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="default_notification_asst_app"
android:title="@string/default_notification_assistant"
android:fragment="com.android.settings.applications.defaultapps.DefaultNotificationAssistantPicker"
/>
-->
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="domain_urls"
android:title="@string/domain_urls_title"
android:fragment="com.android.settings.applications.ManageDomainUrls" />
@@ -79,20 +79,20 @@
android:key="work_app_defaults"
android:title="@string/default_for_work">
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="work_default_browser"
android:title="@string/default_browser_title"
android:fragment="com.android.settings.applications.defaultapps.DefaultBrowserPicker">
<extra android:name="for_work" android:value="true" />
- </Preference>
+ </com.android.settings.widget.AppPreference>
- <Preference
+ <com.android.settings.widget.AppPreference
android:key="work_default_phone_app"
android:title="@string/default_phone_title"
android:fragment="com.android.settings.applications.defaultapps.DefaultPhonePicker"
settings:keywords="@string/keywords_default_phone_app">
<extra android:name="for_work" android:value="true" />
- </Preference>
+ </com.android.settings.widget.AppPreference>
</com.android.settings.widget.WorkOnlyCategory>
diff --git a/res/xml/data_usage.xml b/res/xml/data_usage.xml
index 5bc8087..958459c 100644
--- a/res/xml/data_usage.xml
+++ b/res/xml/data_usage.xml
@@ -28,7 +28,7 @@
android:key="status_header"
android:selectable="false" />
- <com.android.settings.DividerPreference
+ <Preference
android:key="limit_summary"
android:selectable="false"
settings:allowDividerBelow="true" />
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index 18bfe57..a6efb5c 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -84,6 +84,11 @@
android:fragment="com.android.settings.display.ScreenZoomSettings"
settings:keywords="@string/screen_zoom_keywords" />
+ <SwitchPreference
+ android:key="show_operator_name"
+ android:title="@string/show_operator_name_title"
+ android:summary="@string/show_operator_name_summary" />
+
<Preference
android:key="screensaver"
android:title="@string/screensaver_settings_title"
diff --git a/res/xml/security_settings_misc.xml b/res/xml/security_settings_misc.xml
index ee2c534..6a71033 100644
--- a/res/xml/security_settings_misc.xml
+++ b/res/xml/security_settings_misc.xml
@@ -64,7 +64,7 @@
android:key="encryption_and_credential"
android:title="@string/encryption_and_credential_settings_title"
android:summary="@string/encryption_and_credential_settings_summary"
- android:fragment="com.android.settings.EncryptionAndCredential"/>
+ android:fragment="com.android.settings.security.EncryptionAndCredential"/>
<Preference android:key="manage_trust_agents"
android:title="@string/manage_trust_agents"
diff --git a/src/com/android/settings/AppProgressPreference.java b/src/com/android/settings/AppProgressPreference.java
deleted file mode 100644
index 5822d8d..0000000
--- a/src/com/android/settings/AppProgressPreference.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.content.Context;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.util.AttributeSet;
-import android.widget.ProgressBar;
-
-public class AppProgressPreference extends Preference {
-
- private int mProgress;
-
- public AppProgressPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayoutResource(R.layout.preference_app);
- setWidgetLayoutResource(R.layout.widget_progress_bar);
- }
-
- public void setProgress(int amount) {
- mProgress = amount;
- notifyChanged();
- }
-
- @Override
- public void onBindViewHolder(PreferenceViewHolder view) {
- super.onBindViewHolder(view);
-
- final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
- progress.setProgress(mProgress);
- }
-}
diff --git a/src/com/android/settings/DeviceAdminSettings.java b/src/com/android/settings/DeviceAdminSettings.java
index 00f4d9c..350d731 100644
--- a/src/com/android/settings/DeviceAdminSettings.java
+++ b/src/com/android/settings/DeviceAdminSettings.java
@@ -43,9 +43,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
+import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto;
@@ -205,7 +205,7 @@
static class ViewHolder {
ImageView icon;
TextView name;
- CheckBox checkbox;
+ Switch checkbox;
TextView description;
}
@@ -291,10 +291,10 @@
private View newDeviceAdminView(ViewGroup parent) {
View v = mInflater.inflate(R.layout.device_admin_item, parent, false);
ViewHolder h = new ViewHolder();
- h.icon = (ImageView) v.findViewById(R.id.icon);
- h.name = (TextView) v.findViewById(R.id.name);
- h.checkbox = (CheckBox) v.findViewById(R.id.checkbox);
- h.description = (TextView) v.findViewById(R.id.description);
+ h.icon = v.findViewById(R.id.icon);
+ h.name = v.findViewById(R.id.name);
+ h.checkbox = v.findViewById(R.id.checkbox);
+ h.description = v.findViewById(R.id.description);
v.setTag(h);
return v;
}
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 9a3f047..d67758a 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -33,6 +33,7 @@
import com.android.settings.display.NightDisplayPreferenceController;
import com.android.settings.display.NightModePreferenceController;
import com.android.settings.display.ScreenSaverPreferenceController;
+import com.android.settings.display.ShowOperatorNamePreferenceController;
import com.android.settings.display.TapToWakePreferenceController;
import com.android.settings.display.ThemePreferenceController;
import com.android.settings.display.TimeoutPreferenceController;
@@ -98,6 +99,7 @@
controllers.add(new TapToWakePreferenceController(context));
controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT));
controllers.add(new VrDisplayPreferenceController(context));
+ controllers.add(new ShowOperatorNamePreferenceController(context));
controllers.add(new WallpaperPreferenceController(context));
controllers.add(new ThemePreferenceController(context));
controllers.add(new BrightnessLevelPreferenceController(context, lifecycle));
diff --git a/src/com/android/settings/DividerPreference.java b/src/com/android/settings/DividerPreference.java
deleted file mode 100644
index aec7d44..0000000
--- a/src/com/android/settings/DividerPreference.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 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;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.util.AttributeSet;
-
-@Deprecated
-public class DividerPreference extends Preference {
-
- private Boolean mAllowAbove;
- private Boolean mAllowBelow;
-
- public DividerPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DividerPreference, 0, 0);
- if (a.hasValue(R.styleable.DividerPreference_allowDividerAbove)) {
- mAllowAbove = a.getBoolean(R.styleable.DividerPreference_allowDividerAbove, false);
- }
- if (a.hasValue(R.styleable.DividerPreference_allowDividerBelow)) {
- mAllowBelow = a.getBoolean(R.styleable.DividerPreference_allowDividerBelow, false);
- }
- a.recycle();
- }
-
- public DividerPreference(Context context) {
- this(context, null /* attrs */);
- }
-
- public void setDividerAllowedAbove(boolean allowed) {
- mAllowAbove = allowed;
- notifyChanged();
- }
-
- public void setDividerAllowedBelow(boolean allowed) {
- mAllowBelow = allowed;
- notifyChanged();
- }
-
- @Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
- super.onBindViewHolder(holder);
- if (mAllowAbove != null) {
- holder.setDividerAllowedAbove(mAllowAbove);
- }
- if (mAllowBelow != null) {
- holder.setDividerAllowedBelow(mAllowBelow);
- }
- }
-}
diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java
index 7c3b54c..54ddf64 100644
--- a/src/com/android/settings/accounts/AccountPreferenceBase.java
+++ b/src/com/android/settings/accounts/AccountPreferenceBase.java
@@ -23,18 +23,16 @@
import android.content.SyncStatusObserver;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.v7.preference.PreferenceScreen;
import android.text.format.DateFormat;
import android.util.Log;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settingslib.accounts.AuthenticatorHelper;
+import com.android.settingslib.utils.ThreadUtils;
-import java.util.ArrayList;
import java.util.Date;
abstract class AccountPreferenceBase extends SettingsPreferenceFragment
@@ -46,8 +44,6 @@
public static final String AUTHORITIES_FILTER_KEY = "authorities";
public static final String ACCOUNT_TYPES_FILTER_KEY = "account_types";
- private final Handler mHandler = new Handler();
-
private UserManager mUm;
private Object mStatusChangeListenerHandle;
protected AuthenticatorHelper mAuthenticatorHelper;
@@ -118,29 +114,8 @@
ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
}
- private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() {
- public void onStatusChanged(int which) {
- mHandler.post(new Runnable() {
- public void run() {
- onSyncStateUpdated();
- }
- });
- }
- };
-
- public ArrayList<String> getAuthoritiesForAccountType(String type) {
- return mAuthenticatorHelper.getAuthoritiesForAccountType(type);
- }
-
- /**
- * Gets the preferences.xml file associated with a particular account type.
- * @param accountType the type of account
- * @return a PreferenceScreen inflated from accountPreferenceId.
- */
- public PreferenceScreen addPreferencesForType(final String accountType,
- PreferenceScreen parent) {
- return mAccountTypePreferenceLoader.addPreferencesForType(accountType, parent);
- }
+ private SyncStatusObserver mSyncStatusObserver =
+ which -> ThreadUtils.postOnMainThread(() -> onSyncStateUpdated());
public void updateAuthDescriptions() {
mAuthenticatorHelper.updateAuthDescriptions(getActivity());
diff --git a/src/com/android/settings/accounts/AccountTypePreference.java b/src/com/android/settings/accounts/AccountTypePreference.java
index 3e869fe..1d39473 100644
--- a/src/com/android/settings/accounts/AccountTypePreference.java
+++ b/src/com/android/settings/accounts/AccountTypePreference.java
@@ -16,6 +16,8 @@
package com.android.settings.accounts;
+import static android.content.Intent.EXTRA_USER;
+
import android.accounts.Account;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -25,12 +27,10 @@
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import com.android.settings.R;
import com.android.settings.Utils;
+import com.android.settings.widget.AppPreference;
-import static android.content.Intent.EXTRA_USER;
-
-public class AccountTypePreference extends Preference implements OnPreferenceClickListener {
+public class AccountTypePreference extends AppPreference implements OnPreferenceClickListener {
/**
* Title of the tile that is shown to the user.
* @attr ref android.R.styleable#PreferenceHeader_title
diff --git a/src/com/android/settings/accounts/ProviderPreference.java b/src/com/android/settings/accounts/ProviderPreference.java
index 817c3b6..81e0221 100644
--- a/src/com/android/settings/accounts/ProviderPreference.java
+++ b/src/com/android/settings/accounts/ProviderPreference.java
@@ -16,15 +16,14 @@
package com.android.settings.accounts;
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.support.v7.preference.Preference;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
/**
* ProviderPreference is used to display an image to the left of a provider name.
* The preference ultimately calls AccountManager.addAccount() for the account type.
@@ -35,6 +34,7 @@
public ProviderPreference(
Context context, String accountType, Drawable icon, CharSequence providerName) {
super(context);
+ setUseSmallIcon(true);
mAccountType = accountType;
setIcon(icon);
setPersistent(false);
diff --git a/src/com/android/settings/applications/PictureInPictureSettings.java b/src/com/android/settings/applications/PictureInPictureSettings.java
index 9b8a897..3dc8ab3 100644
--- a/src/com/android/settings/applications/PictureInPictureSettings.java
+++ b/src/com/android/settings/applications/PictureInPictureSettings.java
@@ -38,6 +38,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.notification.EmptyTextSettings;
+import com.android.settings.widget.AppPreference;
import com.android.settings.wrapper.ActivityInfoWrapper;
import com.android.settings.wrapper.UserManagerWrapper;
import com.android.settingslib.wrapper.PackageManagerWrapper;
@@ -176,7 +177,7 @@
final String packageName = appInfo.packageName;
final CharSequence label = appInfo.loadLabel(pm);
- final Preference pref = new Preference(prefContext);
+ final Preference pref = new AppPreference(prefContext);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId));
pref.setTitle(pm.getUserBadgedLabel(label, user));
pref.setSummary(PictureInPictureDetails.getPreferenceSummary(prefContext,
diff --git a/src/com/android/settings/applications/PremiumSmsAccess.java b/src/com/android/settings/applications/PremiumSmsAccess.java
index 94945a9..b6613f2 100644
--- a/src/com/android/settings/applications/PremiumSmsAccess.java
+++ b/src/com/android/settings/applications/PremiumSmsAccess.java
@@ -28,7 +28,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.telephony.SmsUsageMonitor;
-import com.android.settings.DividerPreference;
import com.android.settings.R;
import com.android.settings.applications.AppStateBaseBridge.Callback;
import com.android.settings.applications.AppStateSmsPremBridge.SmsState;
@@ -38,6 +37,7 @@
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
import com.android.settingslib.applications.ApplicationsState.Session;
+import com.android.settingslib.widget.FooterPreference;
import java.util.ArrayList;
@@ -141,11 +141,9 @@
screen.addPreference(smsPreference);
}
if (apps.size() != 0) {
- DividerPreference summary = new DividerPreference(getPrefContext());
- summary.setSelectable(false);
- summary.setSummary(R.string.premium_sms_warning);
- summary.setDividerAllowedAbove(true);
- screen.addPreference(summary);
+ FooterPreference footer = new FooterPreference(getPrefContext());
+ footer.setTitle(R.string.premium_sms_warning);
+ screen.addPreference(footer);
}
if (!usePreferenceScreenTitle()) {
diff --git a/src/com/android/settings/applications/ProcessStatsPreference.java b/src/com/android/settings/applications/ProcessStatsPreference.java
index 4ee0a04..034a68d 100644
--- a/src/com/android/settings/applications/ProcessStatsPreference.java
+++ b/src/com/android/settings/applications/ProcessStatsPreference.java
@@ -18,14 +18,13 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.graphics.drawable.ColorDrawable;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
-import com.android.settings.AppProgressPreference;
+import com.android.settings.widget.AppPreference;
-public class ProcessStatsPreference extends AppProgressPreference {
+public class ProcessStatsPreference extends AppPreference {
static final String TAG = "ProcessStatsPreference";
private ProcStatsPackageEntry mEntry;
diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java
index b6ae1ee..090a5a7 100644
--- a/src/com/android/settings/applications/RecentAppsPreferenceController.java
+++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java
@@ -40,6 +40,7 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.AppPreference;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.wrapper.PackageManagerWrapper;
@@ -230,7 +231,7 @@
boolean rebindPref = true;
Preference pref = appPreferences.remove(pkgName);
if (pref == null) {
- pref = new Preference(prefContext);
+ pref = new AppPreference(prefContext);
rebindPref = false;
}
pref.setKey(pkgName);
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
index 84c2ee5..7e73a9b 100644
--- a/src/com/android/settings/applications/RunningServiceDetails.java
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -21,7 +21,6 @@
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Debug;
-import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -37,6 +36,7 @@
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.utils.ThreadUtils;
import java.io.File;
import java.io.FileInputStream;
@@ -481,15 +481,12 @@
addDetailViews();
}
}
-
+
private void finish() {
- (new Handler()).post(new Runnable() {
- @Override
- public void run() {
- Activity a = getActivity();
- if (a != null) {
- a.onBackPressed();
- }
+ ThreadUtils.postOnMainThread(() -> {
+ final Activity a = getActivity();
+ if (a != null) {
+ a.onBackPressed();
}
});
}
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java b/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
index 6eb1ad8..7b27516 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
@@ -84,6 +84,11 @@
}
}
+ @Override
+ protected int getRadioButtonPreferenceCustomLayoutResId() {
+ return R.layout.preference_app;
+ }
+
protected ConfirmationDialogFragment newConfirmationDialogFragment(String selectedKey,
CharSequence confirmationMessage) {
final ConfirmationDialogFragment fragment = new ConfirmationDialogFragment();
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
index 91143b1..d016567 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
@@ -29,6 +29,7 @@
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.widget.GearPreference;
+import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.wrapper.PackageManagerWrapper;
@@ -53,6 +54,12 @@
public void updateState(Preference preference) {
final DefaultAppInfo app = getDefaultAppInfo();
CharSequence defaultAppLabel = getDefaultAppLabel();
+ if (preference instanceof TwoTargetPreference) {
+ // For use small icon because we are displaying an app preference.
+ // We only need to do this for TwoTargetPreference because the other prefs are
+ // already using AppPreference so their icon is already normalized.
+ ((TwoTargetPreference) preference).setUseSmallIcon(true);
+ }
if (!TextUtils.isEmpty(defaultAppLabel)) {
preference.setSummary(defaultAppLabel);
Utils.setSafeIcon(preference, getDefaultAppIcon());
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
index 00dedf9..97f6d4b 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
@@ -27,7 +27,6 @@
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
@@ -39,6 +38,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
@@ -59,7 +59,6 @@
* Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE.
*/
private DialogInterface.OnClickListener mCancelListener;
- private final Handler mHandler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -123,17 +122,17 @@
private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() {
@Override
public void onPackageAdded(String packageName, int uid) {
- mHandler.post(() -> update());
+ ThreadUtils.postOnMainThread(() -> update());
}
@Override
public void onPackageModified(String packageName) {
- mHandler.post(() -> update());
+ ThreadUtils.postOnMainThread(() -> update());
}
@Override
public void onPackageRemoved(String packageName, int uid) {
- mHandler.post(() -> update());
+ ThreadUtils.postOnMainThread(() -> update());
}
};
diff --git a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
index 9ac2167..e968b1c 100644
--- a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
+++ b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
@@ -22,6 +22,7 @@
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,33 +39,39 @@
private final TextView mAppName;
private final ImageView mAppIcon;
+ private final boolean mKeepStableHeight;
+
+ @VisibleForTesting
+ View mSummaryContainer;
@VisibleForTesting
final TextView mSummary;
@VisibleForTesting
final TextView mDisabled;
- ApplicationViewHolder(View itemView) {
+
+ ApplicationViewHolder(View itemView, boolean keepStableHeight) {
super(itemView);
mAppName = itemView.findViewById(android.R.id.title);
mAppIcon = itemView.findViewById(android.R.id.icon);
- mSummary = itemView.findViewById(R.id.widget_text1);
- mDisabled = itemView.findViewById(R.id.widget_text2);
+ mSummaryContainer = itemView.findViewById(R.id.summary_container);
+ mSummary = itemView.findViewById(android.R.id.summary);
+ mDisabled = itemView.findViewById(R.id.appendix);
+ mKeepStableHeight = keepStableHeight;
}
- static View newView(LayoutInflater inflater, ViewGroup parent) {
- final View root = LayoutInflater.from(parent.getContext())
+ static View newView(ViewGroup parent) {
+ return LayoutInflater.from(parent.getContext())
.inflate(R.layout.preference_app, parent, false);
- inflater.inflate(R.layout.widget_text_views,
- root.findViewById(android.R.id.widget_frame));
- return root;
}
void setSummary(CharSequence summary) {
mSummary.setText(summary);
+ updateSummaryContainer();
}
void setSummary(@StringRes int summary) {
mSummary.setText(summary);
+ updateSummaryContainer();
}
void setEnabled(boolean isEnabled) {
@@ -78,6 +85,10 @@
mAppName.setText(title);
}
+ void setIcon(int drawableRes) {
+ mAppIcon.setImageResource(drawableRes);
+ }
+
void setIcon(Drawable icon) {
if (icon == null) {
return;
@@ -96,6 +107,17 @@
} else {
mDisabled.setVisibility(View.GONE);
}
+ updateSummaryContainer();
+ }
+
+ void updateSummaryContainer() {
+ if (mKeepStableHeight) {
+ mSummaryContainer.setVisibility(View.VISIBLE);
+ return;
+ }
+ final boolean hasContent =
+ !TextUtils.isEmpty(mDisabled.getText()) || !TextUtils.isEmpty(mSummary.getText());
+ mSummaryContainer.setVisibility(hasContent ? View.VISIBLE : View.GONE);
}
void updateSizeText(AppEntry entry, CharSequence invalidSizeStr, int whichSize) {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 33762e4..7dc8951 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -412,20 +412,6 @@
return null;
}
- private boolean isFastScrollEnabled() {
- switch (mListType) {
- case LIST_TYPE_MAIN:
- case LIST_TYPE_NOTIFICATION:
- case LIST_TYPE_STORAGE:
- case LIST_TYPE_GAMES:
- case LIST_TYPE_MOVIES:
- case LIST_TYPE_PHOTOGRAPHY:
- return mSortOrder == R.id.sort_order_alpha;
- default:
- return false;
- }
- }
-
@Override
public int getMetricsCategory() {
switch (mListType) {
@@ -924,9 +910,9 @@
@Override
public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- final View view = ApplicationViewHolder.newView(
- LayoutInflater.from(parent.getContext()), parent);
- return new ApplicationViewHolder(view);
+ final View view = ApplicationViewHolder.newView(parent);
+ return new ApplicationViewHolder(view,
+ shouldUseStableItemHeight(mManageApplications.mListType));
}
@Override
@@ -994,7 +980,21 @@
});
}
- static private boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
+ @VisibleForTesting
+ static boolean shouldUseStableItemHeight(int listType) {
+ switch (listType) {
+ case LIST_TYPE_NOTIFICATION:
+ // Most entries in notification type has no summary. Don't use stable height
+ // so height is short for most entries.
+ return false;
+ default:
+ // Other types have non-empty summary, so keep the height as we expect summary
+ // to fill in.
+ return true;
+ }
+ }
+
+ private static boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
if (info1 == null || info2 == null) {
return false;
}
diff --git a/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java b/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java
index 2a2ac3b..14d08aa 100644
--- a/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java
+++ b/src/com/android/settings/applications/manageapplications/MusicViewHolderController.java
@@ -19,7 +19,6 @@
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.InsetDrawable;
import android.os.UserHandle;
import android.provider.DocumentsContract;
import android.support.annotation.WorkerThread;
@@ -39,7 +38,6 @@
private static final String TAG = "MusicViewHolderCtrl";
private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
- private static final int INSET_SIZE = 24; // dp
private Context mContext;
private StorageStatsSource mSource;
@@ -73,8 +71,7 @@
@Override
public void setupView(ApplicationViewHolder holder) {
- holder.setIcon(
- new InsetDrawable(mContext.getDrawable(R.drawable.ic_headset_24dp), INSET_SIZE));
+ holder.setIcon(R.drawable.ic_headset_24dp);
holder.setTitle(mContext.getText(R.string.audio_files_title));
holder.setSummary(Formatter.formatFileSize(mContext, mMusicSize));
}
diff --git a/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java b/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java
index fafa875..73330e9 100644
--- a/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java
+++ b/src/com/android/settings/applications/manageapplications/PhotosViewHolderController.java
@@ -19,7 +19,6 @@
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.InsetDrawable;
import android.os.UserHandle;
import android.support.annotation.WorkerThread;
import android.text.format.Formatter;
@@ -36,7 +35,6 @@
private static final String TAG = "PhotosViewHolderCtrl";
private static final String IMAGE_MIME_TYPE = "image/*";
- private static final int INSET_SIZE = 24; // dp
private Context mContext;
private StorageStatsSource mSource;
@@ -72,8 +70,7 @@
@Override
public void setupView(ApplicationViewHolder holder) {
- holder.setIcon(
- new InsetDrawable(mContext.getDrawable(R.drawable.ic_photo_library), INSET_SIZE));
+ holder.setIcon(R.drawable.ic_photo_library);
holder.setTitle(mContext.getText(R.string.storage_detail_images));
holder.setSummary(Formatter.formatFileSize(mContext, mFilesSize));
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
index 04e9f5a..df7b601 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
@@ -51,8 +51,8 @@
}
protected void setHeaderProperties() {
- final Pair<Drawable, String> pair = Utils.getBtClassDrawableWithDescription(
- mContext, mCachedDevice,
+ final Pair<Drawable, String> pair = com.android.settingslib.bluetooth.Utils
+ .getBtClassDrawableWithDescription(mContext, mCachedDevice,
mContext.getResources().getFraction(R.fraction.bt_battery_scale_fraction, 1, 1));
String summaryText = mCachedDevice.getConnectionSummary();
mHeaderController.setLabel(mCachedDevice.getName());
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 94ba478..043cb95 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -122,8 +122,8 @@
// Null check is done at the framework
setSummary(mCachedDevice.getConnectionSummary());
- final Pair<Drawable, String> pair = Utils.getBtClassDrawableWithDescription(getContext(),
- mCachedDevice);
+ final Pair<Drawable, String> pair = com.android.settingslib.bluetooth.Utils
+ .getBtClassDrawableWithDescription(getContext(), mCachedDevice);
if (pair.first != null) {
setIcon(pair.first);
contentDescription = pair.second;
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index 0ecf62d..d5d4e50 100755
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -17,31 +17,19 @@
package com.android.settings.bluetooth;
import android.app.AlertDialog;
-import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IdRes;
import android.support.annotation.VisibleForTesting;
-import android.util.Pair;
import android.widget.Toast;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.HidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
-import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.Utils.ErrorListener;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
-
-import java.util.List;
/**
* Utils is a helper class that contains constants for various
@@ -57,16 +45,16 @@
public static int getConnectionStateSummary(int connectionState) {
switch (connectionState) {
- case BluetoothProfile.STATE_CONNECTED:
- return R.string.bluetooth_connected;
- case BluetoothProfile.STATE_CONNECTING:
- return R.string.bluetooth_connecting;
- case BluetoothProfile.STATE_DISCONNECTED:
- return R.string.bluetooth_disconnected;
- case BluetoothProfile.STATE_DISCONNECTING:
- return R.string.bluetooth_disconnecting;
- default:
- return 0;
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_connected;
+ case BluetoothProfile.STATE_CONNECTING:
+ return R.string.bluetooth_connecting;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_disconnected;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return R.string.bluetooth_disconnecting;
+ default:
+ return 0;
}
}
@@ -154,81 +142,4 @@
}
};
- static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
- CachedBluetoothDevice cachedDevice) {
- return getBtClassDrawableWithDescription(context, cachedDevice, 1 /* iconScale */);
- }
-
- static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
- CachedBluetoothDevice cachedDevice, float iconScale) {
- BluetoothClass btClass = cachedDevice.getBtClass();
- final int level = cachedDevice.getBatteryLevel();
- if (btClass != null) {
- switch (btClass.getMajorDeviceClass()) {
- case BluetoothClass.Device.Major.COMPUTER:
- return new Pair<>(getBluetoothDrawable(context, R.drawable.ic_bt_laptop, level,
- iconScale),
- context.getString(R.string.bluetooth_talkback_computer));
-
- case BluetoothClass.Device.Major.PHONE:
- return new Pair<>(
- getBluetoothDrawable(context, R.drawable.ic_bt_cellphone, level,
- iconScale),
- context.getString(R.string.bluetooth_talkback_phone));
-
- case BluetoothClass.Device.Major.PERIPHERAL:
- return new Pair<>(
- getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass),
- level, iconScale),
- context.getString(R.string.bluetooth_talkback_input_peripheral));
-
- case BluetoothClass.Device.Major.IMAGING:
- return new Pair<>(
- getBluetoothDrawable(context, R.drawable.ic_settings_print, level,
- iconScale),
- context.getString(R.string.bluetooth_talkback_imaging));
-
- default:
- // unrecognized device class; continue
- }
- }
-
- List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
- for (LocalBluetoothProfile profile : profiles) {
- int resId = profile.getDrawableResource(btClass);
- if (resId != 0) {
- return new Pair<>(getBluetoothDrawable(context, resId, level, iconScale), null);
- }
- }
- if (btClass != null) {
- if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
- return new Pair<>(
- getBluetoothDrawable(context, R.drawable.ic_bt_headset_hfp, level,
- iconScale),
- context.getString(R.string.bluetooth_talkback_headset));
- }
- if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
- return new Pair<>(
- getBluetoothDrawable(context, R.drawable.ic_bt_headphones_a2dp, level,
- iconScale),
- context.getString(R.string.bluetooth_talkback_headphone));
- }
- }
- return new Pair<>(
- getBluetoothDrawable(context, R.drawable.ic_settings_bluetooth, level, iconScale),
- context.getString(R.string.bluetooth_talkback_bluetooth));
- }
-
- @VisibleForTesting
- static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId,
- int batteryLevel, float iconScale) {
- if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- return BluetoothDeviceLayerDrawable.createLayerDrawable(context, resId, batteryLevel,
- iconScale);
- } else if (resId != 0) {
- return context.getDrawable(resId);
- } else {
- return null;
- }
- }
}
diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
index 8ef8c0e..5b95d66 100644
--- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java
+++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
@@ -85,7 +85,7 @@
}
public static boolean usePreferenceScreenTitle() {
- return FeatureFlagUtils.isEnabled(FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE);
+ return FeatureFlagUtils.isEnabled(FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE) || true;
}
protected final Context getPrefContext() {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index e7f36e1..6796c26 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -183,6 +183,9 @@
}
}
+ @Override
+ protected abstract int getPreferenceScreenResId();
+
protected <T extends AbstractPreferenceController> T getPreferenceController(Class<T> clazz) {
AbstractPreferenceController controller = mPreferenceControllers.get(clazz);
return (T) controller;
diff --git a/src/com/android/settings/dashboard/SummaryLoader.java b/src/com/android/settings/dashboard/SummaryLoader.java
index 4586a55..5816bba8 100644
--- a/src/com/android/settings/dashboard/SummaryLoader.java
+++ b/src/com/android/settings/dashboard/SummaryLoader.java
@@ -35,6 +35,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.utils.ThreadUtils;
import java.lang.reflect.Field;
import java.util.List;
@@ -52,7 +53,6 @@
private final String mCategoryKey;
private final Worker mWorker;
- private final Handler mHandler;
private final HandlerThread mWorkerThread;
private SummaryConsumer mSummaryConsumer;
@@ -64,7 +64,6 @@
mDashboardFeatureProvider = FeatureFactory.getFactory(activity)
.getDashboardFeatureProvider(activity);
mCategoryKey = null;
- mHandler = new Handler();
mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND);
mWorkerThread.start();
mWorker = new Worker(mWorkerThread.getLooper());
@@ -82,7 +81,6 @@
mDashboardFeatureProvider = FeatureFactory.getFactory(activity)
.getDashboardFeatureProvider(activity);
mCategoryKey = categoryKey;
- mHandler = new Handler();
mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND);
mWorkerThread.start();
mWorker = new Worker(mWorkerThread.getLooper());
@@ -112,25 +110,22 @@
public void setSummary(SummaryProvider provider, final CharSequence summary) {
final ComponentName component = mSummaryProviderMap.get(provider);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ ThreadUtils.postOnMainThread(() -> {
- final Tile tile = getTileFromCategory(
- mDashboardFeatureProvider.getTilesForCategory(mCategoryKey), component);
+ final Tile tile = getTileFromCategory(
+ mDashboardFeatureProvider.getTilesForCategory(mCategoryKey), component);
- if (tile == null) {
- if (DEBUG) {
- Log.d(TAG, "Can't find tile for " + component);
- }
- return;
- }
+ if (tile == null) {
if (DEBUG) {
- Log.d(TAG, "setSummary " + tile.title + " - " + summary);
+ Log.d(TAG, "Can't find tile for " + component);
}
-
- updateSummaryIfNeeded(tile, summary);
+ return;
}
+ if (DEBUG) {
+ Log.d(TAG, "setSummary " + tile.title + " - " + summary);
+ }
+
+ updateSummaryIfNeeded(tile, summary);
});
}
diff --git a/src/com/android/settings/dashboard/SupportFragment.java b/src/com/android/settings/dashboard/SupportFragment.java
index 8a1a79b..fcc9f78 100644
--- a/src/com/android/settings/dashboard/SupportFragment.java
+++ b/src/com/android/settings/dashboard/SupportFragment.java
@@ -27,8 +27,6 @@
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -40,6 +38,7 @@
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.overlay.SupportFeatureProvider;
+import com.android.settingslib.utils.ThreadUtils;
/**
* Fragment for support tab in SettingsGoogle.
@@ -47,7 +46,6 @@
public final class SupportFragment extends InstrumentedFragment implements View.OnClickListener,
OnAccountsUpdateListener {
- private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ConnectivityManager.NetworkCallback mNetworkCallback =
new ConnectivityManager.NetworkCallback() {
@@ -152,12 +150,9 @@
}
private void postConnectivityChanged() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mSupportItemAdapter != null) {
- mSupportItemAdapter.setHasInternet(hasInternet());
- }
+ ThreadUtils.postOnMainThread(() -> {
+ if (mSupportItemAdapter != null) {
+ mSupportItemAdapter.setHasInternet(hasInternet());
}
});
}
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
index 783987d..1be31b4 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
@@ -92,7 +92,7 @@
}
private static boolean isV2Enabled() {
- return FeatureFlagUtils.isEnabled(FEATURE_FLAG_SUGGESTIONS_V2);
+ return FeatureFlagUtils.isEnabled(FEATURE_FLAG_SUGGESTIONS_V2) || true;
}
@Override
diff --git a/src/com/android/settings/datausage/AppDataUsagePreference.java b/src/com/android/settings/datausage/AppDataUsagePreference.java
index 35f1fe2..f0fd755 100644
--- a/src/com/android/settings/datausage/AppDataUsagePreference.java
+++ b/src/com/android/settings/datausage/AppDataUsagePreference.java
@@ -15,19 +15,18 @@
package com.android.settings.datausage;
import android.content.Context;
-import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.text.format.Formatter;
import android.view.View;
import android.widget.ProgressBar;
-import com.android.settings.R;
+import com.android.settings.widget.AppPreference;
import com.android.settingslib.AppItem;
import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider;
import com.android.settingslib.utils.ThreadUtils;
-public class AppDataUsagePreference extends Preference {
+public class AppDataUsagePreference extends AppPreference {
private final AppItem mItem;
private final int mPercent;
@@ -38,8 +37,6 @@
super(context);
mItem = item;
mPercent = percent;
- setLayoutResource(R.layout.data_usage_item);
- setWidgetLayoutResource(R.layout.widget_progress_bar);
if (item.restricted && item.total <= 0) {
setSummary(com.android.settings.R.string.data_usage_app_restricted);
@@ -60,7 +57,6 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
-
final ProgressBar progress = (ProgressBar) holder.findViewById(
android.R.id.progress);
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index f37a3c3..041a81f 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -14,24 +14,23 @@
package com.android.settings.datausage;
+import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
import android.content.Context;
import android.net.INetworkPolicyListener;
import android.net.NetworkPolicyManager;
-import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.util.SparseIntArray;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
-import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-
public class DataSaverBackend {
private static final String TAG = "DataSaverBackend";
@@ -39,7 +38,6 @@
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
private final NetworkPolicyManager mPolicyManager;
private final ArrayList<Listener> mListeners = new ArrayList<>();
private SparseIntArray mUidPolicies = new SparseIntArray();
@@ -195,7 +193,7 @@
@Override
public void onUidPoliciesChanged(final int uid, final int uidPolicies) {
- mHandler.post(() -> handleUidPoliciesChanged(uid, uidPolicies));
+ ThreadUtils.postOnMainThread(() -> handleUidPoliciesChanged(uid, uidPolicies));
}
@Override
@@ -204,7 +202,7 @@
@Override
public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
- mHandler.post(() -> handleRestrictBackgroundChanged(isDataSaving));
+ ThreadUtils.postOnMainThread(() -> handleRestrictBackgroundChanged(isDataSaving));
}
};
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccess.java b/src/com/android/settings/datausage/UnrestrictedDataAccess.java
index 8fad986..6771abc 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccess.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccess.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
-import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.view.Menu;
@@ -34,6 +33,7 @@
import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.AppSwitchPreference;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
@@ -59,11 +59,7 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setAnimationAllowed(true);
- if (usePreferenceScreenTitle()) {
- addPreferencesFromResource(R.xml.unrestricted_data_access_settings);
- } else {
- setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
- }
+ addPreferencesFromResource(R.xml.unrestricted_data_access_settings);
mApplicationsState = ApplicationsState.getInstance(
(Application) getContext().getApplicationContext());
mDataSaverBackend = new DataSaverBackend(getContext());
@@ -239,7 +235,8 @@
return app != null && UserHandle.isApp(app.info.uid);
}
- private class AccessPreference extends SwitchPreference implements DataSaverBackend.Listener {
+ private class AccessPreference extends AppSwitchPreference
+ implements DataSaverBackend.Listener {
private final AppEntry mEntry;
private final DataUsageState mState;
diff --git a/src/com/android/settings/development/AppViewHolder.java b/src/com/android/settings/development/AppViewHolder.java
index 7a90bcf..1082efe 100644
--- a/src/com/android/settings/development/AppViewHolder.java
+++ b/src/com/android/settings/development/AppViewHolder.java
@@ -18,7 +18,6 @@
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -37,23 +36,21 @@
static public AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.preference_app, null);
- inflater.inflate(R.layout.widget_text_views,
- (ViewGroup) convertView.findViewById(android.R.id.widget_frame));
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
AppViewHolder holder = new AppViewHolder();
holder.rootView = convertView;
- holder.appName = (TextView) convertView.findViewById(android.R.id.title);
- holder.appIcon = (ImageView) convertView.findViewById(android.R.id.icon);
- holder.summary = (TextView) convertView.findViewById(R.id.widget_text1);
- holder.disabled = (TextView) convertView.findViewById(R.id.widget_text2);
+ holder.appName = convertView.findViewById(android.R.id.title);
+ holder.appIcon = convertView.findViewById(android.R.id.icon);
+ holder.summary = convertView.findViewById(android.R.id.summary);
+ holder.disabled = convertView.findViewById(R.id.appendix);
convertView.setTag(holder);
return holder;
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
- return (AppViewHolder)convertView.getTag();
+ return (AppViewHolder) convertView.getTag();
}
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/development/OemLockInfoDialog.java b/src/com/android/settings/development/OemLockInfoDialog.java
new file mode 100644
index 0000000..6d75812
--- /dev/null
+++ b/src/com/android/settings/development/OemLockInfoDialog.java
@@ -0,0 +1,53 @@
+/*
+ * 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.development;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class OemLockInfoDialog extends InstrumentedDialogFragment {
+
+ private static final String TAG = "OemLockInfoDialog";
+
+ public static void show(Fragment host) {
+ final FragmentManager manager = host.getChildFragmentManager();
+ if (manager.findFragmentByTag(TAG) == null) {
+ final OemLockInfoDialog dialog = new OemLockInfoDialog();
+ dialog.show(manager, TAG);
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DIALOG_OEM_LOCK_INFO;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setMessage(R.string.oem_lock_info_message);
+
+ return builder.create();
+ }
+}
diff --git a/src/com/android/settings/development/OemUnlockPreferenceController.java b/src/com/android/settings/development/OemUnlockPreferenceController.java
index 91994c2..f486b3d 100644
--- a/src/com/android/settings/development/OemUnlockPreferenceController.java
+++ b/src/com/android/settings/development/OemUnlockPreferenceController.java
@@ -90,6 +90,7 @@
}
} else {
mOemLockManager.setOemUnlockAllowedByUser(false);
+ OemLockInfoDialog.show(mFragment);
}
return true;
}
diff --git a/src/com/android/settings/display/ShowOperatorNamePreferenceController.java b/src/com/android/settings/display/ShowOperatorNamePreferenceController.java
new file mode 100644
index 0000000..4573c6d
--- /dev/null
+++ b/src/com/android/settings/display/ShowOperatorNamePreferenceController.java
@@ -0,0 +1,58 @@
+/*
+ * 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.display;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public class ShowOperatorNamePreferenceController extends AbstractPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
+
+ public ShowOperatorNamePreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mContext.getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SHOW_OPERATOR_NAME;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ boolean value = (Boolean) newValue;
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ KEY_SHOW_OPERATOR_NAME, value ? 1 : 0);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ int value = Settings.Secure.getInt(mContext.getContentResolver(),
+ KEY_SHOW_OPERATOR_NAME, 1);
+ ((SwitchPreference) preference).setChecked(value != 0);
+ }
+}
diff --git a/src/com/android/settings/enterprise/ApplicationListPreferenceController.java b/src/com/android/settings/enterprise/ApplicationListPreferenceController.java
index dce6f57..8be7944 100644
--- a/src/com/android/settings/enterprise/ApplicationListPreferenceController.java
+++ b/src/com/android/settings/enterprise/ApplicationListPreferenceController.java
@@ -22,11 +22,11 @@
import android.support.v7.preference.PreferenceScreen;
import android.util.IconDrawableFactory;
-import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.applications.UserAppInfo;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.AppPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.List;
@@ -68,8 +68,7 @@
final Context prefContext = mParent.getPreferenceManager().getContext();
for (int position = 0; position < result.size(); position++) {
final UserAppInfo item = result.get(position);
- final Preference preference = new Preference(prefContext);
- preference.setLayoutResource(R.layout.preference_app);
+ final Preference preference = new AppPreference(prefContext);
preference.setTitle(item.appInfo.loadLabel(mPm));
preference.setIcon(iconDrawableFactory.getBadgedIcon(item.appInfo));
preference.setOrder(position);
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
index 03a78ea..0927299 100644
--- a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
@@ -20,7 +20,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.os.Handler;
import android.os.UserHandle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
@@ -36,6 +35,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.users.UserFeatureProvider;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -100,7 +100,7 @@
userMap.put(typeOfDefault, applicationInfos);
}
}
- new Handler(mContext.getMainLooper()).post(() -> { updateUi(); });
+ ThreadUtils.postOnMainThread(() -> updateUi());
}
@Override
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
index 10bc9e3..756f826 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
@@ -32,6 +32,8 @@
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
@@ -74,6 +76,9 @@
*/
private static final int ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN = 3;
+ private static final VibrationEffect VIBRATE_EFFECT_ERROR =
+ VibrationEffect.createWaveform(new long[] {0, 5, 55, 60}, -1);
+
private ProgressBar mProgressBar;
private ObjectAnimator mProgressAnim;
private TextView mStartMessage;
@@ -90,6 +95,7 @@
private int mIndicatorBackgroundRestingColor;
private int mIndicatorBackgroundActivatedColor;
private boolean mRestoring;
+ private Vibrator mVibrator;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -100,6 +106,7 @@
mRepeatMessage = (TextView) findViewById(R.id.repeat_message);
mErrorText = (TextView) findViewById(R.id.error_text);
mProgressBar = (ProgressBar) findViewById(R.id.fingerprint_progress_bar);
+ mVibrator = getSystemService(Vibrator.class);
Button skipButton = findViewById(R.id.skip_button);
skipButton.setOnClickListener(this);
@@ -368,6 +375,9 @@
mErrorText.setAlpha(1f);
mErrorText.setTranslationY(0f);
}
+ if (isResumed()) {
+ mVibrator.vibrate(VIBRATE_EFFECT_ERROR);
+ }
}
private void clearError() {
@@ -378,12 +388,7 @@
R.dimen.fingerprint_error_text_disappear_distance))
.setDuration(100)
.setInterpolator(mFastOutLinearInInterpolator)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mErrorText.setVisibility(View.INVISIBLE);
- }
- })
+ .withEndAction(() -> mErrorText.setVisibility(View.INVISIBLE))
.start();
}
}
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index d96db68..819846a 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -45,6 +45,7 @@
import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.wrapper.DevicePolicyManagerWrapper;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
@@ -360,6 +361,9 @@
@Override
public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
+ final AnomalyUtils anomalyUtils = AnomalyUtils.getInstance(getContext());
+ anomalyUtils.logAnomalies(mMetricsFeatureProvider, data,
+ MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL);
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
}
diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
index 05dad27..93e33cc 100644
--- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java
+++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
@@ -19,15 +19,13 @@
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.Utils;
+import com.android.settings.widget.AppPreference;
/**
* Custom preference for displaying battery usage info as a bar and an icon on
@@ -36,8 +34,7 @@
* The battery usage info could be usage percentage or usage time. The preference
* won't show any icon if it is null.
*/
-public class PowerGaugePreference extends Preference {
- private final int mIconSize;
+public class PowerGaugePreference extends AppPreference {
private BatteryEntry mInfo;
private CharSequence mContentDescription;
@@ -64,7 +61,6 @@
setWidgetLayoutResource(R.layout.preference_widget_summary);
mInfo = info;
mContentDescription = contentDescription;
- mIconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
mShowAnomalyIcon = false;
}
@@ -107,8 +103,6 @@
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
- ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
- icon.setLayoutParams(new LinearLayout.LayoutParams(mIconSize, mIconSize));
final TextView subtitle = (TextView) view.findViewById(R.id.widget_summary);
subtitle.setText(mProgress);
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
index adadb78..6ed92a7 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Bundle;
@@ -250,7 +251,7 @@
boolean shouldHideCategory(PowerUsageData powerUsageData) {
return powerUsageData.usageType == UsageType.UNACCOUNTED
|| powerUsageData.usageType == UsageType.OVERCOUNTED
- || (powerUsageData.usageType == UsageType.USER && mUserManager.getUserCount() == 1)
+ || (powerUsageData.usageType == UsageType.USER && isSingleNormalUser())
|| (powerUsageData.usageType == UsageType.CELL
&& !DataUsageUtils.hasMobileData(getContext()));
}
@@ -373,6 +374,18 @@
mBatteryUtils = batteryUtils;
}
+ @VisibleForTesting
+ boolean isSingleNormalUser() {
+ int count = 0;
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ if (userInfo.isEnabled() && !userInfo.isManagedProfile()) {
+ count++;
+ }
+ }
+
+ return count == 1;
+ }
+
/**
* Class that contains data used in {@link PowerGaugePreference}.
*/
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java b/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java
index bf4c2af..0d73511 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java
@@ -125,8 +125,7 @@
@Override
public int getMetricsCategory() {
- //TODO(b/37681923): add correct metrics category
- return 0;
+ return MetricsProto.MetricsEvent.FUELGAUGE_ANOMALY_DETAIL;
}
void refreshUi() {
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index ac50e66..dd8c169 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -67,6 +67,7 @@
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment.AnomalyDialogListener;
import com.android.settings.fuelgauge.anomaly.AnomalyLoader;
import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
+import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -148,6 +149,10 @@
@Override
public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
+ final AnomalyUtils anomalyUtils = AnomalyUtils.getInstance(getContext());
+ anomalyUtils.logAnomalies(mMetricsFeatureProvider, data,
+ MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY);
+
// show high usage preference if possible
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java
index 69d03b9..538f5b1 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
@@ -79,8 +80,7 @@
@Override
public int getMetricsCategory() {
- // TODO(b/37681923): add anomaly metric id
- return 0;
+ return MetricsProto.MetricsEvent.DIALOG_HANDLE_ANOMALY;
}
@Override
@@ -100,9 +100,13 @@
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle bundle = getArguments();
- mAnomaly = bundle.getParcelable(ARG_ANOMALY);
-
final Context context = getContext();
+ final AnomalyUtils anomalyUtils = AnomalyUtils.getInstance(context);
+
+ mAnomaly = bundle.getParcelable(ARG_ANOMALY);
+ anomalyUtils.logAnomaly(mMetricsFeatureProvider, mAnomaly,
+ MetricsProto.MetricsEvent.DIALOG_HANDLE_ANOMALY);
+
final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly);
switch (anomalyAction.getActionType()) {
case Anomaly.AnomalyActionType.FORCE_STOP:
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
index 491a3b4..39d51dc0 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
@@ -19,8 +19,12 @@
import android.content.Context;
import android.os.Build;
import android.support.annotation.VisibleForTesting;
+import android.util.Pair;
+import android.util.SparseIntArray;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
import com.android.settings.fuelgauge.anomaly.action.LocationCheckAction;
@@ -40,6 +44,17 @@
private Context mContext;
private static AnomalyUtils sInstance;
+ private static final SparseIntArray mMetricArray;
+ static {
+ mMetricArray = new SparseIntArray();
+ mMetricArray.append(Anomaly.AnomalyType.WAKE_LOCK,
+ MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKELOCK);
+ mMetricArray.append(Anomaly.AnomalyType.WAKEUP_ALARM,
+ MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKEUP_ALARM);
+ mMetricArray.append(Anomaly.AnomalyType.BLUETOOTH_SCAN,
+ MetricsProto.MetricsEvent.ANOMALY_TYPE_UNOPTIMIZED_BT);
+ }
+
@VisibleForTesting
AnomalyUtils(Context context) {
mContext = context.getApplicationContext();
@@ -118,4 +133,39 @@
return anomalies;
}
+ /**
+ * Log the list of {@link Anomaly} using {@link MetricsFeatureProvider}, which contains
+ * anomaly type, package name, field_context, field_action_type
+ *
+ * @param provider provider to do the logging
+ * @param anomalies contains the data to log
+ * @param contextId which page invoke this logging
+ * @see #logAnomaly(MetricsFeatureProvider, Anomaly, int)
+ */
+ public void logAnomalies(MetricsFeatureProvider provider, List<Anomaly> anomalies,
+ int contextId) {
+ for (int i = 0, size = anomalies.size(); i < size; i++) {
+ logAnomaly(provider, anomalies.get(i), contextId);
+ }
+ }
+
+ /**
+ * Log the {@link Anomaly} using {@link MetricsFeatureProvider}, which contains
+ * anomaly type, package name, field_context, field_action_type
+ *
+ * @param provider provider to do the logging
+ * @param anomaly contains the data to log
+ * @param contextId which page invoke this logging
+ * @see #logAnomalies(MetricsFeatureProvider, List, int)
+ */
+ public void logAnomaly(MetricsFeatureProvider provider, Anomaly anomaly, int contextId) {
+ provider.action(
+ mContext,
+ mMetricArray.get(anomaly.type, MetricsProto.MetricsEvent.VIEW_UNKNOWN),
+ anomaly.packageName,
+ Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, contextId),
+ Pair.create(MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
+ getAnomalyAction(anomaly).getActionType()));
+ }
+
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
index 87212e7..3ee89d1 100644
--- a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
+++ b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java
@@ -58,5 +58,6 @@
*/
public abstract boolean isActionActive(Anomaly anomaly);
+ @Anomaly.AnomalyActionType
public abstract int getActionType();
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/action/StopAndBackgroundCheckAction.java b/src/com/android/settings/fuelgauge/anomaly/action/StopAndBackgroundCheckAction.java
index dba221a..2c799ee 100644
--- a/src/com/android/settings/fuelgauge/anomaly/action/StopAndBackgroundCheckAction.java
+++ b/src/com/android/settings/fuelgauge/anomaly/action/StopAndBackgroundCheckAction.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.support.annotation.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.anomaly.Anomaly;
/**
@@ -34,6 +35,7 @@
public StopAndBackgroundCheckAction(Context context) {
this(context, new ForceStopAction(context), new BackgroundCheckAction(context));
+ mActionMetricKey = MetricsProto.MetricsEvent.ACTION_APP_STOP_AND_BACKGROUND_CHECK;
}
@VisibleForTesting
@@ -46,6 +48,7 @@
@Override
public void handlePositiveAction(Anomaly anomaly, int metricsKey) {
+ super.handlePositiveAction(anomaly, metricsKey);
mForceStopAction.handlePositiveAction(anomaly, metricsKey);
mBackgroundCheckAction.handlePositiveAction(anomaly, metricsKey);
}
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index 39d5f6d..7d61f7f 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -43,9 +43,9 @@
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
+import com.android.settings.widget.AppPreference;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.location.RecentLocationApps;
@@ -211,21 +211,19 @@
List<Preference> recentLocationPrefs = new ArrayList<>(recentLocationRequests.size());
for (final RecentLocationApps.Request request : recentLocationRequests) {
- RestrictedPreference pref = new RestrictedPreference(getPrefContext());
+ final AppPreference pref = new AppPreference(getPrefContext());
pref.setSummary(request.contentDescription);
pref.setIcon(request.icon);
pref.setTitle(request.label);
pref.setOnPreferenceClickListener(
new PackageEntryClickedListener(request.packageName, request.userHandle));
recentLocationPrefs.add(pref);
-
}
if (recentLocationRequests.size() > 0) {
addPreferencesSorted(recentLocationPrefs, mCategoryRecentLocationRequests);
} else {
// If there's no item to display, add a "No recent apps" item.
- Preference banner = new Preference(getPrefContext());
- banner.setLayoutResource(R.layout.location_list_no_item);
+ Preference banner = new AppPreference(getPrefContext());
banner.setTitle(R.string.location_no_recent_apps);
banner.setSelectable(false);
mCategoryRecentLocationRequests.addPreference(banner);
diff --git a/src/com/android/settings/location/SettingsInjector.java b/src/com/android/settings/location/SettingsInjector.java
index 428745b..c6d1f37 100644
--- a/src/com/android/settings/location/SettingsInjector.java
+++ b/src/com/android/settings/location/SettingsInjector.java
@@ -40,6 +40,8 @@
import android.util.Log;
import android.util.Xml;
+import com.android.settings.widget.AppPreference;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -268,7 +270,7 @@
PackageManager pm = mContext.getPackageManager();
Drawable appIcon = pm.getDrawable(info.packageName, info.iconId, null);
Drawable icon = pm.getUserBadgedIcon(appIcon, info.mUserHandle);
- Preference pref = new Preference(prefContext);
+ Preference pref = new AppPreference(prefContext);
pref.setTitle(info.title);
pref.setSummary(null);
pref.setIcon(icon);
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index 763fb72..95513e0 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -23,8 +23,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -45,6 +43,7 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.List;
@@ -157,7 +156,7 @@
} else {
summary = getNameForVpnConfig(vpn, UserHandle.of(uid));
}
- new Handler(Looper.getMainLooper()).post(() -> mPreference.setSummary(summary));
+ ThreadUtils.postOnMainThread(() -> mPreference.setSummary(summary));
}
private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
diff --git a/src/com/android/settings/notification/ZenAccessSettings.java b/src/com/android/settings/notification/ZenAccessSettings.java
index 44052b1..0a4915a 100644
--- a/src/com/android/settings/notification/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/ZenAccessSettings.java
@@ -51,6 +51,7 @@
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.AppSwitchPreference;
import java.util.ArrayList;
import java.util.Collections;
@@ -138,7 +139,7 @@
for (ApplicationInfo app : apps) {
final String pkg = app.packageName;
final CharSequence label = app.loadLabel(mPkgMan);
- final SwitchPreference pref = new SwitchPreference(getPrefContext());
+ final SwitchPreference pref = new AppSwitchPreference(getPrefContext());
pref.setPersistent(false);
pref.setIcon(app.loadIcon(mPkgMan));
pref.setTitle(label);
diff --git a/src/com/android/settings/search/AccessibilityServiceResultLoader.java b/src/com/android/settings/search/AccessibilityServiceResultLoader.java
index 345ab30..327aef2 100644
--- a/src/com/android/settings/search/AccessibilityServiceResultLoader.java
+++ b/src/com/android/settings/search/AccessibilityServiceResultLoader.java
@@ -30,105 +30,112 @@
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.ContextCompat;
import android.util.IconDrawableFactory;
+import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.utils.AsyncLoader;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
-public class AccessibilityServiceResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
+public class AccessibilityServiceResultLoader extends
+ FutureTask<List<? extends SearchResult>> {
- private static final int NAME_NO_MATCH = -1;
-
- private final Context mContext;
-
- private List<String> mBreadcrumb;
- private SiteMapManager mSiteMapManager;
- @VisibleForTesting
- final String mQuery;
- private final AccessibilityManager mAccessibilityManager;
- private final PackageManager mPackageManager;
- private final int mUserId;
-
+ private static final String TAG = "A11yResultFutureTask";
public AccessibilityServiceResultLoader(Context context, String query,
- SiteMapManager mapManager) {
- super(context);
- mContext = context;
- mUserId = UserHandle.myUserId();
- mSiteMapManager = mapManager;
- mPackageManager = context.getPackageManager();
- mAccessibilityManager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mQuery = query;
+ SiteMapManager manager) {
+ super(new AccessibilityServiceResultCallable(context, query, manager));
}
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- final Set<SearchResult> results = new HashSet<>();
- final Context context = getContext();
- final List<AccessibilityServiceInfo> services = mAccessibilityManager
- .getInstalledAccessibilityServiceList();
- final IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(mContext);
- final String screenTitle = context.getString(R.string.accessibility_settings);
- for (AccessibilityServiceInfo service : services) {
- if (service == null) {
- continue;
- }
- final ResolveInfo resolveInfo = service.getResolveInfo();
- if (service.getResolveInfo() == null) {
- continue;
- }
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- final CharSequence title = resolveInfo.loadLabel(mPackageManager);
- final int wordDiff = getWordDifference(title.toString(), mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- continue;
- }
- final Drawable icon;
- if (resolveInfo.getIconResource() == 0) {
- icon = ContextCompat.getDrawable(context, R.mipmap.ic_accessibility_generic);
- } else {
- icon = iconFactory.getBadgedIcon(
- resolveInfo.serviceInfo,
- resolveInfo.serviceInfo.applicationInfo,
- mUserId);
- }
- final String componentName = new ComponentName(serviceInfo.packageName,
- serviceInfo.name).flattenToString();
- final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context,
- AccessibilitySettings.class.getName(), componentName, screenTitle);
+ static class AccessibilityServiceResultCallable implements
+ Callable<List<? extends SearchResult>> {
- results.add(new SearchResult.Builder()
- .setTitle(title)
- .addBreadcrumbs(getBreadCrumb())
- .setPayload(new ResultPayload(intent))
- .setRank(wordDiff)
- .setIcon(icon)
- .setStableId(Objects.hash(screenTitle, componentName))
- .build());
+ private static final int NAME_NO_MATCH = -1;
+
+ private final Context mContext;
+ private List<String> mBreadcrumb;
+ private SiteMapManager mSiteMapManager;
+ @VisibleForTesting
+ final String mQuery;
+ private final AccessibilityManager mAccessibilityManager;
+ private final PackageManager mPackageManager;
+ private final int mUserId;
+
+ public AccessibilityServiceResultCallable(Context context, String query,
+ SiteMapManager mapManager) {
+ mUserId = UserHandle.myUserId();
+ mContext = context;
+ mSiteMapManager = mapManager;
+ mPackageManager = context.getPackageManager();
+ mAccessibilityManager =
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mQuery = query;
}
- return results;
- }
- private List<String> getBreadCrumb() {
- if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, AccessibilitySettings.class.getName(),
- context.getString(R.string.accessibility_settings));
+ @Override
+ public List<? extends SearchResult> call() throws Exception {
+ long startTime = System.currentTimeMillis();
+ final List<SearchResult> results = new ArrayList<>();
+ final List<AccessibilityServiceInfo> services = mAccessibilityManager
+ .getInstalledAccessibilityServiceList();
+ final IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(mContext);
+ final String screenTitle = mContext.getString(R.string.accessibility_settings);
+ for (AccessibilityServiceInfo service : services) {
+ if (service == null) {
+ continue;
+ }
+ final ResolveInfo resolveInfo = service.getResolveInfo();
+ if (service.getResolveInfo() == null) {
+ continue;
+ }
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ final CharSequence title = resolveInfo.loadLabel(mPackageManager);
+ final int wordDiff = getWordDifference(title.toString(), mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final Drawable icon;
+ if (resolveInfo.getIconResource() == 0) {
+ icon = ContextCompat.getDrawable(mContext, R.mipmap.ic_accessibility_generic);
+ } else {
+ icon = iconFactory.getBadgedIcon(
+ resolveInfo.serviceInfo,
+ resolveInfo.serviceInfo.applicationInfo,
+ mUserId);
+ }
+ final String componentName = new ComponentName(serviceInfo.packageName,
+ serviceInfo.name).flattenToString();
+ final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(mContext,
+ AccessibilitySettings.class.getName(), componentName, screenTitle);
+
+ results.add(new SearchResult.Builder()
+ .setTitle(title)
+ .addBreadcrumbs(getBreadCrumb())
+ .setPayload(new ResultPayload(intent))
+ .setRank(wordDiff)
+ .setIcon(icon)
+ .setStableId(Objects.hash(screenTitle, componentName))
+ .build());
+ }
+ Collections.sort(results);
+ Log.i(TAG, "A11y search loading took:" + (System.currentTimeMillis() - startTime));
+ return results;
}
- return mBreadcrumb;
- }
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
+ private List<String> getBreadCrumb() {
+ if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
+ mBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ mContext, AccessibilitySettings.class.getName(),
+ mContext.getString(R.string.accessibility_settings));
+ }
+ return mBreadcrumb;
+ }
}
}
diff --git a/src/com/android/settings/search/CursorToSearchResultConverter.java b/src/com/android/settings/search/CursorToSearchResultConverter.java
index ce64de9..8528c56 100644
--- a/src/com/android/settings/search/CursorToSearchResultConverter.java
+++ b/src/com/android/settings/search/CursorToSearchResultConverter.java
@@ -36,16 +36,6 @@
import java.util.Set;
import static com.android.settings.search.DatabaseResultLoader.BASE_RANKS;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_CLASS_NAME;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ICON;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_KEY;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD_TYPE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_SCREEN_TITLE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_TITLE;
import static com.android.settings.search.SearchResult.TOP_RANK;
/**
@@ -62,6 +52,25 @@
private static final String TAG = "CursorConverter";
+ /**
+ * These indices are used to match the columns of the this loader's SELECT statement.
+ * These are not necessarily the same order nor similar coverage as the schema defined in
+ * IndexDatabaseHelper
+ */
+ public static final int COLUMN_INDEX_ID = 0;
+ public static final int COLUMN_INDEX_TITLE = 1;
+ public static final int COLUMN_INDEX_SUMMARY_ON = 2;
+ public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
+ public static final int COLUMN_INDEX_CLASS_NAME = 4;
+ public static final int COLUMN_INDEX_SCREEN_TITLE = 5;
+ public static final int COLUMN_INDEX_ICON = 6;
+ public static final int COLUMN_INDEX_INTENT_ACTION = 7;
+ public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
+ public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
+ public static final int COLUMN_INDEX_KEY = 10;
+ public static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
+ public static final int COLUMN_INDEX_PAYLOAD = 12;
+
private final Context mContext;
private final int LONG_TITLE_LENGTH = 20;
diff --git a/src/com/android/settings/search/DatabaseIndexingManager.java b/src/com/android/settings/search/DatabaseIndexingManager.java
index e94befb..970b50f 100644
--- a/src/com/android/settings/search/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search/DatabaseIndexingManager.java
@@ -17,11 +17,13 @@
package com.android.settings.search;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
-import static com.android.settings.search.DatabaseResultLoader
+
+import static com.android.settings.search.CursorToSearchResultConverter.COLUMN_INDEX_ID;
+import static com.android.settings.search.CursorToSearchResultConverter
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_KEY;
+import static com.android.settings.search.CursorToSearchResultConverter.COLUMN_INDEX_KEY;
import static com.android.settings.search.DatabaseResultLoader.SELECT_COLUMNS;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.CLASS_NAME;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_ENTRIES;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS;
@@ -31,7 +33,6 @@
.DATA_SUMMARY_ON_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED;
-import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ENABLED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ICON;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_ACTION;
diff --git a/src/com/android/settings/search/DatabaseResultLoader.java b/src/com/android/settings/search/DatabaseResultLoader.java
index c1663ab..66548a4 100644
--- a/src/com/android/settings/search/DatabaseResultLoader.java
+++ b/src/com/android/settings/search/DatabaseResultLoader.java
@@ -24,35 +24,31 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import android.util.Pair;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.utils.AsyncLoader;
+import com.android.settings.overlay.FeatureFactory;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
- * AsyncTask to retrieve Settings, First party app and any intent based results.
+ * AsyncTask to retrieve Settings, first party app and any intent based results.
*/
-public class DatabaseResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
- private static final String LOG = "DatabaseResultLoader";
+public class DatabaseResultLoader extends FutureTask<List<? extends SearchResult>> {
- /* These indices are used to match the columns of the this loader's SELECT statement.
- These are not necessarily the same order nor similar coverage as the schema defined in
- IndexDatabaseHelper */
- public static final int COLUMN_INDEX_ID = 0;
- public static final int COLUMN_INDEX_TITLE = 1;
- public static final int COLUMN_INDEX_SUMMARY_ON = 2;
- public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
- public static final int COLUMN_INDEX_CLASS_NAME = 4;
- public static final int COLUMN_INDEX_SCREEN_TITLE = 5;
- public static final int COLUMN_INDEX_ICON = 6;
- public static final int COLUMN_INDEX_INTENT_ACTION = 7;
- public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
- public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
- public static final int COLUMN_INDEX_KEY = 10;
- public static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
- public static final int COLUMN_INDEX_PAYLOAD = 12;
+ private static final String TAG = "DatabaseResultLoader";
public static final String[] SELECT_COLUMNS = {
IndexColumns.DOCID,
@@ -82,194 +78,267 @@
IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
};
- public static final String[] MATCH_COLUMNS_TERTIARY = {
- IndexColumns.DATA_KEYWORDS,
- IndexColumns.DATA_ENTRIES
- };
-
/**
* Base ranks defines the best possible rank based on what the query matches.
- * If the query matches the prefix of the first word in the title, the best rank it can be is 1
- * If the query matches the prefix of the other words in the title, the best rank it can be is 3
+ * If the query matches the prefix of the first word in the title, the best rank it can be
+ * is 1
+ * If the query matches the prefix of the other words in the title, the best rank it can be
+ * is 3
* If the query only matches the summary, the best rank it can be is 7
* If the query only matches keywords or entries, the best rank it can be is 9
*/
public static final int[] BASE_RANKS = {1, 3, 7, 9};
- @VisibleForTesting
- final String mQueryText;
- private final Context mContext;
- private final CursorToSearchResultConverter mConverter;
- private final SiteMapManager mSiteMapManager;
-
- public DatabaseResultLoader(Context context, String queryText, SiteMapManager mapManager) {
- super(context);
- mSiteMapManager = mapManager;
- mContext = context;
- mQueryText = queryText;
- mConverter = new CursorToSearchResultConverter(context);
+ public DatabaseResultLoader(Context context, String query, SiteMapManager manager) {
+ super(new StaticSearchResultCallable(context, query, manager));
}
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
- // TODO Search
- }
+ static class StaticSearchResultCallable implements
+ Callable<List<? extends SearchResult>> {
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- if (mQueryText == null || mQueryText.isEmpty()) {
- return null;
+ public final String[] MATCH_COLUMNS_TERTIARY = {
+ IndexColumns.DATA_KEYWORDS,
+ IndexColumns.DATA_ENTRIES
+ };
+
+ @VisibleForTesting
+ final String mQueryText;
+ private final Context mContext;
+ private final CursorToSearchResultConverter mConverter;
+ private final SiteMapManager mSiteMapManager;
+ private final SearchFeatureProvider mFeatureProvider;
+
+ public StaticSearchResultCallable(Context context, String queryText,
+ SiteMapManager mapManager) {
+ mContext = context;
+ mSiteMapManager = mapManager;
+ mQueryText = queryText;
+ mConverter = new CursorToSearchResultConverter(context);
+ mFeatureProvider = FeatureFactory.getFactory(context).getSearchFeatureProvider();
}
- final Set<SearchResult> results = new HashSet<>();
+ @Override
+ public List<? extends SearchResult> call() {
+ if (mQueryText == null || mQueryText.isEmpty()) {
+ return new ArrayList<>();
+ }
- results.addAll(firstWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]));
- results.addAll(secondaryWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[1]));
- results.addAll(anyWordQuery(MATCH_COLUMNS_SECONDARY, BASE_RANKS[2]));
- results.addAll(anyWordQuery(MATCH_COLUMNS_TERTIARY, BASE_RANKS[3]));
- return results;
- }
+ // TODO (b/68656233) Consolidate timing metrics
+ long startTime = System.currentTimeMillis();
+ // Start a Future to get search result scores.
+ FutureTask<List<Pair<String, Float>>> rankerTask = mFeatureProvider.getRankerTask(
+ mContext, mQueryText);
- @Override
- protected boolean onCancelLoad() {
- // TODO
- return super.onCancelLoad();
- }
+ if (rankerTask != null) {
+ ExecutorService executorService = mFeatureProvider.getExecutorService();
+ executorService.execute(rankerTask);
+ }
- /**
- * Creates and executes the query which matches prefixes of the first word of the given columns.
- *
- * @param matchColumns The columns to match on
- * @param baseRank The highest rank achievable by these results
- * @return A set of the matching results.
- */
- private Set<SearchResult> firstWordQuery(String[] matchColumns, int baseRank) {
- final String whereClause = buildSingleWordWhereClause(matchColumns);
- final String query = mQueryText + "%";
- final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+ final Set<SearchResult> resultSet = new HashSet<>();
- return query(whereClause, selection, baseRank);
- }
+ resultSet.addAll(firstWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]));
+ resultSet.addAll(secondaryWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[1]));
+ resultSet.addAll(anyWordQuery(MATCH_COLUMNS_SECONDARY, BASE_RANKS[2]));
+ resultSet.addAll(anyWordQuery(MATCH_COLUMNS_TERTIARY, BASE_RANKS[3]));
- /**
- * Creates and executes the query which matches prefixes of the non-first words of the
- * given columns.
- *
- * @param matchColumns The columns to match on
- * @param baseRank The highest rank achievable by these results
- * @return A set of the matching results.
- */
- private Set<SearchResult> secondaryWordQuery(String[] matchColumns, int baseRank) {
- final String whereClause = buildSingleWordWhereClause(matchColumns);
- final String query = "% " + mQueryText + "%";
- final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+ // Try to retrieve the scores in time. Otherwise use static ranking.
+ if (rankerTask != null) {
+ try {
+ final long timeoutMs = mFeatureProvider.smartSearchRankingTimeoutMs(mContext);
+ List<Pair<String, Float>> searchRankScores = rankerTask.get(timeoutMs,
+ TimeUnit.MILLISECONDS);
+ return getDynamicRankedResults(resultSet, searchRankScores);
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Error waiting for result scores: " + e);
+ }
+ }
- return query(whereClause, selection, baseRank);
- }
-
- /**
- * Creates and executes the query which matches prefixes of the any word of the given columns.
- *
- * @param matchColumns The columns to match on
- * @param baseRank The highest rank achievable by these results
- * @return A set of the matching results.
- */
- private Set<SearchResult> anyWordQuery(String[] matchColumns, int baseRank) {
- final String whereClause = buildTwoWordWhereClause(matchColumns);
- final String[] selection = buildAnyWordSelection(matchColumns.length * 2);
-
- return query(whereClause, selection, baseRank);
- }
-
- /**
- * Generic method used by all of the query methods above to execute a query.
- *
- * @param whereClause Where clause for the SQL query which uses bindings.
- * @param selection List of the transformed query to match each bind in the whereClause
- * @param baseRank The highest rank achievable by these results.
- * @return A set of the matching results.
- */
- private Set<SearchResult> query(String whereClause, String[] selection, int baseRank) {
- final SQLiteDatabase database =
- IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
- try (Cursor resultCursor = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS, whereClause,
- selection, null, null, null)) {
- return mConverter.convertCursor(mSiteMapManager, resultCursor, baseRank);
+ List<SearchResult> resultList = new ArrayList<>(resultSet);
+ Collections.sort(resultList);
+ Log.i(TAG, "Static search loading took:" + (System.currentTimeMillis() - startTime));
+ return resultList;
}
- }
- /**
- * Builds the SQLite WHERE clause that matches all matchColumns for a single query.
- *
- * @param matchColumns List of columns that will be used for matching.
- * @return The constructed WHERE clause.
- */
- private static String buildSingleWordWhereClause(String[] matchColumns) {
- StringBuilder sb = new StringBuilder(" (");
- final int count = matchColumns.length;
- for (int n = 0; n < count; n++) {
- sb.append(matchColumns[n]);
- sb.append(" like ? ");
- if (n < count - 1) {
- sb.append(" OR ");
+ // TODO (b/33577327) Retrieve all search results with a single query.
+
+ /**
+ * Creates and executes the query which matches prefixes of the first word of the given
+ * columns.
+ *
+ * @param matchColumns The columns to match on
+ * @param baseRank The highest rank achievable by these results
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> firstWordQuery(String[] matchColumns, int baseRank) {
+ final String whereClause = buildSingleWordWhereClause(matchColumns);
+ final String query = mQueryText + "%";
+ final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+
+ return query(whereClause, selection, baseRank);
+ }
+
+ /**
+ * Creates and executes the query which matches prefixes of the non-first words of the
+ * given columns.
+ *
+ * @param matchColumns The columns to match on
+ * @param baseRank The highest rank achievable by these results
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> secondaryWordQuery(String[] matchColumns, int baseRank) {
+ final String whereClause = buildSingleWordWhereClause(matchColumns);
+ final String query = "% " + mQueryText + "%";
+ final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+
+ return query(whereClause, selection, baseRank);
+ }
+
+ /**
+ * Creates and executes the query which matches prefixes of the any word of the given
+ * columns.
+ *
+ * @param matchColumns The columns to match on
+ * @param baseRank The highest rank achievable by these results
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> anyWordQuery(String[] matchColumns, int baseRank) {
+ final String whereClause = buildTwoWordWhereClause(matchColumns);
+ final String[] selection = buildAnyWordSelection(matchColumns.length * 2);
+
+ return query(whereClause, selection, baseRank);
+ }
+
+ /**
+ * Generic method used by all of the query methods above to execute a query.
+ *
+ * @param whereClause Where clause for the SQL query which uses bindings.
+ * @param selection List of the transformed query to match each bind in the whereClause
+ * @param baseRank The highest rank achievable by these results.
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> query(String whereClause, String[] selection, int baseRank) {
+ final SQLiteDatabase database =
+ IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
+ try (Cursor resultCursor = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,
+ whereClause,
+ selection, null, null, null)) {
+ return mConverter.convertCursor(mSiteMapManager, resultCursor, baseRank);
}
}
- sb.append(") AND enabled = 1");
- return sb.toString();
- }
- /**
- * Builds the SQLite WHERE clause that matches all matchColumns to two different queries.
- *
- * @param matchColumns List of columns that will be used for matching.
- * @return The constructed WHERE clause.
- */
- private static String buildTwoWordWhereClause(String[] matchColumns) {
- StringBuilder sb = new StringBuilder(" (");
- final int count = matchColumns.length;
- for (int n = 0; n < count; n++) {
- sb.append(matchColumns[n]);
- sb.append(" like ? OR ");
- sb.append(matchColumns[n]);
- sb.append(" like ?");
- if (n < count - 1) {
- sb.append(" OR ");
+ /**
+ * Builds the SQLite WHERE clause that matches all matchColumns for a single query.
+ *
+ * @param matchColumns List of columns that will be used for matching.
+ * @return The constructed WHERE clause.
+ */
+ private static String buildSingleWordWhereClause(String[] matchColumns) {
+ StringBuilder sb = new StringBuilder(" (");
+ final int count = matchColumns.length;
+ for (int n = 0; n < count; n++) {
+ sb.append(matchColumns[n]);
+ sb.append(" like ? ");
+ if (n < count - 1) {
+ sb.append(" OR ");
+ }
}
+ sb.append(") AND enabled = 1");
+ return sb.toString();
}
- sb.append(") AND enabled = 1");
- return sb.toString();
- }
- /**
- * Fills out the selection array to match the query as the prefix of a single word.
- *
- * @param size is the number of columns to be matched.
- */
- private String[] buildSingleWordSelection(String query, int size) {
- String[] selection = new String[size];
-
- for (int i = 0; i < size; i++) {
- selection[i] = query;
+ /**
+ * Builds the SQLite WHERE clause that matches all matchColumns to two different queries.
+ *
+ * @param matchColumns List of columns that will be used for matching.
+ * @return The constructed WHERE clause.
+ */
+ private static String buildTwoWordWhereClause(String[] matchColumns) {
+ StringBuilder sb = new StringBuilder(" (");
+ final int count = matchColumns.length;
+ for (int n = 0; n < count; n++) {
+ sb.append(matchColumns[n]);
+ sb.append(" like ? OR ");
+ sb.append(matchColumns[n]);
+ sb.append(" like ?");
+ if (n < count - 1) {
+ sb.append(" OR ");
+ }
+ }
+ sb.append(") AND enabled = 1");
+ return sb.toString();
}
- return selection;
- }
- /**
- * Fills out the selection array to match the query as the prefix of a word.
- *
- * @param size is twice the number of columns to be matched. The first match is for the prefix
- * of the first word in the column. The second match is for any subsequent word
- * prefix match.
- */
- private String[] buildAnyWordSelection(int size) {
- String[] selection = new String[size];
- final String query = mQueryText + "%";
- final String subStringQuery = "% " + mQueryText + "%";
+ /**
+ * Fills out the selection array to match the query as the prefix of a single word.
+ *
+ * @param size is the number of columns to be matched.
+ */
+ private String[] buildSingleWordSelection(String query, int size) {
+ String[] selection = new String[size];
- for (int i = 0; i < (size - 1); i += 2) {
- selection[i] = query;
- selection[i + 1] = subStringQuery;
+ for (int i = 0; i < size; i++) {
+ selection[i] = query;
+ }
+ return selection;
}
- return selection;
+
+ /**
+ * Fills out the selection array to match the query as the prefix of a word.
+ *
+ * @param size is twice the number of columns to be matched. The first match is for the
+ * prefix
+ * of the first word in the column. The second match is for any subsequent word
+ * prefix match.
+ */
+ private String[] buildAnyWordSelection(int size) {
+ String[] selection = new String[size];
+ final String query = mQueryText + "%";
+ final String subStringQuery = "% " + mQueryText + "%";
+
+ for (int i = 0; i < (size - 1); i += 2) {
+ selection[i] = query;
+ selection[i + 1] = subStringQuery;
+ }
+ return selection;
+ }
+
+ private List<SearchResult> getDynamicRankedResults(Set<SearchResult> unsortedSet,
+ List<Pair<String, Float>> searchRankScores) {
+ TreeSet<SearchResult> dbResultsSortedByScores = new TreeSet<>(
+ (o1, o2) -> {
+ float score1 = getRankingScoreByStableId(searchRankScores, o1.stableId);
+ float score2 = getRankingScoreByStableId(searchRankScores, o2.stableId);
+ if (score1 > score2) {
+ return -1;
+ } else if (score1 == score2) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+ dbResultsSortedByScores.addAll(unsortedSet);
+
+ return new ArrayList<>(dbResultsSortedByScores);
+ }
+
+ /**
+ * Looks up ranking score for stableId
+ *
+ * @param stableId String of stableId
+ * @return the ranking score corresponding to the given stableId. If there is no score
+ * available for this stableId, -Float.MAX_VALUE is returned.
+ */
+ @VisibleForTesting
+ Float getRankingScoreByStableId(List<Pair<String, Float>> searchRankScores, int stableId) {
+ for (Pair<String, Float> rankingScore : searchRankScores) {
+ if (Integer.toString(stableId).compareTo(rankingScore.first) == 0) {
+ return rankingScore.second;
+ }
+ }
+ // If stableId not found in the list, we assign the minimum score so it will appear at
+ // the end of the list.
+ Log.w(TAG, "stableId " + stableId + " was not in the ranking scores.");
+ return -Float.MAX_VALUE;
+ }
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/search/InputDeviceResultLoader.java b/src/com/android/settings/search/InputDeviceResultLoader.java
index e5e6553..598281c 100644
--- a/src/com/android/settings/search/InputDeviceResultLoader.java
+++ b/src/com/android/settings/search/InputDeviceResultLoader.java
@@ -26,6 +26,7 @@
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
import android.support.annotation.VisibleForTesting;
+import android.util.Log;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -35,20 +36,24 @@
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
-import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
/**
* Search result for input devices (physical/virtual keyboard, game controllers, etc)
*/
-public class InputDeviceResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
- private static final int NAME_NO_MATCH = -1;
+
+public class InputDeviceResultLoader extends FutureTask<List<? extends SearchResult>> {
+
+ private static final String TAG = "InputResultFutureTask";
@VisibleForTesting
static final String PHYSICAL_KEYBOARD_FRAGMENT = PhysicalKeyboardFragment.class.getName();
@@ -56,145 +61,151 @@
static final String VIRTUAL_KEYBOARD_FRAGMENT =
AvailableVirtualKeyboardFragment.class.getName();
- private final SiteMapManager mSiteMapManager;
- private final InputManager mInputManager;
- private final InputMethodManager mImm;
- private final PackageManager mPackageManager;
- @VisibleForTesting
- final String mQuery;
-
- private List<String> mPhysicalKeyboardBreadcrumb;
- private List<String> mVirtualKeyboardBreadcrumb;
-
- public InputDeviceResultLoader(Context context, String query, SiteMapManager mapManager) {
- super(context);
- mQuery = query;
- mSiteMapManager = mapManager;
- mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
- mImm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
- mPackageManager = context.getPackageManager();
+ public InputDeviceResultLoader(Context context, String query, SiteMapManager manager) {
+ super(new InputDeviceResultCallable(context, query, manager));
}
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
- }
+ static class InputDeviceResultCallable implements
+ Callable<List<? extends SearchResult>> {
+ private static final int NAME_NO_MATCH = -1;
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- final Set<SearchResult> results = new HashSet<>();
- results.addAll(buildPhysicalKeyboardSearchResults());
- results.addAll(buildVirtualKeyboardSearchResults());
- return results;
- }
+ private final Context mContext;
+ private final SiteMapManager mSiteMapManager;
+ private final InputManager mInputManager;
+ private final InputMethodManager mImm;
+ private final PackageManager mPackageManager;
+ @VisibleForTesting
+ final String mQuery;
- private Set<SearchResult> buildPhysicalKeyboardSearchResults() {
- final Set<SearchResult> results = new HashSet<>();
- final Context context = getContext();
- final String screenTitle = context.getString(R.string.physical_keyboard_title);
+ private List<String> mPhysicalKeyboardBreadcrumb;
+ private List<String> mVirtualKeyboardBreadcrumb;
- for (final InputDevice device : getPhysicalFullKeyboards()) {
- final String deviceName = device.getName();
- final int wordDiff = InstalledAppResultLoader.getWordDifference(deviceName, mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- continue;
+ public InputDeviceResultCallable(Context context, String query, SiteMapManager mapManager) {
+ mContext = context;
+ mQuery = query;
+ mSiteMapManager = mapManager;
+ mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+ mImm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public List<? extends SearchResult> call() {
+ long startTime = System.currentTimeMillis();
+ final List<SearchResult> results = new ArrayList<>();
+ results.addAll(buildPhysicalKeyboardSearchResults());
+ results.addAll(buildVirtualKeyboardSearchResults());
+ Collections.sort(results);
+ Log.i(TAG, "Input search loading took:" + (System.currentTimeMillis() - startTime));
+ return results;
+ }
+
+ private Set<SearchResult> buildPhysicalKeyboardSearchResults() {
+ final Set<SearchResult> results = new HashSet<>();
+ final String screenTitle = mContext.getString(R.string.physical_keyboard_title);
+
+ for (final InputDevice device : getPhysicalFullKeyboards()) {
+ final String deviceName = device.getName();
+ final int wordDiff = InstalledAppResultLoader.getWordDifference(deviceName,
+ mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final String keyboardLayoutDescriptor = mInputManager
+ .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
+ final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
+ ? mInputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
+ final String summary = (keyboardLayout != null)
+ ? keyboardLayout.toString()
+ : mContext.getString(R.string.keyboard_layout_default_label);
+
+ final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(mContext,
+ PHYSICAL_KEYBOARD_FRAGMENT, deviceName, screenTitle);
+ results.add(new SearchResult.Builder()
+ .setTitle(deviceName)
+ .setPayload(new ResultPayload(intent))
+ .setStableId(Objects.hash(PHYSICAL_KEYBOARD_FRAGMENT, deviceName))
+ .setSummary(summary)
+ .setRank(wordDiff)
+ .addBreadcrumbs(getPhysicalKeyboardBreadCrumb())
+ .build());
}
- final String keyboardLayoutDescriptor = mInputManager
- .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
- final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
- ? mInputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
- final String summary = (keyboardLayout != null)
- ? keyboardLayout.toString()
- : context.getString(R.string.keyboard_layout_default_label);
- final String key = deviceName;
-
- final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context,
- PHYSICAL_KEYBOARD_FRAGMENT, key, screenTitle);
- results.add(new SearchResult.Builder()
- .setTitle(deviceName)
- .setPayload(new ResultPayload(intent))
- .setStableId(Objects.hash(PHYSICAL_KEYBOARD_FRAGMENT, key))
- .setSummary(summary)
- .setRank(wordDiff)
- .addBreadcrumbs(getPhysicalKeyboardBreadCrumb())
- .build());
+ return results;
}
- return results;
- }
- private Set<SearchResult> buildVirtualKeyboardSearchResults() {
- final Set<SearchResult> results = new HashSet<>();
- final Context context = getContext();
- final String screenTitle = context.getString(R.string.add_virtual_keyboard);
- final List<InputMethodInfo> inputMethods = mImm.getInputMethodList();
- for (InputMethodInfo info : inputMethods) {
- final String title = info.loadLabel(mPackageManager).toString();
- final String summary = InputMethodAndSubtypeUtil
- .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(info), context, info);
- int wordDiff = InstalledAppResultLoader.getWordDifference(title, mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- wordDiff = InstalledAppResultLoader.getWordDifference(summary, mQuery);
+ private Set<SearchResult> buildVirtualKeyboardSearchResults() {
+ final Set<SearchResult> results = new HashSet<>();
+ final String screenTitle = mContext.getString(R.string.add_virtual_keyboard);
+ final List<InputMethodInfo> inputMethods = mImm.getInputMethodList();
+ for (InputMethodInfo info : inputMethods) {
+ final String title = info.loadLabel(mPackageManager).toString();
+ final String summary = InputMethodAndSubtypeUtil
+ .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(info), mContext, info);
+ int wordDiff = InstalledAppResultLoader.getWordDifference(title, mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ wordDiff = InstalledAppResultLoader.getWordDifference(summary, mQuery);
+ }
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final ServiceInfo serviceInfo = info.getServiceInfo();
+ final String key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
+ .flattenToString();
+ final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(mContext,
+ VIRTUAL_KEYBOARD_FRAGMENT, key, screenTitle);
+ results.add(new SearchResult.Builder()
+ .setTitle(title)
+ .setSummary(summary)
+ .setRank(wordDiff)
+ .setStableId(Objects.hash(VIRTUAL_KEYBOARD_FRAGMENT, key))
+ .addBreadcrumbs(getVirtualKeyboardBreadCrumb())
+ .setPayload(new ResultPayload(intent))
+ .build());
}
- if (wordDiff == NAME_NO_MATCH) {
- continue;
+ return results;
+ }
+
+ private List<String> getPhysicalKeyboardBreadCrumb() {
+ if (mPhysicalKeyboardBreadcrumb == null || mPhysicalKeyboardBreadcrumb.isEmpty()) {
+ mPhysicalKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ mContext, PHYSICAL_KEYBOARD_FRAGMENT,
+ mContext.getString(R.string.physical_keyboard_title));
}
- final ServiceInfo serviceInfo = info.getServiceInfo();
- final String key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
- .flattenToString();
- final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context,
- VIRTUAL_KEYBOARD_FRAGMENT, key, screenTitle);
- results.add(new SearchResult.Builder()
- .setTitle(title)
- .setSummary(summary)
- .setRank(wordDiff)
- .setStableId(Objects.hash(VIRTUAL_KEYBOARD_FRAGMENT, key))
- .addBreadcrumbs(getVirtualKeyboardBreadCrumb())
- .setPayload(new ResultPayload(intent))
- .build());
+ return mPhysicalKeyboardBreadcrumb;
}
- return results;
- }
- private List<String> getPhysicalKeyboardBreadCrumb() {
- if (mPhysicalKeyboardBreadcrumb == null || mPhysicalKeyboardBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mPhysicalKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, PHYSICAL_KEYBOARD_FRAGMENT,
- context.getString(R.string.physical_keyboard_title));
+
+ private List<String> getVirtualKeyboardBreadCrumb() {
+ if (mVirtualKeyboardBreadcrumb == null || mVirtualKeyboardBreadcrumb.isEmpty()) {
+ final Context context = mContext;
+ mVirtualKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ context, VIRTUAL_KEYBOARD_FRAGMENT,
+ context.getString(R.string.add_virtual_keyboard));
+ }
+ return mVirtualKeyboardBreadcrumb;
}
- return mPhysicalKeyboardBreadcrumb;
- }
-
- private List<String> getVirtualKeyboardBreadCrumb() {
- if (mVirtualKeyboardBreadcrumb == null || mVirtualKeyboardBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mVirtualKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, VIRTUAL_KEYBOARD_FRAGMENT,
- context.getString(R.string.add_virtual_keyboard));
- }
- return mVirtualKeyboardBreadcrumb;
- }
-
- private List<InputDevice> getPhysicalFullKeyboards() {
- final List<InputDevice> keyboards = new ArrayList<>();
- final int[] deviceIds = InputDevice.getDeviceIds();
- if (deviceIds != null) {
- for (int deviceId : deviceIds) {
- final InputDevice device = InputDevice.getDevice(deviceId);
- if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
- keyboards.add(device);
+ private List<InputDevice> getPhysicalFullKeyboards() {
+ final List<InputDevice> keyboards = new ArrayList<>();
+ final int[] deviceIds = InputDevice.getDeviceIds();
+ if (deviceIds != null) {
+ for (int deviceId : deviceIds) {
+ final InputDevice device = InputDevice.getDevice(deviceId);
+ if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
+ keyboards.add(device);
+ }
}
}
+ return keyboards;
}
- return keyboards;
- }
- private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
- final int subtypeCount = imi.getSubtypeCount();
- final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
- for (int index = 0; index < subtypeCount; index++) {
- allSubtypes.add(imi.getSubtypeAt(index));
+ private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
+ final int subtypeCount = imi.getSubtypeCount();
+ final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
+ for (int index = 0; index < subtypeCount; index++) {
+ allSubtypes.add(imi.getSubtypeAt(index));
+ }
+ return allSubtypes;
}
- return allSubtypes;
}
}
diff --git a/src/com/android/settings/search/InstalledAppResultLoader.java b/src/com/android/settings/search/InstalledAppResultLoader.java
index 7645c15..e5d8ac1 100644
--- a/src/com/android/settings/search/InstalledAppResultLoader.java
+++ b/src/com/android/settings/search/InstalledAppResultLoader.java
@@ -29,124 +29,39 @@
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
/**
* Search loader for installed apps.
*/
-public class InstalledAppResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
+public class InstalledAppResultLoader extends FutureTask<List<? extends SearchResult>> {
+
+ private static final String TAG = "InstalledAppFutureTask";
private static final int NAME_NO_MATCH = -1;
private static final Intent LAUNCHER_PROBE = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER);
- private List<String> mBreadcrumb;
- private SiteMapManager mSiteMapManager;
- @VisibleForTesting
- final String mQuery;
- private final UserManager mUserManager;
- private final PackageManagerWrapper mPackageManager;
- private final List<ResolveInfo> mHomeActivities = new ArrayList<>();
-
- public InstalledAppResultLoader(Context context, PackageManagerWrapper pmWrapper,
- String query, SiteMapManager mapManager) {
- super(context);
- mSiteMapManager = mapManager;
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- mPackageManager = pmWrapper;
- mQuery = query;
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- final Set<AppSearchResult> results = new HashSet<>();
- final PackageManager pm = mPackageManager.getPackageManager();
-
- mHomeActivities.clear();
- mPackageManager.getHomeActivities(mHomeActivities);
-
- for (UserInfo user : getUsersToCount()) {
- final List<ApplicationInfo> apps =
- mPackageManager.getInstalledApplicationsAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
- user.id);
- for (ApplicationInfo info : apps) {
- if (!shouldIncludeAsCandidate(info, user)) {
- continue;
- }
- final CharSequence label = info.loadLabel(pm);
- final int wordDiff = getWordDifference(label.toString(), mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- continue;
- }
- final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- .setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- .setData(Uri.fromParts("package", info.packageName, null))
- .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
- MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS);
-
- final AppSearchResult.Builder builder = new AppSearchResult.Builder();
- builder.setAppInfo(info)
- .setStableId(Objects.hash(info.packageName, user.id))
- .setTitle(info.loadLabel(pm))
- .setRank(getRank(wordDiff))
- .addBreadcrumbs(getBreadCrumb())
- .setPayload(new ResultPayload(intent));
- results.add(builder.build());
- }
- }
- return results;
- }
-
- /**
- * Returns true if the candidate should be included in candidate list
- * <p/>
- * This method matches logic in {@code ApplicationState#FILTER_DOWNLOADED_AND_LAUNCHER}.
- */
- private boolean shouldIncludeAsCandidate(ApplicationInfo info, UserInfo user) {
- // Not system app
- if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
- || (info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- return true;
- }
- // Shows up in launcher
- final Intent launchIntent = new Intent(LAUNCHER_PROBE)
- .setPackage(info.packageName);
- final List<ResolveInfo> intents = mPackageManager.queryIntentActivitiesAsUser(
- launchIntent,
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- user.id);
- if (intents != null && intents.size() != 0) {
- return true;
- }
- // Is launcher app itself
- return isPackageInList(mHomeActivities, info.packageName);
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-
- private List<UserInfo> getUsersToCount() {
- return mUserManager.getProfiles(UserHandle.myUserId());
+ public InstalledAppResultLoader(Context context, PackageManagerWrapper wrapper,
+ String query, SiteMapManager manager) {
+ super(new InstalledAppResultCallable(context, wrapper, query, manager));
}
/**
@@ -213,35 +128,133 @@
return NAME_NO_MATCH;
}
- private boolean isPackageInList(List<ResolveInfo> resolveInfos, String pkg) {
- for (ResolveInfo info : resolveInfos) {
- if (TextUtils.equals(info.activityInfo.packageName, pkg)) {
+ static class InstalledAppResultCallable implements
+ Callable<List<? extends SearchResult>> {
+
+ private final Context mContext;
+ private List<String> mBreadcrumb;
+ private SiteMapManager mSiteMapManager;
+ @VisibleForTesting
+ final String mQuery;
+ private final UserManager mUserManager;
+ private final PackageManagerWrapper mPackageManager;
+ private final List<ResolveInfo> mHomeActivities = new ArrayList<>();
+
+ public InstalledAppResultCallable(Context context, PackageManagerWrapper pmWrapper,
+ String query, SiteMapManager mapManager) {
+ mContext = context;
+ mSiteMapManager = mapManager;
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mPackageManager = pmWrapper;
+ mQuery = query;
+ }
+
+ @Override
+ public List<? extends SearchResult> call() throws Exception {
+ long startTime = System.currentTimeMillis();
+ final List<AppSearchResult> results = new ArrayList<>();
+ final PackageManager pm = mPackageManager.getPackageManager();
+
+ mHomeActivities.clear();
+ mPackageManager.getHomeActivities(mHomeActivities);
+
+ for (UserInfo user : getUsersToCount()) {
+ final List<ApplicationInfo> apps =
+ mPackageManager.getInstalledApplicationsAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
+ user.id);
+ for (ApplicationInfo info : apps) {
+ if (!shouldIncludeAsCandidate(info, user)) {
+ continue;
+ }
+ final CharSequence label = info.loadLabel(pm);
+ final int wordDiff = getWordDifference(label.toString(), mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", info.packageName, null))
+ .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
+ MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS);
+
+ final AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setAppInfo(info)
+ .setStableId(Objects.hash(info.packageName, user.id))
+ .setTitle(info.loadLabel(pm))
+ .setRank(getRank(wordDiff))
+ .addBreadcrumbs(getBreadCrumb())
+ .setPayload(new ResultPayload(intent));
+ results.add(builder.build());
+ }
+ }
+ Collections.sort(results);
+ Log.i(TAG, "App search loading took:" + (System.currentTimeMillis() - startTime));
+ return results;
+ }
+
+ /**
+ * Returns true if the candidate should be included in candidate list
+ * <p/>
+ * This method matches logic in {@code ApplicationState#FILTER_DOWNLOADED_AND_LAUNCHER}.
+ */
+ private boolean shouldIncludeAsCandidate(ApplicationInfo info, UserInfo user) {
+ // Not system app
+ if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+ || (info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return true;
}
+ // Shows up in launcher
+ final Intent launchIntent = new Intent(LAUNCHER_PROBE)
+ .setPackage(info.packageName);
+ final List<ResolveInfo> intents = mPackageManager.queryIntentActivitiesAsUser(
+ launchIntent,
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ user.id);
+ if (intents != null && intents.size() != 0) {
+ return true;
+ }
+ // Is launcher app itself
+ return isPackageInList(mHomeActivities, info.packageName);
}
- return false;
- }
- private List<String> getBreadCrumb() {
- if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, ManageApplications.class.getName(),
- context.getString(R.string.applications_settings));
+ private List<UserInfo> getUsersToCount() {
+ return mUserManager.getProfiles(UserHandle.myUserId());
}
- return mBreadcrumb;
- }
- /**
- * A temporary ranking scheme for installed apps.
- *
- * @param wordDiff difference between query length and app name length.
- * @return the ranking.
- */
- private int getRank(int wordDiff) {
- if (wordDiff < 6) {
- return 2;
+ private boolean isPackageInList(List<ResolveInfo> resolveInfos, String pkg) {
+ for (ResolveInfo info : resolveInfos) {
+ if (TextUtils.equals(info.activityInfo.packageName, pkg)) {
+ return true;
+ }
+ }
+ return false;
}
- return 3;
+
+ private List<String> getBreadCrumb() {
+ if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
+ mBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ mContext, ManageApplications.class.getName(),
+ mContext.getString(R.string.applications_settings));
+ }
+ return mBreadcrumb;
+ }
+
+ /**
+ * A temporary ranking scheme for installed apps.
+ *
+ * @param wordDiff difference between query length and app name length.
+ * @return the ranking.
+ */
+ private int getRank(int wordDiff) {
+ if (wordDiff < 6) {
+ return 2;
+ }
+ return 3;
+ }
}
}
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 4df8203..42afee9 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -19,10 +19,14 @@
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.util.Pair;
import android.view.View;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.search.ranking.SearchResultsRankerCallback;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
/**
* FeatureProvider for Settings Search
@@ -44,25 +48,30 @@
throws SecurityException, IllegalArgumentException;
/**
+ * Returns a new loader to get settings search results.
+ */
+ SearchResultLoader getSearchResultLoader(Context context, String query);
+
+ /**
* Returns a new loader to search in index database.
*/
- DatabaseResultLoader getDatabaseSearchLoader(Context context, String query);
+ DatabaseResultLoader getStaticSearchResultTask(Context context, String query);
/**
* Returns a new loader to search installed apps.
*/
- InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query);
+ InstalledAppResultLoader getInstalledAppSearchTask(Context context, String query);
/**
* Returns a new loader to search accessibility services.
*/
- AccessibilityServiceResultLoader getAccessibilityServiceResultLoader(Context context,
+ AccessibilityServiceResultLoader getAccessibilityServiceResultTask(Context context,
String query);
/**
* Returns a new loader to search input devices.
*/
- InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query);
+ InputDeviceResultLoader getInputDeviceResultTask(Context context, String query);
/**
* Returns a new loader to get all recently saved queries search terms.
@@ -96,6 +105,11 @@
boolean isIndexingComplete(Context context);
/**
+ * @return a {@link ExecutorService} to be shared between search tasks.
+ */
+ ExecutorService getExecutorService();
+
+ /**
* Initializes the feedback button in case it was dismissed.
*/
default void initFeedbackButton() {
@@ -115,23 +129,6 @@
}
/**
- * Query search results based on the input query.
- *
- * @param context application context
- * @param query input user query
- * @param searchResultsRankerCallback {@link SearchResultsRankerCallback}
- */
- default void querySearchResults(Context context, String query,
- SearchResultsRankerCallback searchResultsRankerCallback) {
- }
-
- /**
- * Cancel pending search query
- */
- default void cancelPendingSearchQuery(Context context) {
- }
-
- /**
* Notify that a search result is clicked.
*
* @param context application context
@@ -161,4 +158,10 @@
default void searchRankingWarmup(Context context) {
}
+ /**
+ * Return a FutureTask to get a list of scores for search results.
+ */
+ default FutureTask<List<Pair<String, Float>>> getRankerTask(Context context, String query) {
+ return null;
+ }
}
diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java
index af7f177..e0fbfd7 100644
--- a/src/com/android/settings/search/SearchFeatureProviderImpl.java
+++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java
@@ -22,12 +22,15 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.indexing.IndexData;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/**
* FeatureProvider for the refactored search code.
@@ -40,6 +43,7 @@
private DatabaseIndexingManager mDatabaseIndexingManager;
private SiteMapManager mSiteMapManager;
+ private ExecutorService mExecutorService;
@Override
public boolean isEnabled(Context context) {
@@ -59,26 +63,31 @@
}
@Override
- public DatabaseResultLoader getDatabaseSearchLoader(Context context, String query) {
+ public SearchResultLoader getSearchResultLoader(Context context, String query) {
+ return new SearchResultLoader(context, cleanQuery(query));
+ }
+
+ @Override
+ public DatabaseResultLoader getStaticSearchResultTask(Context context, String query) {
return new DatabaseResultLoader(context, cleanQuery(query), getSiteMapManager());
}
@Override
- public InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query) {
+ public InstalledAppResultLoader getInstalledAppSearchTask(Context context, String query) {
return new InstalledAppResultLoader(
context, new PackageManagerWrapper(context.getPackageManager()),
cleanQuery(query), getSiteMapManager());
}
@Override
- public AccessibilityServiceResultLoader getAccessibilityServiceResultLoader(Context context,
+ public AccessibilityServiceResultLoader getAccessibilityServiceResultTask(Context context,
String query) {
return new AccessibilityServiceResultLoader(context, cleanQuery(query),
getSiteMapManager());
}
@Override
- public InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query) {
+ public InputDeviceResultLoader getInputDeviceResultTask(Context context, String query) {
return new InputDeviceResultLoader(context, cleanQuery(query), getSiteMapManager());
}
@@ -124,12 +133,21 @@
.histogram(context, METRICS_ACTION_SETTINGS_INDEX, indexingTime);
}
+ @Override
+ public ExecutorService getExecutorService() {
+ if (mExecutorService == null) {
+ mExecutorService = Executors.newCachedThreadPool();
+ }
+ return mExecutorService;
+ }
+
/**
* A generic method to make the query suitable for searching the database.
*
* @return the cleaned query string
*/
- private String cleanQuery(String query) {
+ @VisibleForTesting
+ String cleanQuery(String query) {
if (TextUtils.isEmpty(query)) {
return null;
}
diff --git a/src/com/android/settings/search/SearchFragment.java b/src/com/android/settings/search/SearchFragment.java
index ca951c6..e6316a8 100644
--- a/src/com/android/settings/search/SearchFragment.java
+++ b/src/com/android/settings/search/SearchFragment.java
@@ -54,8 +54,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* This fragment manages the lifecycle of indexing and searching.
@@ -68,7 +66,7 @@
* the query if the user has entered text.
*/
public class SearchFragment extends InstrumentedFragment implements SearchView.OnQueryTextListener,
- LoaderManager.LoaderCallbacks<Set<? extends SearchResult>>, IndexingCallback {
+ LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {
private static final String TAG = "SearchFragment";
// State values
@@ -78,23 +76,14 @@
static final class SearchLoaderId {
// Search Query IDs
- public static final int DATABASE = 1;
- public static final int INSTALLED_APPS = 2;
- public static final int ACCESSIBILITY_SERVICES = 3;
- public static final int INPUT_DEVICES = 4;
+ public static final int SEARCH_RESULT = 1;
// Saved Query IDs
- public static final int SAVE_QUERY_TASK = 5;
- public static final int REMOVE_QUERY_TASK = 6;
- public static final int SAVED_QUERIES = 7;
+ public static final int SAVE_QUERY_TASK = 2;
+ public static final int REMOVE_QUERY_TASK = 3;
+ public static final int SAVED_QUERIES = 4;
}
-
- private static final int NUM_QUERY_LOADERS = 4;
-
- @VisibleForTesting
- AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
-
@VisibleForTesting
String mQuery;
@@ -147,7 +136,7 @@
setHasOptionsMenu(true);
final LoaderManager loaderManager = getLoaderManager();
- mSearchAdapter = new SearchResultsAdapter(this, mSearchFeatureProvider);
+ mSearchAdapter = new SearchResultsAdapter(this /* fragment */);
mSavedQueryController = new SavedQueryController(
getContext(), loaderManager, mSearchAdapter);
mSearchFeatureProvider.initFeedbackButton();
@@ -277,15 +266,11 @@
if (isEmptyQuery) {
final LoaderManager loaderManager = getLoaderManager();
- loaderManager.destroyLoader(SearchLoaderId.DATABASE);
- loaderManager.destroyLoader(SearchLoaderId.INSTALLED_APPS);
- loaderManager.destroyLoader(SearchLoaderId.ACCESSIBILITY_SERVICES);
- loaderManager.destroyLoader(SearchLoaderId.INPUT_DEVICES);
+ loaderManager.destroyLoader(SearchLoaderId.SEARCH_RESULT);
mShowingSavedQuery = true;
mSavedQueryController.loadSavedQueries();
mSearchFeatureProvider.hideFeedbackButton();
} else {
- mSearchAdapter.initializeSearch(mQuery);
restartLoaders();
}
@@ -301,35 +286,25 @@
}
@Override
- public Loader<Set<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
+ public Loader<List<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
final Activity activity = getActivity();
- switch (id) {
- case SearchLoaderId.DATABASE:
- return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
- case SearchLoaderId.INSTALLED_APPS:
- return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
- case SearchLoaderId.ACCESSIBILITY_SERVICES:
- return mSearchFeatureProvider.getAccessibilityServiceResultLoader(activity, mQuery);
- case SearchLoaderId.INPUT_DEVICES:
- return mSearchFeatureProvider.getInputDeviceResultLoader(activity, mQuery);
+ switch(id) {
+ case SearchLoaderId.SEARCH_RESULT:
+ return mSearchFeatureProvider.getSearchResultLoader(activity, mQuery);
default:
return null;
}
}
@Override
- public void onLoadFinished(Loader<Set<? extends SearchResult>> loader,
- Set<? extends SearchResult> data) {
- mSearchAdapter.addSearchResults(data, loader.getClass().getName());
- if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
- return;
- }
- mSearchAdapter.notifyResultsLoaded();
+ public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
+ List<? extends SearchResult> data) {
+ mSearchAdapter.postSearchResults(data);
}
@Override
- public void onLoaderReset(Loader<Set<? extends SearchResult>> loader) {
+ public void onLoaderReset(Loader<List<? extends SearchResult>> loader) {
}
/**
@@ -344,13 +319,8 @@
mSavedQueryController.loadSavedQueries();
} else {
final LoaderManager loaderManager = getLoaderManager();
- loaderManager.initLoader(SearchLoaderId.DATABASE, null /* args */, this /* callback */);
- loaderManager.initLoader(
- SearchLoaderId.INSTALLED_APPS, null /* args */, this /* callback */);
- loaderManager.initLoader(
- SearchLoaderId.ACCESSIBILITY_SERVICES, null /* args */, this /* callback */);
- loaderManager.initLoader(
- SearchLoaderId.INPUT_DEVICES, null /* args */, this /* callback */);
+ loaderManager.initLoader(SearchLoaderId.SEARCH_RESULT, null /* args */,
+ this /* callback */);
}
requery();
@@ -388,15 +358,8 @@
private void restartLoaders() {
mShowingSavedQuery = false;
final LoaderManager loaderManager = getLoaderManager();
- mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
loaderManager.restartLoader(
- SearchLoaderId.DATABASE, null /* args */, this /* callback */);
- loaderManager.restartLoader(
- SearchLoaderId.INSTALLED_APPS, null /* args */, this /* callback */);
- loaderManager.restartLoader(
- SearchLoaderId.ACCESSIBILITY_SERVICES, null /* args */, this /* callback */);
- loaderManager.restartLoader(
- SearchLoaderId.INPUT_DEVICES, null /* args */, this /* callback */);
+ SearchLoaderId.SEARCH_RESULT, null /* args */, this /* callback */);
}
public String getQuery() {
@@ -453,9 +416,7 @@
taggedData.add(Pair.create(
MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_RANK,
resultViewHolder.getAdapterPosition()));
- taggedData.add(Pair.create(
- MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_ASYNC_RANKING_STATE,
- mSearchAdapter.getAsyncRankingState()));
+ // TODO (b/67744820) Move metrics to SettingsIntelligence (including ranking state).
taggedData.add(Pair.create(
MetricsEvent.FIELD_SETTINGS_SEARCH_QUERY_LENGTH,
TextUtils.isEmpty(mQuery) ? 0 : mQuery.length()));
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index cb39970..69b2f9f 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -23,7 +23,6 @@
import com.android.settings.DateTimeSettings;
import com.android.settings.DeviceInfoSettings;
import com.android.settings.DisplaySettings;
-import com.android.settings.EncryptionAndCredential;
import com.android.settings.LegalSettings;
import com.android.settings.ScreenPinningSettings;
import com.android.settings.SecuritySettings;
@@ -75,6 +74,7 @@
import com.android.settings.notification.ZenModeBehaviorSettings;
import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintSettingsFragment;
+import com.android.settings.security.EncryptionAndCredential;
import com.android.settings.security.LockscreenDashboardFragment;
import com.android.settings.sim.SimSettings;
import com.android.settings.support.SupportDashboardActivity;
diff --git a/src/com/android/settings/search/SearchResultAggregator.java b/src/com/android/settings/search/SearchResultAggregator.java
new file mode 100644
index 0000000..890e3f0
--- /dev/null
+++ b/src/com/android/settings/search/SearchResultAggregator.java
@@ -0,0 +1,177 @@
+package com.android.settings.search;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Collects the sorted list of all setting search results.
+ *
+ * TODO (b/64939692) Convert the timing logs to metrics
+ */
+public class SearchResultAggregator {
+
+ private static final String TAG = "SearchResultAggregator";
+
+ /**
+ * Timeout for first task. Allows for longer delay.
+ */
+ private static final long LONG_CHECK_TASK_TIMEOUT_MS = 500;
+
+ /**
+ * Timeout for subsequent tasks to allow for fast returning tasks.
+ */
+ private static final long SHORT_CHECK_TASK_TIMEOUT_MS = 150;
+
+ private static SearchResultAggregator sResultAggregator;
+
+ // TODO (b/33577327) Merge the other loaders into a single dynamic loader
+ static final class ResultLoaderId {
+ static final int STATIC_RESULTS = 1;
+ static final int INSTALLED_RESULTS = 2;
+ static final int INPUT_RESULTS = 3;
+ static final int ACCESSIBILITY_RESULTS = 4;
+ }
+
+ private SearchResultAggregator() {
+ }
+
+ public static SearchResultAggregator getInstance() {
+ if (sResultAggregator == null) {
+ sResultAggregator = new SearchResultAggregator();
+ }
+
+ return sResultAggregator;
+ }
+
+ @NonNull
+ public synchronized List<? extends SearchResult> fetchResults(Context context, String query) {
+ SearchFeatureProvider mFeatureProvider = FeatureFactory.getFactory(
+ context).getSearchFeatureProvider();
+ ExecutorService executorService = mFeatureProvider.getExecutorService();
+
+ final DatabaseResultLoader staticResultsTask =
+ mFeatureProvider.getStaticSearchResultTask(context, query);
+ final InstalledAppResultLoader installedAppTask =
+ mFeatureProvider.getInstalledAppSearchTask(context, query);
+ final InputDeviceResultLoader inputDevicesTask =
+ mFeatureProvider.getInputDeviceResultTask(context, query);
+ final AccessibilityServiceResultLoader accessibilityServicesTask =
+ mFeatureProvider.getAccessibilityServiceResultTask(context,
+ query);
+
+ executorService.execute(staticResultsTask);
+ executorService.execute(installedAppTask);
+ executorService.execute(inputDevicesTask);
+ executorService.execute(accessibilityServicesTask);
+
+ SparseArray<List<? extends SearchResult>> resultsArray = new SparseArray<>();
+ List<? extends SearchResult> EMPTY_LIST = new ArrayList<>();
+
+ long allTasksStart = System.currentTimeMillis();
+ try {
+ resultsArray.put(ResultLoaderId.INPUT_RESULTS,
+ inputDevicesTask.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve input devices results in time: " + e);
+ resultsArray.put(ResultLoaderId.INPUT_RESULTS, EMPTY_LIST);
+ }
+
+ try {
+ resultsArray.put(ResultLoaderId.ACCESSIBILITY_RESULTS,
+ accessibilityServicesTask.get(SHORT_CHECK_TASK_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve accessibility results in time: " + e);
+ resultsArray.put(ResultLoaderId.ACCESSIBILITY_RESULTS, EMPTY_LIST);
+ }
+
+ try {
+ resultsArray.put(ResultLoaderId.STATIC_RESULTS,
+ staticResultsTask.get(LONG_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve static results: " + e);
+ resultsArray.put(ResultLoaderId.STATIC_RESULTS, EMPTY_LIST);
+ }
+
+ try {
+ resultsArray.put(ResultLoaderId.INSTALLED_RESULTS,
+ installedAppTask.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve installed app results in time: " + e);
+
+ resultsArray.put(ResultLoaderId.INSTALLED_RESULTS, EMPTY_LIST);
+ }
+
+ long mergeStartTime = System.currentTimeMillis();
+ Log.i(TAG, "Total result loader time: " + (mergeStartTime - allTasksStart));
+ List<? extends SearchResult> mergedResults = mergeSearchResults(resultsArray);
+ Log.i(TAG, "Total merge time: " + (System.currentTimeMillis() - mergeStartTime));
+ Log.i(TAG, "Total aggregator time: " + (System.currentTimeMillis() - allTasksStart));
+
+ return mergedResults;
+ }
+
+ // TODO (b/68255021) scale the dynamic search results ranks and do a k-way merge
+ private List<? extends SearchResult> mergeSearchResults(
+ SparseArray<List<? extends SearchResult>> resultsArray) {
+ List<? extends SearchResult> staticResults = resultsArray.get(
+ ResultLoaderId.STATIC_RESULTS);
+ List<? extends SearchResult> installedAppResults = resultsArray.get(
+ ResultLoaderId.INSTALLED_RESULTS);
+ List<? extends SearchResult> accessibilityResults = resultsArray.get(
+ ResultLoaderId.ACCESSIBILITY_RESULTS);
+ List<? extends SearchResult> inputDeviceResults = resultsArray.get(
+ ResultLoaderId.INPUT_RESULTS);
+ List<SearchResult> searchResults;
+
+ int staticSize = staticResults.size();
+ int appSize = installedAppResults.size();
+ int a11ySize = accessibilityResults.size();
+ int inputDeviceSize = inputDeviceResults.size();
+ int appIndex = 0;
+ int a11yIndex = 0;
+ int inputDeviceIndex = 0;
+ int rank = SearchResult.TOP_RANK;
+
+ // TODO: We need a helper method to do k-way merge.
+ searchResults = new ArrayList<>(staticSize + appSize + a11ySize + inputDeviceSize);
+ searchResults.addAll(resultsArray.get(ResultLoaderId.STATIC_RESULTS));
+
+ while (rank <= SearchResult.BOTTOM_RANK) {
+ while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
+ searchResults.add(installedAppResults.get(appIndex++));
+ }
+ while ((a11yIndex < a11ySize) && (accessibilityResults.get(a11yIndex).rank == rank)) {
+ searchResults.add(accessibilityResults.get(a11yIndex++));
+ }
+ while (inputDeviceIndex < inputDeviceSize
+ && inputDeviceResults.get(inputDeviceIndex).rank == rank) {
+ searchResults.add(inputDeviceResults.get(inputDeviceIndex++));
+ }
+ rank++;
+ }
+
+ while (appIndex < appSize) {
+ searchResults.add(installedAppResults.get(appIndex++));
+ }
+ while (a11yIndex < a11ySize) {
+ searchResults.add(accessibilityResults.get(a11yIndex++));
+ }
+ while (inputDeviceIndex < inputDeviceSize) {
+ searchResults.add(inputDeviceResults.get(inputDeviceIndex++));
+ }
+
+ return searchResults;
+ }
+}
diff --git a/src/com/android/settings/search/SearchResultDiffCallback.java b/src/com/android/settings/search/SearchResultDiffCallback.java
index b7bbc66..0f0b977 100644
--- a/src/com/android/settings/search/SearchResultDiffCallback.java
+++ b/src/com/android/settings/search/SearchResultDiffCallback.java
@@ -26,10 +26,11 @@
*/
public class SearchResultDiffCallback extends DiffUtil.Callback {
- private List<SearchResult> mOldList;
- private List<SearchResult> mNewList;
+ private List<? extends SearchResult> mOldList;
+ private List<? extends SearchResult> mNewList;
- public SearchResultDiffCallback(List<SearchResult> oldList, List<SearchResult> newList) {
+ public SearchResultDiffCallback(List<? extends SearchResult> oldList,
+ List<? extends SearchResult> newList) {
mOldList = oldList;
mNewList = newList;
}
diff --git a/src/com/android/settings/search/SearchResultLoader.java b/src/com/android/settings/search/SearchResultLoader.java
new file mode 100644
index 0000000..7ec3146
--- /dev/null
+++ b/src/com/android/settings/search/SearchResultLoader.java
@@ -0,0 +1,30 @@
+package com.android.settings.search;
+
+import com.android.settings.utils.AsyncLoader;
+
+import android.content.Context;
+
+import java.util.List;
+
+/**
+ * Loads a sorted list of Search results for a given query.
+ */
+public class SearchResultLoader extends AsyncLoader<List<? extends SearchResult>> {
+
+ private final String mQuery;
+
+ public SearchResultLoader(Context context, String query) {
+ super(context);
+ mQuery = query;
+ }
+
+ @Override
+ public List<? extends SearchResult> loadInBackground() {
+ SearchResultAggregator aggregator = SearchResultAggregator.getInstance();
+ return aggregator.fetchResults(getContext(), mQuery);
+ }
+
+ @Override
+ protected void onDiscardResult(List<? extends SearchResult> result) {
+ }
+}
diff --git a/src/com/android/settings/search/SearchResultsAdapter.java b/src/com/android/settings/search/SearchResultsAdapter.java
index 5fedc52..c05ce18 100644
--- a/src/com/android/settings/search/SearchResultsAdapter.java
+++ b/src/com/android/settings/search/SearchResultsAdapter.java
@@ -18,87 +18,25 @@
package com.android.settings.search;
import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.support.annotation.IntDef;
-import android.support.annotation.MainThread;
-import android.support.annotation.VisibleForTesting;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.settings.R;
-import com.android.settings.search.ranking.SearchResultsRankerCallback;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
- implements SearchResultsRankerCallback {
- private static final String TAG = "SearchResultsAdapter";
-
- @VisibleForTesting
- static final String DB_RESULTS_LOADER_KEY = DatabaseResultLoader.class.getName();
-
- @VisibleForTesting
- static final String APP_RESULTS_LOADER_KEY = InstalledAppResultLoader.class.getName();
- @VisibleForTesting
- static final String ACCESSIBILITY_LOADER_KEY = AccessibilityServiceResultLoader.class.getName();
- @VisibleForTesting
- static final String INPUT_DEVICE_LOADER_KEY = InputDeviceResultLoader.class.getName();
-
- @VisibleForTesting
- static final int MSG_RANKING_TIMED_OUT = 1;
+public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder> {
private final SearchFragment mFragment;
- private final Context mContext;
private final List<SearchResult> mSearchResults;
- private final List<SearchResult> mStaticallyRankedSearchResults;
- private Map<String, Set<? extends SearchResult>> mResultsMap;
- private final SearchFeatureProvider mSearchFeatureProvider;
- private List<Pair<String, Float>> mSearchRankingScores;
- private Handler mHandler;
- private boolean mSearchResultsLoaded;
- private boolean mSearchResultsUpdated;
- @IntDef({DISABLED, PENDING_RESULTS, SUCCEEDED, FAILED, TIMED_OUT})
- @Retention(RetentionPolicy.SOURCE)
- private @interface AsyncRankingState {}
- @VisibleForTesting
- static final int DISABLED = 0;
- @VisibleForTesting
- static final int PENDING_RESULTS = 1;
- @VisibleForTesting
- static final int SUCCEEDED = 2;
- @VisibleForTesting
- static final int FAILED = 3;
- @VisibleForTesting
- static final int TIMED_OUT = 4;
- private @AsyncRankingState int mAsyncRankingState;
-
- public SearchResultsAdapter(SearchFragment fragment,
- SearchFeatureProvider searchFeatureProvider) {
+ public SearchResultsAdapter(SearchFragment fragment) {
mFragment = fragment;
- mContext = fragment.getContext().getApplicationContext();
mSearchResults = new ArrayList<>();
- mResultsMap = new ArrayMap<>();
- mSearchRankingScores = new ArrayList<>();
- mStaticallyRankedSearchResults = new ArrayList<>();
- mSearchFeatureProvider = searchFeatureProvider;
setHasStableIds(true);
}
@@ -149,298 +87,30 @@
return mSearchResults.size();
}
- @MainThread
- @Override
- public void onRankingScoresAvailable(List<Pair<String, Float>> searchRankingScores) {
- // Received the scores, stop the timeout timer.
- getHandler().removeMessages(MSG_RANKING_TIMED_OUT);
- if (mAsyncRankingState == PENDING_RESULTS) {
- mAsyncRankingState = SUCCEEDED;
- mSearchRankingScores.clear();
- mSearchRankingScores.addAll(searchRankingScores);
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
- } else {
- Log.w(TAG, "Ranking scores became available in invalid state: " + mAsyncRankingState);
- }
- }
-
- @MainThread
- @Override
- public void onRankingFailed() {
- if (mAsyncRankingState == PENDING_RESULTS) {
- mAsyncRankingState = FAILED;
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
- } else {
- Log.w(TAG, "Ranking scores failed in invalid states: " + mAsyncRankingState);
- }
- }
-
- /**
- * Store the results from each of the loaders to be merged when all loaders are finished.
- *
- * @param results the results from the loader.
- * @param loaderClassName class name of the loader.
- */
- @MainThread
- public void addSearchResults(Set<? extends SearchResult> results, String loaderClassName) {
- if (results == null) {
- return;
- }
- mResultsMap.put(loaderClassName, results);
- }
-
/**
* Displays recent searched queries.
- *
- * @return The number of saved queries to display
*/
- public int displaySavedQuery(List<? extends SearchResult> data) {
+ public void displaySavedQuery(List<? extends SearchResult> data) {
clearResults();
mSearchResults.addAll(data);
notifyDataSetChanged();
- return mSearchResults.size();
- }
-
- /**
- * Notifies the adapter that all the unsorted results are loaded and now the ladapter can
- * proceed with ranking the results.
- */
- @MainThread
- public void notifyResultsLoaded() {
- mSearchResultsLoaded = true;
- // static ranking is skipped only if asyc ranking is already succeeded.
- if (mAsyncRankingState != SUCCEEDED) {
- doStaticRanking();
- }
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
}
public void clearResults() {
mSearchResults.clear();
- mStaticallyRankedSearchResults.clear();
- mResultsMap.clear();
notifyDataSetChanged();
}
- @VisibleForTesting
public List<SearchResult> getSearchResults() {
return mSearchResults;
}
- @MainThread
- public void initializeSearch(String query) {
- clearResults();
- mSearchResultsLoaded = false;
- mSearchResultsUpdated = false;
- if (mSearchFeatureProvider.isSmartSearchRankingEnabled(mContext)) {
- mAsyncRankingState = PENDING_RESULTS;
- mSearchFeatureProvider.cancelPendingSearchQuery(mContext);
- final Handler handler = getHandler();
- final long timeoutMs = mSearchFeatureProvider.smartSearchRankingTimeoutMs(mContext);
- handler.sendMessageDelayed(
- handler.obtainMessage(MSG_RANKING_TIMED_OUT), timeoutMs);
- mSearchFeatureProvider.querySearchResults(mContext, query, this);
- } else {
- mAsyncRankingState = DISABLED;
- }
- }
-
- @AsyncRankingState int getAsyncRankingState() {
- return mAsyncRankingState;
- }
-
- /**
- * Merge the results from each of the loaders into one list for the adapter.
- * Prioritizes results from the local database over installed apps.
- */
- private void doStaticRanking() {
- List<? extends SearchResult> databaseResults =
- getSortedLoadedResults(DB_RESULTS_LOADER_KEY);
- List<? extends SearchResult> installedAppResults =
- getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
- List<? extends SearchResult> accessibilityResults =
- getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
- List<? extends SearchResult> inputDeviceResults =
- getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
-
- int dbSize = databaseResults.size();
- int appSize = installedAppResults.size();
- int a11ySize = accessibilityResults.size();
- int inputDeviceSize = inputDeviceResults.size();
- int dbIndex = 0;
- int appIndex = 0;
- int a11yIndex = 0;
- int inputDeviceIndex = 0;
- int rank = SearchResult.TOP_RANK;
-
- // TODO: We need a helper method to do k-way merge.
- mStaticallyRankedSearchResults.clear();
- while (rank <= SearchResult.BOTTOM_RANK) {
- while ((dbIndex < dbSize) && (databaseResults.get(dbIndex).rank == rank)) {
- mStaticallyRankedSearchResults.add(databaseResults.get(dbIndex++));
- }
- while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
- mStaticallyRankedSearchResults.add(installedAppResults.get(appIndex++));
- }
- while ((a11yIndex < a11ySize) && (accessibilityResults.get(a11yIndex).rank == rank)) {
- mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
- }
- while (inputDeviceIndex < inputDeviceSize
- && inputDeviceResults.get(inputDeviceIndex).rank == rank) {
- mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
- }
- rank++;
- }
-
- while (dbIndex < dbSize) {
- mStaticallyRankedSearchResults.add(databaseResults.get(dbIndex++));
- }
- while (appIndex < appSize) {
- mStaticallyRankedSearchResults.add(installedAppResults.get(appIndex++));
- }
- while(a11yIndex < a11ySize) {
- mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
- }
- while (inputDeviceIndex < inputDeviceSize) {
- mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
- }
- }
-
- private void updateSearchResults() {
- switch (mAsyncRankingState) {
- case PENDING_RESULTS:
- break;
- case DISABLED:
- case FAILED:
- case TIMED_OUT:
- // When DISABLED or FAILED or TIMED_OUT, we use static ranking results.
- postSearchResults(mStaticallyRankedSearchResults, false);
- break;
- case SUCCEEDED:
- postSearchResults(doAsyncRanking(), true);
- break;
- }
- }
-
- private boolean canUpdateSearchResults() {
- // Results are not updated yet and db results are loaded and we are not waiting on async
- // ranking scores.
- return !mSearchResultsUpdated
- && mSearchResultsLoaded
- && mAsyncRankingState != PENDING_RESULTS;
- }
-
- @VisibleForTesting
- List<SearchResult> doAsyncRanking() {
- Set<? extends SearchResult> databaseResults =
- getUnsortedLoadedResults(DB_RESULTS_LOADER_KEY);
- List<? extends SearchResult> installedAppResults =
- getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
- List<? extends SearchResult> accessibilityResults =
- getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
- List<? extends SearchResult> inputDeviceResults =
- getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
- int dbSize = databaseResults.size();
- int appSize = installedAppResults.size();
- int a11ySize = accessibilityResults.size();
- int inputDeviceSize = inputDeviceResults.size();
-
- final List<SearchResult> asyncRankingResults = new ArrayList<>(
- dbSize + appSize + a11ySize + inputDeviceSize);
- TreeSet<SearchResult> dbResultsSortedByScores = new TreeSet<>(
- new Comparator<SearchResult>() {
- @Override
- public int compare(SearchResult o1, SearchResult o2) {
- float score1 = getRankingScoreByStableId(o1.stableId);
- float score2 = getRankingScoreByStableId(o2.stableId);
- if (score1 > score2) {
- return -1;
- } else if (score1 == score2) {
- return 0;
- } else {
- return 1;
- }
- }
- });
- dbResultsSortedByScores.addAll(databaseResults);
- asyncRankingResults.addAll(dbResultsSortedByScores);
- // Other results are not ranked by async ranking and appended at the end of the list.
- asyncRankingResults.addAll(installedAppResults);
- asyncRankingResults.addAll(accessibilityResults);
- asyncRankingResults.addAll(inputDeviceResults);
- return asyncRankingResults;
- }
-
- @VisibleForTesting
- Set<? extends SearchResult> getUnsortedLoadedResults(String loaderKey) {
- return mResultsMap.containsKey(loaderKey) ? mResultsMap.get(loaderKey) : new HashSet<>();
- }
-
- @VisibleForTesting
- List<? extends SearchResult> getSortedLoadedResults(String loaderKey) {
- List<? extends SearchResult> sortedLoadedResults =
- new ArrayList<>(getUnsortedLoadedResults(loaderKey));
- Collections.sort(sortedLoadedResults);
- return sortedLoadedResults;
- }
-
- /**
- * Looks up ranking score for stableId
- * @param stableId String of stableId
- * @return the ranking score corresponding to the given stableId. If there is no score
- * available for this stableId, -Float.MAX_VALUE is returned.
- */
- @VisibleForTesting
- Float getRankingScoreByStableId(int stableId) {
- for (Pair<String, Float> rankingScore : mSearchRankingScores) {
- if (Integer.toString(stableId).compareTo(rankingScore.first) == 0) {
- return rankingScore.second;
- }
- }
- // If stableId not found in the list, we assign the minimum score so it will appear at
- // the end of the list.
- Log.w(TAG, "stableId " + stableId + " was not in the ranking scores.");
- return -Float.MAX_VALUE;
- }
-
- @VisibleForTesting
- Handler getHandler() {
- if (mHandler == null) {
- mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_RANKING_TIMED_OUT) {
- mSearchFeatureProvider.cancelPendingSearchQuery(mContext);
- if (mAsyncRankingState == PENDING_RESULTS) {
- mAsyncRankingState = TIMED_OUT;
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
- } else {
- Log.w(TAG, "Ranking scores timed out in invalid state: " +
- mAsyncRankingState);
- }
- }
- }
- };
- }
- return mHandler;
- }
-
- @VisibleForTesting
- public void postSearchResults(List<SearchResult> newSearchResults, boolean detectMoves) {
+ public void postSearchResults(List<? extends SearchResult> newSearchResults) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
- new SearchResultDiffCallback(mSearchResults, newSearchResults), detectMoves);
+ new SearchResultDiffCallback(mSearchResults, newSearchResults));
mSearchResults.clear();
mSearchResults.addAll(newSearchResults);
diffResult.dispatchUpdatesTo(this);
mFragment.onSearchResultsDisplayed(mSearchResults.size());
- mSearchResultsUpdated = true;
}
}
diff --git a/src/com/android/settings/EncryptionAndCredential.java b/src/com/android/settings/security/EncryptionAndCredential.java
similarity index 93%
rename from src/com/android/settings/EncryptionAndCredential.java
rename to src/com/android/settings/security/EncryptionAndCredential.java
index 4892f7e..0972e3e 100644
--- a/src/com/android/settings/EncryptionAndCredential.java
+++ b/src/com/android/settings/security/EncryptionAndCredential.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.security;
-import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
-import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
@@ -30,11 +28,12 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
+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.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
@@ -43,7 +42,7 @@
* Encryption and Credential settings.
* TODO: Extends this from {@link DashboardFragment} instead
*/
-public class EncryptionAndCredential extends SettingsPreferenceFragment implements Indexable {
+public class EncryptionAndCredential extends DashboardFragment {
private static final String TAG = "EncryptionAndCredential";
@@ -69,12 +68,19 @@
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ protected String getLogTag() {
+ return TAG;
+ }
- final Activity activity = getActivity();
+ @Override
+ protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+ mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ return null;
+ }
- mUm = UserManager.get(activity);
+ @Override
+ protected int getPreferenceScreenResId() {
+ return 0;
}
/**
diff --git a/src/com/android/settings/utils/ManagedServiceSettings.java b/src/com/android/settings/utils/ManagedServiceSettings.java
index 7a888db..d488dfe 100644
--- a/src/com/android/settings/utils/ManagedServiceSettings.java
+++ b/src/com/android/settings/utils/ManagedServiceSettings.java
@@ -21,7 +21,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Fragment;
-import android.app.Notification;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -46,6 +45,7 @@
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.notification.EmptyTextSettings;
+import com.android.settings.widget.AppSwitchPreference;
import java.util.Collections;
import java.util.List;
@@ -127,7 +127,7 @@
Log.e(TAG, "can't find package name", e);
}
final String summary = service.loadLabel(mPm).toString();
- final SwitchPreference pref = new SwitchPreference(getPrefContext());
+ final SwitchPreference pref = new AppSwitchPreference(getPrefContext());
pref.setPersistent(false);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
UserHandle.getUserId(service.applicationInfo.uid)));
diff --git a/src/com/android/settings/vpn2/LegacyVpnPreference.java b/src/com/android/settings/vpn2/LegacyVpnPreference.java
index 4ef2808..8fa9680 100644
--- a/src/com/android/settings/vpn2/LegacyVpnPreference.java
+++ b/src/com/android/settings/vpn2/LegacyVpnPreference.java
@@ -16,6 +16,8 @@
package com.android.settings.vpn2;
+import static com.android.internal.net.LegacyVpnInfo.STATE_CONNECTED;
+
import android.content.Context;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
@@ -23,7 +25,6 @@
import com.android.internal.net.VpnProfile;
import com.android.settings.R;
-import static com.android.internal.net.LegacyVpnInfo.STATE_CONNECTED;
/**
* {@link android.support.v7.preference.Preference} tracks the underlying legacy vpn profile and
@@ -35,6 +36,7 @@
LegacyVpnPreference(Context context) {
super(context, null /* attrs */);
setIcon(R.drawable.ic_vpn_key);
+ setUseSmallIcon(true);
}
public VpnProfile getProfile() {
diff --git a/src/com/android/settings/widget/AppPreference.java b/src/com/android/settings/widget/AppPreference.java
new file mode 100644
index 0000000..8a0e6a1
--- /dev/null
+++ b/src/com/android/settings/widget/AppPreference.java
@@ -0,0 +1,63 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import com.android.settings.R;
+
+public class AppPreference extends Preference {
+
+ private int mProgress;
+ private boolean mProgressVisible;
+
+ public AppPreference(Context context) {
+ super(context);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public AppPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public void setProgress(int amount) {
+ mProgress = amount;
+ mProgressVisible = true;
+ notifyChanged();
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+
+ view.findViewById(R.id.summary_container)
+ .setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
+ final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
+ if (mProgressVisible) {
+ progress.setProgress(mProgress);
+ progress.setVisibility(View.VISIBLE);
+ } else {
+ progress.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/src/com/android/settings/widget/AppSwitchPreference.java b/src/com/android/settings/widget/AppSwitchPreference.java
new file mode 100644
index 0000000..d67e895
--- /dev/null
+++ b/src/com/android/settings/widget/AppSwitchPreference.java
@@ -0,0 +1,41 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.settings.R;
+
+public class AppSwitchPreference extends SwitchPreference {
+
+ public AppSwitchPreference(Context context) {
+ super(context);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+
+ view.findViewById(R.id.summary_container)
+ .setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
+ }
+}
diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java
index e446340..7489a77 100644
--- a/src/com/android/settings/widget/RadioButtonPickerFragment.java
+++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.support.annotation.LayoutRes;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
@@ -133,8 +134,13 @@
final String systemDefaultKey = getSystemDefaultKey();
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
+
+ final int customLayoutResId = getRadioButtonPreferenceCustomLayoutResId();
if (shouldShowItemNone()) {
final RadioButtonPreference nonePref = new RadioButtonPreference(getPrefContext());
+ if (customLayoutResId > 0) {
+ nonePref.setLayoutResource(customLayoutResId);
+ }
nonePref.setIcon(R.drawable.ic_remove_circle);
nonePref.setTitle(R.string.app_list_preference_none);
nonePref.setChecked(TextUtils.isEmpty(defaultKey));
@@ -144,6 +150,9 @@
if (candidateList != null) {
for (CandidateInfo info : candidateList) {
RadioButtonPreference pref = new RadioButtonPreference(getPrefContext());
+ if (customLayoutResId > 0) {
+ pref.setLayoutResource(customLayoutResId);
+ }
bindPreference(pref, info.getKey(), info, defaultKey);
bindPreferenceExtra(pref, info.getKey(), info, defaultKey, systemDefaultKey);
screen.addPreference(pref);
@@ -206,6 +215,14 @@
return null;
}
+ /**
+ * Provides a custom layout for each candidate row.
+ */
+ @LayoutRes
+ protected int getRadioButtonPreferenceCustomLayoutResId() {
+ return 0;
+ }
+
public static abstract class CandidateInfo {
public final boolean enabled;
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 8ee340a..5a10ebf 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -61,6 +61,7 @@
import com.android.settings.ProxySelector;
import com.android.settings.R;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.wifi.AccessPoint;
import java.net.Inet4Address;
@@ -116,8 +117,6 @@
/* Full list of phase2 methods */
private final ArrayAdapter<String> mPhase2FullAdapter;
- private final Handler mTextViewChangedHandler;
-
// e.g. AccessPoint.SECURITY_NONE
private int mAccessPointSecurity;
private TextView mPasswordView;
@@ -175,7 +174,6 @@
accessPoint.getSecurity();
mMode = mode;
- mTextViewChangedHandler = new Handler();
mContext = mConfigUi.getContext();
final Resources res = mContext.getResources();
@@ -1258,12 +1256,10 @@
@Override
public void afterTextChanged(Editable s) {
- mTextViewChangedHandler.post(new Runnable() {
- public void run() {
- showWarningMessagesIfAppropriate();
- enableSubmitIfAppropriate();
- }
- });
+ ThreadUtils.postOnMainThread(() -> {
+ showWarningMessagesIfAppropriate();
+ enableSubmitIfAppropriate();
+ });
}
@Override
diff --git a/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java b/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
index dfed801..dc98e21 100644
--- a/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
+++ b/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
@@ -63,7 +63,6 @@
private View mView;
private Button mSubmitButton;
private Button mCancelButton;
- private Handler mOnTextChangedHandler;
private TextView mPasswordView;
private TextView mLabelView;
private CheckBox mPasswordCheckBox;
@@ -79,7 +78,6 @@
mContext = context;
mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock");
- mOnTextChangedHandler = new Handler();
mSecurity = security;
mWifiManager = wifiManager;
}
@@ -90,7 +88,6 @@
mContext = context;
mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock");
- mOnTextChangedHandler = new Handler();
mSecurity = savedState.getInt(SECURITY);
mWifiManager = wifiManager;
}
@@ -226,12 +223,7 @@
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- mOnTextChangedHandler.post(new Runnable() {
- @Override
- public void run() {
- enableSubmitIfAppropriate();
- }
- });
+ enableSubmitIfAppropriate();
}
private void enableSubmitIfAppropriate() {
diff --git a/tests/anomaly-tester/res/values/strings.xml b/tests/anomaly-tester/res/values/strings.xml
index 99de34e..da56356 100644
--- a/tests/anomaly-tester/res/values/strings.xml
+++ b/tests/anomaly-tester/res/values/strings.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <string name="app_name">AnomalyTester</string>
+ <string name="app_name" translatable="false">AnomalyTester</string>
</resources>
diff --git a/tests/robotests/README.md b/tests/robotests/README.md
new file mode 100644
index 0000000..648f1af
--- /dev/null
+++ b/tests/robotests/README.md
@@ -0,0 +1,24 @@
+# Running Settings Robolectric tests
+
+
+## The full suite
+```
+$ croot
+$ make RunSettingsRoboTests
+```
+
+## Running a single test class
+
+```
+$ croot
+$ make RunSettingsRoboTests ROBOTEST_FILTER=<ClassName>
+```
+
+For example:
+
+```
+make RunSettingsRoboTests ROBOTEST_FILTER=CodeInspectionTest
+```
+
+You can also use partial class name in ROBOTEST_FILTER. If the partial class name matches
+multiple file names, all of them will be executed.
diff --git a/tests/robotests/src/com/android/settings/accounts/ProviderPreferenceTest.java b/tests/robotests/src/com/android/settings/accounts/ProviderPreferenceTest.java
new file mode 100644
index 0000000..4e4bb4f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accounts/ProviderPreferenceTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.accounts;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+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.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 ProviderPreferenceTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void shouldUseSmallIcon() {
+ final ProviderPreference providerPreference = new ProviderPreference(
+ mContext, "account_type", null /* icon */, "provider_name");
+ final boolean useSmallIcon =
+ ReflectionHelpers.getField(providerPreference, "mUseSmallIcon");
+ assertThat(useSmallIcon).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragmentTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragmentTest.java
index 66ec80a..3621edd 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragmentTest.java
@@ -17,6 +17,14 @@
package com.android.settings.applications.defaultapps;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.app.Activity;
import android.content.Context;
import android.os.UserManager;
@@ -24,9 +32,10 @@
import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
+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.widget.RadioButtonPreference;
import org.junit.Before;
@@ -41,13 +50,6 @@
import java.util.ArrayList;
import java.util.List;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DefaultAppPickerFragmentTest {
@@ -97,6 +99,12 @@
any(Pair.class));
}
+ @Test
+ public void shouldHaveAppPreferenceLayout() {
+ assertThat(mFragment.getRadioButtonPreferenceCustomLayoutResId())
+ .isEqualTo(R.layout.preference_app);
+ }
+
public static class TestFragment extends DefaultAppPickerFragment {
boolean setDefaultAppKeyCalled;
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java
index 120f3ae..a02a2de 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java
@@ -26,8 +26,9 @@
import android.support.v7.preference.Preference;
import com.android.settings.R;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.TwoTargetPreference;
import org.junit.Before;
import org.junit.Test;
@@ -79,6 +80,16 @@
verify(mPreference).setSummary(R.string.app_list_preference_none);
}
+ @Test
+ public void updateState_twoTargetPref_shouldUseSmallIcon() {
+ final TwoTargetPreference pref = mock(TwoTargetPreference.class);
+ mController = new TestPreferenceController(mContext);
+
+ mController.updateState(pref);
+
+ verify(pref).setUseSmallIcon(true);
+ }
+
private static class TestPreferenceController extends DefaultAppPreferenceController {
private DefaultAppInfo mAppInfo;
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java
index cf2403b..c32b262 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ApplicationViewHolderTest.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -48,9 +47,8 @@
@Before
public void seUp() {
mContext = RuntimeEnvironment.application;
- mView = ApplicationViewHolder.newView(LayoutInflater.from(mContext),
- new FrameLayout(mContext));
- mHolder = new ApplicationViewHolder(mView);
+ mView = ApplicationViewHolder.newView(new FrameLayout(mContext));
+ mHolder = new ApplicationViewHolder(mView, false /* useStableHeight */);
}
@Test
@@ -71,6 +69,10 @@
mHolder.setSummary(R.string.disabled);
assertThat(mHolder.mSummary.getText()).isEqualTo(mContext.getText(R.string.disabled));
+ assertThat(mHolder.mSummaryContainer.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mHolder.setSummary(null);
+ assertThat(mHolder.mSummaryContainer.getVisibility()).isEqualTo(View.GONE);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
index 9eb36ef..a0b0146 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
@@ -18,6 +18,10 @@
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_ALL;
+import static com.android.settings.applications.manageapplications.ManageApplications
+ .LIST_TYPE_MAIN;
+import static com.android.settings.applications.manageapplications.ManageApplications
+ .LIST_TYPE_NOTIFICATION;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -102,7 +106,7 @@
@Test
public void updateMenu_mainListType_showAppReset() {
setUpOptionMenus();
- ReflectionHelpers.setField(mFragment, "mListType", ManageApplications.LIST_TYPE_MAIN);
+ ReflectionHelpers.setField(mFragment, "mListType", LIST_TYPE_MAIN);
ReflectionHelpers.setField(mFragment, "mOptionsMenu", mMenu);
mFragment.updateOptionsMenu();
@@ -183,6 +187,21 @@
}
@Test
+ public void shouldUseStableItemHeight_mainType_yes() {
+ assertThat(ManageApplications.ApplicationsAdapter.shouldUseStableItemHeight(
+ LIST_TYPE_MAIN))
+ .isTrue();
+ assertThat(ManageApplications.ApplicationsAdapter.shouldUseStableItemHeight(
+ LIST_TYPE_NOTIFICATION))
+ .isFalse();
+ }
+
+ @Test
+ public void shouldUseStableItemHeight_notificationType_no() {
+
+ }
+
+ @Test
public void onRebuildComplete_shouldHideLoadingView() {
final Context context = RuntimeEnvironment.application;
final ManageApplications fragment = mock(ManageApplications.class);
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java
index 28ac9d9..fc761cc 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/MusicViewHolderControllerTest.java
@@ -27,7 +27,6 @@
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
import android.provider.DocumentsContract;
-import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -68,9 +67,8 @@
mController = new MusicViewHolderController(mContext, mSource, mVolume.fsUuid,
new UserHandle(0));
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mView = ApplicationViewHolder.newView(inflater, new FrameLayout(mContext));
- mHolder = new ApplicationViewHolder(mView);
+ mView = ApplicationViewHolder.newView(new FrameLayout(mContext));
+ mHolder = new ApplicationViewHolder(mView, false /* useStableHeight */);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java
index 2a26e31..0a147ac 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/PhotosViewHolderControllerTest.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
-import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -67,9 +66,8 @@
new PhotosViewHolderController(
mContext, mSource, mVolume.fsUuid, new UserHandle(0));
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- mView = ApplicationViewHolder.newView(inflater, new FrameLayout(mContext));
- mHolder = new ApplicationViewHolder(mView);
+ mView = ApplicationViewHolder.newView(new FrameLayout(mContext));
+ mHolder = new ApplicationViewHolder(mView, false /* useStableHeight */);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
index 220d829..e0d4638 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
@@ -15,21 +15,15 @@
*/
package com.android.settings.bluetooth;
-import static com.google.common.truth.Truth.assertThat;
-
-import android.bluetooth.BluetoothDevice;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import org.junit.Before;
import org.junit.Test;
@@ -37,7 +31,6 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import static org.mockito.Matchers.anyInt;
@@ -76,20 +69,4 @@
verify(mMetricsFeatureProvider).visible(eq(mContext), anyInt(),
eq(MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR));
}
-
- @Test
- public void testGetBluetoothDrawable_noBatteryLevel_returnSimpleDrawable() {
- final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application,
- R.drawable.ic_bt_laptop, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, 1 /* iconScale */);
-
- assertThat(drawable).isNotInstanceOf(BluetoothDeviceLayerDrawable.class);
- }
-
- @Test
- public void testGetBluetoothDrawable_hasBatteryLevel_returnLayerDrawable() {
- final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application,
- R.drawable.ic_bt_laptop, 10 /* batteryLevel */, 1 /* iconScale */);
-
- assertThat(drawable).isInstanceOf(BluetoothDeviceLayerDrawable.class);
- }
}
diff --git a/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
index ab7228c..30d60cc 100644
--- a/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
@@ -74,17 +74,6 @@
}
@Test
- public void onCreatePreferences_preferenceScreenTitleFeatureOff_shouldNotAddPreference() {
- SettingsShadowSystemProperties.set(
- FeatureFlagUtils.FFLAG_PREFIX + mFragment.FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE,
- "false");
-
- mFragment.onCreatePreferences(Bundle.EMPTY, null /* rootKey */);
-
- verify(mFragment, never()).addPreferencesFromResource(anyInt());
- }
-
- @Test
public void onCreatePreferences_noPreferenceScreenResId_shouldNotAddPreference() {
SettingsShadowSystemProperties.set(
FeatureFlagUtils.FFLAG_PREFIX + mFragment.FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE,
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
index 5e56714..8feef92 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
@@ -133,13 +133,6 @@
}
@Test
- public void isSuggestionEnabled_isNotLowMemoryDevice_shouldReturnTrue() {
- when(mActivityManager.isLowRamDevice()).thenReturn(false);
-
- assertThat(mProvider.isSuggestionEnabled(mContext)).isTrue();
- }
-
- @Test
public void isSuggestionV2Enabled_isNotLowMemoryDevice_sysPropOn_shouldReturnTrue() {
when(mActivityManager.isLowRamDevice()).thenReturn(false);
SettingsShadowSystemProperties.set(
@@ -148,14 +141,6 @@
}
@Test
- public void isSuggestionV2Enabled_isNotLowMemoryDevice_sysPropOff_shouldReturnTrue() {
- when(mActivityManager.isLowRamDevice()).thenReturn(false);
- SettingsShadowSystemProperties.set(
- FeatureFlagUtils.FFLAG_PREFIX + mProvider.FEATURE_FLAG_SUGGESTIONS_V2, "false");
- assertThat(mProvider.isSuggestionV2Enabled(mContext)).isFalse();
- }
-
- @Test
public void dismissSuggestion_noParserOrSuggestion_noop() {
mProvider.dismissSuggestion(mContext, null, (Tile) null);
mProvider.dismissSuggestion(mContext, mSuggestionParser, null);
diff --git a/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java
index 1367870..f59c29f 100644
--- a/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/OemUnlockPreferenceControllerTest.java
@@ -18,18 +18,17 @@
import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
.REQUEST_CODE_ENABLE_OEM_UNLOCK;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
+import android.app.FragmentManager;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.UserManager;
import android.service.oemlock.OemLockManager;
@@ -43,6 +42,7 @@
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;
@@ -81,6 +81,8 @@
mController = new OemUnlockPreferenceController(mContext, mActivity, mFragment);
when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
mPreference);
+ when(mFragment.getChildFragmentManager()).thenReturn(
+ mock(FragmentManager.class, Answers.RETURNS_DEEP_STUBS));
mController.displayPreference(mPreferenceScreen);
}
@@ -114,8 +116,8 @@
@Test
public void onPreferenceChanged_turnOffUnlock() {
mController.onPreferenceChange(null, false);
-
verify(mOemLockManager).setOemUnlockAllowedByUser(false);
+ verify(mFragment).getChildFragmentManager();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
index b737d1f..7d47fc2 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
@@ -32,6 +32,7 @@
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.TestConfig;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
@@ -41,6 +42,8 @@
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
import com.android.settings.widget.MasterSwitchPreference;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,6 +86,10 @@
mContext, mMetricsFeature, mFragmentManager);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
}
+ @After
+ public void tearDown() {
+ SettingsShadowSystemProperties.clear();
+ }
@Test
public void isAvailable_shouldReturnTrue_forHighRamDevice() {
@@ -146,6 +153,9 @@
public void togglingOnShouldTriggerWarningFragment() {
FragmentTransaction transaction = mock(FragmentTransaction.class);
when (mFragmentManager.beginTransaction()).thenReturn(transaction);
+ SettingsShadowSystemProperties.set(
+ AutomaticStorageManagementSwitchPreferenceController
+ .STORAGE_MANAGER_ENABLED_BY_DEFAULT_PROPERTY, "false");
mController.onSwitchToggled(true);
diff --git a/tests/robotests/src/com/android/settings/display/ShowOperatorNamePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/ShowOperatorNamePreferenceControllerTest.java
new file mode 100644
index 0000000..23b43d6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/ShowOperatorNamePreferenceControllerTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.display;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+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;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ShowOperatorNamePreferenceControllerTest {
+
+ private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock
+ private SwitchPreference mPreference;
+
+ private ShowOperatorNamePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mController = new ShowOperatorNamePreferenceController(mContext);
+ }
+
+ @Test
+ public void testIsAvailable_configIsTrue_ReturnTrue() {
+ when(mContext.getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar))
+ .thenReturn(true);
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void testIsAvailable_configIsFalse_ReturnFalse() {
+ when(mContext.getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar))
+ .thenReturn(false);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void testOnPreferenceChange_TurnOn_ReturnOn() {
+ mController.onPreferenceChange(mPreference, true);
+
+ final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
+ KEY_SHOW_OPERATOR_NAME, 0);
+ assertThat(mode).isEqualTo(1);
+ }
+
+ @Test
+ public void testOnPreferenceChange_TurnOff_ReturnOff() {
+ mController.onPreferenceChange(mPreference, false);
+
+ final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
+ KEY_SHOW_OPERATOR_NAME, 1);
+ assertThat(mode).isEqualTo(0);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java
index 0d22d2e..81223a5 100644
--- a/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java
@@ -16,6 +16,16 @@
package com.android.settings.enterprise;
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+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.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -23,10 +33,10 @@
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.applications.UserAppInfo;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -43,16 +53,6 @@
import java.util.List;
import java.util.Set;
-import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
-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.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class ApplicationListPreferenceControllerTest {
@@ -120,7 +120,7 @@
implements ApplicationListPreferenceController.ApplicationListBuilder {
@Override
public void buildApplicationList(Context context,
- ApplicationFeatureProvider.ListOfAppsCallback callback) {
+ ApplicationFeatureProvider.ListOfAppsCallback callback) {
final List<UserAppInfo> apps = new ArrayList<>();
final UserInfo user = new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN);
apps.add(new UserAppInfo(user, buildInfo(MAIN_USER_APP_UID, APP_1, 0, 0)));
diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java
new file mode 100644
index 0000000..ace535e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
+import android.media.AudioAttributes;
+import android.os.CancellationSignal;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settings.testutils.shadow.ShadowVibrator;
+import com.android.settings.wrapper.FingerprintManagerWrapper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+ manifest = TestConfig.MANIFEST_PATH,
+ sdk = Config.NEWEST_SDK,
+ shadows = {
+ ShadowUtils.class,
+ ShadowVibrator.class
+ })
+public class FingerprintEnrollEnrollingTest {
+
+ @Mock
+ private FingerprintManagerWrapper mFingerprintManager;
+
+ private FingerprintEnrollEnrolling mActivity;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowUtils.setFingerprintManager(mFingerprintManager);
+ ShadowVibrator.addToServiceMap();
+
+ mActivity = Robolectric.buildActivity(
+ FingerprintEnrollEnrolling.class,
+ new Intent()
+ // Set the challenge token so the confirm screen will not be shown
+ .putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]))
+ .setup().get();
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUtils.reset();
+ ShadowVibrator.reset();
+ }
+
+ @Test
+ public void fingerprintEnrollHelp_shouldShowHelpTextAndVibrate() {
+ EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback();
+
+ enrollmentCallback.onEnrollmentProgress(123);
+ enrollmentCallback.onEnrollmentHelp(
+ FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
+ "test enrollment help");
+
+ TextView errorText = mActivity.findViewById(R.id.error_text);
+ assertThat(errorText.getText()).isEqualTo("test enrollment help");
+
+ Robolectric.getForegroundThreadScheduler().advanceBy(2, TimeUnit.MILLISECONDS);
+
+
+ ShadowVibrator shadowVibrator =
+ Shadow.extract(application.getSystemService(Vibrator.class));
+ verify(shadowVibrator.delegate).vibrate(
+ anyInt(),
+ nullable(String.class),
+ any(VibrationEffect.class),
+ nullable(AudioAttributes.class));
+ }
+
+ private EnrollmentCallback verifyAndCaptureEnrollmentCallback() {
+ ArgumentCaptor<EnrollmentCallback> callbackCaptor =
+ ArgumentCaptor.forClass(EnrollmentCallback.class);
+ verify(mFingerprintManager).enroll(
+ any(byte[].class),
+ any(CancellationSignal.class),
+ anyInt(),
+ anyInt(),
+ callbackCaptor.capture());
+
+ return callbackCaptor.getValue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerGaugePreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerGaugePreferenceTest.java
index 1259670..32f6a96 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerGaugePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerGaugePreferenceTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settings.fuelgauge;
+import static com.google.common.truth.Truth.assertThat;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
@@ -35,8 +37,6 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import static com.google.common.truth.Truth.assertThat;
-
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class PowerGaugePreferenceTest {
@@ -53,7 +53,7 @@
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mRootView = LayoutInflater.from(mContext).inflate(R.layout.preference,
+ mRootView = LayoutInflater.from(mContext).inflate(R.layout.preference_app,
null);
mWidgetView = LayoutInflater.from(mContext).inflate(R.layout.preference_widget_summary,
null);
@@ -61,6 +61,7 @@
mPreferenceViewHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
mPowerGaugePreference = new PowerGaugePreference(mContext);
+ assertThat(mPowerGaugePreference.getLayoutResource()).isEqualTo(R.layout.preference_app);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
index 8030463..2b5e704 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
@@ -31,6 +31,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.os.UserManager;
import android.support.v7.preference.PreferenceCategory;
@@ -93,6 +94,10 @@
private PreferenceGroup mUsageListGroup;
@Mock
private ConnectivityManager mConnectivityManager;
+ @Mock
+ private UserInfo mNormalUserInfo;
+ @Mock
+ private UserInfo mManagedUserInfo;
private PowerUsageAdvanced mPowerUsageAdvanced;
private PowerUsageData mPowerUsageData;
private Context mShadowContext;
@@ -136,6 +141,11 @@
mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
mMaxBatterySipper.drainType = DrainType.BLUETOOTH;
mNormalBatterySipper.drainType = DrainType.SCREEN;
+
+ doReturn(true).when(mNormalUserInfo).isEnabled();
+ doReturn(false).when(mNormalUserInfo).isManagedProfile();
+ doReturn(true).when(mManagedUserInfo).isEnabled();
+ doReturn(true).when(mManagedUserInfo).isManagedProfile();
}
@Test
@@ -293,9 +303,12 @@
}
@Test
- public void testShouldHideCategory_typeUserAndOnlyOne_returnTrue() {
+ public void testShouldHideCategory_typeUserAndOnlyOneNormalUser_returnTrue() {
mPowerUsageData.usageType = UsageType.USER;
- doReturn(1).when(mUserManager).getUserCount();
+ List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(mNormalUserInfo);
+ userInfos.add(mManagedUserInfo);
+ doReturn(userInfos).when(mUserManager).getUsers();
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isTrue();
}
@@ -321,7 +334,10 @@
@Test
public void testShouldHideCategory_typeUserAndMoreThanOne_returnFalse() {
mPowerUsageData.usageType = UsageType.USER;
- doReturn(2).when(mUserManager).getUserCount();
+ List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(mNormalUserInfo);
+ userInfos.add(mNormalUserInfo);
+ doReturn(userInfos).when(mUserManager).getUsers();
assertThat(mPowerUsageAdvanced.shouldHideCategory(mPowerUsageData)).isFalse();
}
@@ -423,4 +439,5 @@
mPowerUsageAdvanced.refreshUi();
verify(mHistPref, atLeastOnce()).setBottomSummary(any());
}
+
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java
index 8cd5a06..38391c9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java
@@ -18,8 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
-import android.os.Build;
+import static org.mockito.Mockito.verify;
+import android.os.Build;
+import android.util.Pair;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.fuelgauge.anomaly.action.StopAndBackgroundCheckAction;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
@@ -31,18 +36,42 @@
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, shadows = {
ShadowKeyValueListParserWrapperImpl.class})
public class AnomalyUtilsTest {
+ private static final String PACKAGE_NAME_WAKEUP = "com.android.app1";
+ private static final String PACKAGE_NAME_WAKELOCK = "com.android.app2";
+ private static final int CONTEXT_ID = 55;
+
+ @Mock
+ private MetricsFeatureProvider mMetricsFeatureProvider;
private AnomalyUtils mAnomalyUtils;
+ private Anomaly mWakeupAnomaly;
+ private Anomaly mWakeLockAnomaly;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
mAnomalyUtils = new AnomalyUtils(RuntimeEnvironment.application);
+
+ mWakeLockAnomaly = new Anomaly.Builder()
+ .setType(Anomaly.AnomalyType.WAKE_LOCK)
+ .setPackageName(PACKAGE_NAME_WAKELOCK)
+ .build();
+ mWakeupAnomaly = new Anomaly.Builder()
+ .setType(Anomaly.AnomalyType.WAKEUP_ALARM)
+ .setPackageName(PACKAGE_NAME_WAKEUP)
+ .build();
}
@Test
@@ -97,4 +126,47 @@
assertThat(mAnomalyUtils.getAnomalyDetector(Anomaly.AnomalyType.WAKEUP_ALARM)).isInstanceOf(
WakeupAlarmAnomalyDetector.class);
}
+
+ @Test
+ public void testLogAnomaly() {
+ mAnomalyUtils.logAnomaly(mMetricsFeatureProvider, mWakeLockAnomaly, CONTEXT_ID);
+
+ verify(mMetricsFeatureProvider).action(RuntimeEnvironment.application,
+ MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKELOCK,
+ PACKAGE_NAME_WAKELOCK,
+ Pair.create(
+ MetricsProto.MetricsEvent.FIELD_CONTEXT,
+ CONTEXT_ID),
+ Pair.create(
+ MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
+ Anomaly.AnomalyActionType.FORCE_STOP));
+ }
+
+ @Test
+ public void testLogAnomalies() {
+ final List<Anomaly> anomalies = new ArrayList<>();
+ anomalies.add(mWakeLockAnomaly);
+ anomalies.add(mWakeupAnomaly);
+
+ mAnomalyUtils.logAnomalies(mMetricsFeatureProvider, anomalies, CONTEXT_ID);
+
+ verify(mMetricsFeatureProvider).action(RuntimeEnvironment.application,
+ MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKELOCK,
+ PACKAGE_NAME_WAKELOCK,
+ Pair.create(
+ MetricsProto.MetricsEvent.FIELD_CONTEXT,
+ CONTEXT_ID),
+ Pair.create(
+ MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
+ Anomaly.AnomalyActionType.FORCE_STOP));
+ verify(mMetricsFeatureProvider).action(RuntimeEnvironment.application,
+ MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKEUP_ALARM,
+ PACKAGE_NAME_WAKEUP,
+ Pair.create(
+ MetricsProto.MetricsEvent.FIELD_CONTEXT,
+ CONTEXT_ID),
+ Pair.create(
+ MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
+ Anomaly.AnomalyActionType.STOP_AND_BACKGROUND_CHECK));
+ }
}
diff --git a/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultFutureTaskTest.java
similarity index 83%
rename from tests/robotests/src/com/android/settings/search/AccessibilityServiceResultLoaderTest.java
rename to tests/robotests/src/com/android/settings/search/AccessibilityServiceResultFutureTaskTest.java
index 4896dc4..0e4abe1 100644
--- a/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultFutureTaskTest.java
@@ -17,6 +17,7 @@
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -46,7 +47,7 @@
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class AccessibilityServiceResultLoaderTest {
+public class AccessibilityServiceResultFutureTaskTest {
private static final String QUERY = "test_query";
@@ -59,7 +60,7 @@
@Mock
private SiteMapManager mSiteMapManager;
- private AccessibilityServiceResultLoader mLoader;
+ private AccessibilityServiceResultLoader.AccessibilityServiceResultCallable mCallable;
@Before
public void setUp() {
@@ -68,19 +69,20 @@
.thenReturn(mAccessibilityManager);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- mLoader = new AccessibilityServiceResultLoader(mContext, QUERY, mSiteMapManager);
+ mCallable = new AccessibilityServiceResultLoader.AccessibilityServiceResultCallable(
+ mContext, QUERY, mSiteMapManager);
}
@Test
- public void query_noService_shouldNotReturnAnything() {
- assertThat(mLoader.loadInBackground()).isEmpty();
+ public void query_noService_shouldNotReturnAnything() throws Exception {
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_hasServiceMatchingTitle_shouldReturnResult() {
+ public void query_hasServiceMatchingTitle_shouldReturnResult() throws Exception {
addFakeAccessibilityService();
- List<? extends SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+ List<? extends SearchResult> results = mCallable.call();
assertThat(results).hasSize(1);
SearchResult result = results.get(0);
@@ -88,13 +90,14 @@
}
@Test
- public void query_serviceDoesNotMatchTitle_shouldReturnResult() {
+ public void query_serviceDoesNotMatchTitle_shouldReturnResult() throws Exception {
addFakeAccessibilityService();
- mLoader = new AccessibilityServiceResultLoader(mContext,
+ mCallable = new AccessibilityServiceResultLoader.AccessibilityServiceResultCallable(
+ mContext,
QUERY + "no_match", mSiteMapManager);
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
}
private void addFakeAccessibilityService() {
diff --git a/tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/InputDeviceResultFutureTaskTest.java
similarity index 87%
rename from tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java
rename to tests/robotests/src/com/android/settings/search/InputDeviceResultFutureTaskTest.java
index a955af1..e31b3d7 100644
--- a/tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/InputDeviceResultFutureTaskTest.java
@@ -17,9 +17,11 @@
package com.android.settings.search;
import static android.content.Context.INPUT_METHOD_SERVICE;
+
import static com.android.settings.search.InputDeviceResultLoader.PHYSICAL_KEYBOARD_FRAGMENT;
import static com.android.settings.search.InputDeviceResultLoader.VIRTUAL_KEYBOARD_FRAGMENT;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -58,7 +60,7 @@
shadows = {
ShadowInputDevice.class
})
-public class InputDeviceResultLoaderTest {
+public class InputDeviceResultFutureTaskTest {
private static final String QUERY = "test_query";
private static final List<String> PHYSICAL_KEYBOARD_BREADCRUMB;
@@ -84,7 +86,7 @@
@Mock
private PackageManager mPackageManager;
- private InputDeviceResultLoader mLoader;
+ private InputDeviceResultLoader.InputDeviceResultCallable mCallable;
@Before
public void setUp() {
@@ -99,7 +101,8 @@
when(mContext.getString(anyInt()))
.thenAnswer(invocation -> RuntimeEnvironment.application.getString(
(Integer) invocation.getArguments()[0]));
- mLoader = new InputDeviceResultLoader(mContext, QUERY, mSiteMapManager);
+ mCallable = new InputDeviceResultLoader.InputDeviceResultCallable(mContext, QUERY,
+ mSiteMapManager);
}
@After
@@ -108,18 +111,19 @@
}
@Test
- public void query_noKeyboard_shouldNotReturnAnything() {
- assertThat(mLoader.loadInBackground()).isEmpty();
+ public void query_noKeyboard_shouldNotReturnAnything() throws Exception {
+
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_hasPhysicalKeyboard_match() {
+ public void query_hasPhysicalKeyboard_match() throws Exception {
addPhysicalKeyboard(QUERY);
when(mSiteMapManager.buildBreadCrumb(mContext, PHYSICAL_KEYBOARD_FRAGMENT,
RuntimeEnvironment.application.getString(R.string.physical_keyboard_title)))
.thenReturn(PHYSICAL_KEYBOARD_BREADCRUMB);
- final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+ final List<? extends SearchResult> results = mCallable.call();
assertThat(results).hasSize(1);
assertThat(results.get(0).title).isEqualTo(QUERY);
@@ -128,13 +132,13 @@
}
@Test
- public void query_hasVirtualKeyboard_match() {
+ public void query_hasVirtualKeyboard_match() throws Exception {
addVirtualKeyboard(QUERY);
when(mSiteMapManager.buildBreadCrumb(mContext, VIRTUAL_KEYBOARD_FRAGMENT,
RuntimeEnvironment.application.getString(R.string.add_virtual_keyboard)))
.thenReturn(VIRTUAL_KEYBOARD_BREADCRUMB);
- final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+ final List<? extends SearchResult> results = mCallable.call();
assertThat(results).hasSize(1);
assertThat(results.get(0).title).isEqualTo(QUERY);
assertThat(results.get(0).breadcrumbs)
@@ -142,11 +146,11 @@
}
@Test
- public void query_hasPhysicalVirtualKeyboard_doNotMatch() {
+ public void query_hasPhysicalVirtualKeyboard_doNotMatch() throws Exception {
addPhysicalKeyboard("abc");
addVirtualKeyboard("def");
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
verifyZeroInteractions(mSiteMapManager);
}
@@ -170,4 +174,4 @@
when(mImm.getInputMethodList()).thenReturn(imis);
}
-}
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
index f1a25a1..0e84dc7 100644
--- a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
@@ -19,7 +19,9 @@
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyList;
@@ -53,8 +55,6 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
@@ -77,7 +77,7 @@
@Mock
private SiteMapManager mSiteMapManager;
- private InstalledAppResultLoader mLoader;
+ private InstalledAppResultLoader.InstalledAppResultCallable mCallable;
@Before
public void setUp() {
@@ -109,49 +109,50 @@
}
@Test
- public void query_noMatchingQuery_shouldReturnEmptyResult() {
+ public void query_noMatchingQuery_shouldReturnEmptyResult() throws Exception {
final String query = "abc";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_matchingQuery_shouldReturnNonSystemApps() {
+ public void query_matchingQuery_shouldReturnNonSystemApps() throws Exception {
final String query = "app";
- mLoader = spy(new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = spy(new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager));
- when(mLoader.getContext()).thenReturn(mContext);
when(mSiteMapManager.buildBreadCrumb(eq(mContext), anyString(), anyString()))
.thenReturn(Arrays.asList(new String[]{"123"}));
- assertThat(mLoader.loadInBackground().size()).isEqualTo(3);
+ assertThat(mCallable.call()).hasSize(3);
verify(mSiteMapManager)
.buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void query_matchingQuery_shouldReturnSystemAppUpdates() {
+ public void query_matchingQuery_shouldReturnSystemAppUpdates() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_UPDATED_SYSTEM_APP,
0 /* targetSdkVersion */)));
final String query = "app";
- mLoader = spy(new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = spy(new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager));
- when(mLoader.getContext()).thenReturn(mContext);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
verify(mSiteMapManager)
.buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void query_matchingQuery_shouldReturnSystemAppIfLaunchable() {
+ public void query_matchingQuery_shouldReturnSystemAppIfLaunchable() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM,
@@ -164,14 +165,15 @@
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_matchingQuery_shouldReturnSystemAppIfHomeApp() {
+ public void query_matchingQuery_shouldReturnSystemAppIfHomeApp() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM,
@@ -180,28 +182,26 @@
any(Intent.class), anyInt(), anyInt()))
.thenReturn(null);
- when(mPackageManagerWrapper.getHomeActivities(anyList())).thenAnswer(new Answer<Object>() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- final List<ResolveInfo> list = (List<ResolveInfo>) invocation.getArguments()[0];
- final ResolveInfo info = new ResolveInfo();
- info.activityInfo = new ActivityInfo();
- info.activityInfo.packageName = "app1";
- list.add(info);
- return null;
- }
+ when(mPackageManagerWrapper.getHomeActivities(anyList())).thenAnswer(invocation -> {
+ final List<ResolveInfo> list = (List<ResolveInfo>) invocation.getArguments()[0];
+ final ResolveInfo info = new ResolveInfo();
+ info.activityInfo = new ActivityInfo();
+ info.activityInfo.packageName = "app1";
+ list.add(info);
+ return null;
});
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_matchingQuery_shouldNotReturnSystemAppIfNotLaunchable() {
+ public void query_matchingQuery_shouldNotReturnSystemAppIfNotLaunchable() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM,
@@ -212,21 +212,23 @@
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
verify(mSiteMapManager, never())
.buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void query_matchingQuery_multipleResults() {
+ public void query_matchingQuery_multipleResults() throws Exception {
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- final Set<? extends SearchResult> results = mLoader.loadInBackground();
+ final List<? extends SearchResult> results = mCallable.call();
Set<CharSequence> expectedTitles = new HashSet<>(Arrays.asList("app4", "app", "appBuffer"));
Set<CharSequence> actualTitles = new HashSet<>();
@@ -237,161 +239,172 @@
}
@Test
- public void query_normalWord_MatchPrefix() {
+ public void query_normalWord_MatchPrefix() throws Exception {
final String query = "ba";
final String packageName = "Bananas";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_CapitalCase_DoestMatchSecondWord() {
+ public void query_CapitalCase_DoestMatchSecondWord() throws Exception {
final String query = "Apples";
final String packageName = "BananasApples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(0);
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_TwoWords_MatchesFirstWord() {
+ public void query_TwoWords_MatchesFirstWord() throws Exception {
final String query = "Banana";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_TwoWords_MatchesSecondWord() {
+ public void query_TwoWords_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_ThreeWords_MatchesThirdWord() {
+ public void query_ThreeWords_MatchesThirdWord() throws Exception {
final String query = "Pear";
final String packageName = "Bananas Apples Pears";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_DoubleSpacedWords_MatchesSecondWord() {
+ public void query_DoubleSpacedWords_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_SpecialChar_MatchesSecondWord() {
+ public void query_SpecialChar_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas & Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_TabSeparated_MatchesSecondWord() {
+ public void query_TabSeparated_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas\tApples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_LeadingNumber_MatchesWord() {
+ public void query_LeadingNumber_MatchesWord() throws Exception {
final String query = "4";
final String packageName = "4Bananas";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_FirstWordPrefixOfQuery_NoMatch() {
+ public void query_FirstWordPrefixOfQuery_NoMatch() throws Exception {
final String query = "Bananass";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(0);
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_QueryLongerThanAppName_NoMatch() {
+ public void query_QueryLongerThanAppName_NoMatch() throws Exception {
final String query = "BananasApples";
final String packageName = "Bananas";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(0);
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_appExistsInBothProfiles() {
+ public void query_appExistsInBothProfiles() throws Exception {
final String query = "carrot";
final String packageName = "carrot";
final int user1 = 0;
@@ -414,10 +427,11 @@
packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- Set<AppSearchResult> searchResults = (Set<AppSearchResult>) mLoader.loadInBackground();
+ List<AppSearchResult> searchResults = (List<AppSearchResult>) mCallable.call();
assertThat(searchResults).hasSize(2);
Set<Integer> uidResults = searchResults.stream().map(result -> result.info.uid).collect(
diff --git a/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java b/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java
deleted file mode 100644
index 0a06a35..0000000
--- a/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java
+++ /dev/null
@@ -1,39 +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.search;
-
-import android.content.Context;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class MockAccessibilityLoader extends AccessibilityServiceResultLoader {
-
- public MockAccessibilityLoader(Context context) {
- super(context, "test_query", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockAppLoader.java b/tests/robotests/src/com/android/settings/search/MockAppLoader.java
deleted file mode 100644
index c68cbdf..0000000
--- a/tests/robotests/src/com/android/settings/search/MockAppLoader.java
+++ /dev/null
@@ -1,46 +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.search;
-
-import android.content.Context;
-import com.android.settings.search.InstalledAppResultLoader;
-import com.android.settings.search.SearchResult;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Mock loader to subvert the requirements of returning data while also driving the Loader
- * lifecycle.
- */
-class MockAppLoader extends InstalledAppResultLoader {
-
- public MockAppLoader(Context context) {
- super(context, null, "", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockDBLoader.java b/tests/robotests/src/com/android/settings/search/MockDBLoader.java
deleted file mode 100644
index b28c1ed..0000000
--- a/tests/robotests/src/com/android/settings/search/MockDBLoader.java
+++ /dev/null
@@ -1,46 +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.search;
-
-import android.content.Context;
-import com.android.settings.search.DatabaseResultLoader;
-import com.android.settings.search.SearchResult;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Mock loader to subvert the requirements of returning data while also driving the Loader
- * lifecycle.
- */
-class MockDBLoader extends DatabaseResultLoader {
-
- public MockDBLoader(Context context) {
- super(context, "test", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java b/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java
deleted file mode 100644
index 2c16b14..0000000
--- a/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java
+++ /dev/null
@@ -1,38 +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.search;
-
-import android.content.Context;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class MockInputDeviceResultLoader extends InputDeviceResultLoader {
- public MockInputDeviceResultLoader(Context context) {
- super(context, "test_query", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockSearchResultLoader.java b/tests/robotests/src/com/android/settings/search/MockSearchResultLoader.java
new file mode 100644
index 0000000..6af258a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/MockSearchResultLoader.java
@@ -0,0 +1,29 @@
+package com.android.settings.search;
+
+import android.content.Context;
+
+import com.android.settings.search.SearchResult;
+import com.android.settings.search.SearchResultLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mock loader to subvert the requirements of returning data while also driving the Loader
+ * lifecycle.
+ */
+public class MockSearchResultLoader extends SearchResultLoader {
+
+ public MockSearchResultLoader(Context context) {
+ super(context, "test");
+ }
+
+ @Override
+ public List<? extends SearchResult> loadInBackground() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ protected void onDiscardResult(List<? extends SearchResult> result) {
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
index 050d7aa..a529b0b 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
@@ -21,7 +21,6 @@
import android.app.Activity;
import android.content.ComponentName;
-
import com.android.settings.TestConfig;
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -33,6 +32,10 @@
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SearchFeatureProviderImplTest {
@@ -43,7 +46,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivity = Robolectric.buildActivity(Activity.class).create().visible().get();
- mProvider = new SearchFeatureProviderImpl();
+ mProvider = spy(new SearchFeatureProviderImpl());
}
@Test
@@ -57,18 +60,19 @@
@Test
public void getDatabaseSearchLoader_shouldCleanupQuery() {
final String query = " space ";
- final DatabaseResultLoader loader = mProvider.getDatabaseSearchLoader(mActivity, query);
- assertThat(loader.mQueryText).isEqualTo(query.trim());
+ mProvider.getStaticSearchResultTask(mActivity, query);
+
+ verify(mProvider).cleanQuery(eq(query));
}
@Test
public void getInstalledAppSearchLoader_shouldCleanupQuery() {
final String query = " space ";
- final InstalledAppResultLoader loader =
- mProvider.getInstalledAppSearchLoader(mActivity, query);
- assertThat(loader.mQuery).isEqualTo(query.trim());
+ mProvider.getInstalledAppSearchTask(mActivity, query);
+
+ verify(mProvider).cleanQuery(eq(query));
}
@Test(expected = IllegalArgumentException.class)
@@ -87,4 +91,12 @@
final ComponentName cn = new ComponentName(mActivity.getPackageName(), "class");
mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
}
+
+ @Test
+ public void cleanQuery_trimsWhitespace() {
+ final String query = " space ";
+ final String cleanQuery = "space";
+
+ assertThat(mProvider.cleanQuery(query)).isEqualTo(cleanQuery);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
index b897008..a3f3334 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
@@ -18,6 +18,7 @@
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -54,9 +55,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
@@ -65,7 +64,7 @@
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
-import java.util.Set;
+import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH,
@@ -79,22 +78,13 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
- private DatabaseResultLoader mDatabaseResultLoader;
- @Mock
- private InstalledAppResultLoader mInstalledAppResultLoader;
- @Mock
- private AccessibilityServiceResultLoader mAccessibilityServiceResultLoader;
- @Mock
- private InputDeviceResultLoader mInputDeviceResultLoader;
-
+ private SearchResultLoader mSearchResultLoader;
@Mock
private SavedQueryLoader mSavedQueryLoader;
@Mock
private SavedQueryController mSavedQueryController;
@Mock
private SearchResultsAdapter mSearchResultsAdapter;
- @Captor
- private ArgumentCaptor<String> mQueryCaptor = ArgumentCaptor.forClass(String.class);
private FakeFeatureFactory mFeatureFactory;
@@ -113,17 +103,8 @@
@Test
public void screenRotate_shouldPersistQuery() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(new MockSearchResultLoader(RuntimeEnvironment.application));
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -168,25 +149,16 @@
activityController.setup(bundle);
verify(mFeatureFactory.searchFeatureProvider, never())
- .getDatabaseSearchLoader(any(Context.class), anyString());
+ .getStaticSearchResultTask(any(Context.class), anyString());
verify(mFeatureFactory.searchFeatureProvider, never())
- .getInstalledAppSearchLoader(any(Context.class), anyString());
+ .getInstalledAppSearchTask(any(Context.class), anyString());
}
@Test
- public void queryTextChange_shouldTriggerLoaderAndInitializeSearch() {
+ public void queryTextChange_shouldTriggerLoader() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(mSearchResultLoader);
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -199,7 +171,6 @@
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
.thenReturn(true);
- ReflectionHelpers.setField(fragment, "mSearchAdapter", mSearchResultsAdapter);
fragment.onQueryTextChange(testQuery);
activityController.get().onBackPressed();
@@ -209,11 +180,7 @@
any(Context.class),
eq(MetricsProto.MetricsEvent.ACTION_LEAVE_SEARCH_RESULT_WITHOUT_QUERY));
verify(mFeatureFactory.searchFeatureProvider)
- .getDatabaseSearchLoader(any(Context.class), anyString());
- verify(mFeatureFactory.searchFeatureProvider)
- .getInstalledAppSearchLoader(any(Context.class), anyString());
- verify(mSearchResultsAdapter).initializeSearch(mQueryCaptor.capture());
- assertThat(mQueryCaptor.getValue()).isEqualTo(testQuery);
+ .getSearchResultLoader(any(Context.class), anyString());
}
@Test
@@ -238,18 +205,6 @@
@Test
public void queryTextChangeToEmpty_shouldLoadSavedQueryAndNotInitializeSearch() {
- when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
ActivityController<SearchActivity> activityController =
@@ -266,27 +221,14 @@
fragment.onQueryTextChange("");
verify(mFeatureFactory.searchFeatureProvider, never())
- .getDatabaseSearchLoader(any(Context.class), anyString());
+ .getStaticSearchResultTask(any(Context.class), anyString());
verify(mFeatureFactory.searchFeatureProvider, never())
- .getInstalledAppSearchLoader(any(Context.class), anyString());
+ .getInstalledAppSearchTask(any(Context.class), anyString());
verify(mSavedQueryController).loadSavedQueries();
- verify(mSearchResultsAdapter, never()).initializeSearch(anyString());
}
@Test
public void updateIndex_TriggerOnCreate() {
- when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -304,40 +246,10 @@
}
@Test
- public void syncLoaders_MergeWhenAllLoadersDone() {
- when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockDBLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
- .thenReturn(mSavedQueryLoader);
-
- ActivityController<SearchActivity> activityController =
- Robolectric.buildActivity(SearchActivity.class);
- activityController.setup();
-
- SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
- .findFragmentById(R.id.main_content));
- when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
- .thenReturn(true);
-
- fragment.onQueryTextChange("non-empty");
-
- Robolectric.flushForegroundThreadScheduler();
-
- verify(fragment, times(2)).onLoadFinished(any(Loader.class), any(Set.class));
- }
-
- @Test
public void whenNoQuery_HideFeedbackIsCalled() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockDBLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(new MockSearchResultLoader(RuntimeEnvironment.application));
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -359,17 +271,8 @@
@Test
public void onLoadFinished_ShowsFeedback() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockDBLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(new MockAccessibilityLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(new MockInputDeviceResultLoader(RuntimeEnvironment.application));
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(new MockSearchResultLoader(RuntimeEnvironment.application));
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
ActivityController<SearchActivity> activityController =
@@ -413,9 +316,7 @@
fragment.onIndexingFinished();
- verify(loaderManager).initLoader(eq(SearchFragment.SearchLoaderId.DATABASE),
- eq(null), any(LoaderManager.LoaderCallbacks.class));
- verify(loaderManager).initLoader(eq(SearchFragment.SearchLoaderId.INSTALLED_APPS),
+ verify(loaderManager).initLoader(eq(SearchFragment.SearchLoaderId.SEARCH_RESULT),
eq(null), any(LoaderManager.LoaderCallbacks.class));
}
@@ -480,16 +381,13 @@
eq("test_setting"),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_COUNT)),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_RANK)),
- argThat(pairMatches(MetricsProto.MetricsEvent
- .FIELD_SETTINGS_SEARCH_RESULT_ASYNC_RANKING_STATE)),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_QUERY_LENGTH)));
-
verify(mFeatureFactory.searchFeatureProvider).searchResultClicked(nullable(Context.class),
nullable(String.class), eq(searchResult));
}
@Test
- public void onResume_shouldCallSearchRankingWarmupIfSmartSearchRankingEnabled(){
+ public void onResume_shouldCallSearchRankingWarmupIfSmartSearchRankingEnabled() {
when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any(Context.class)))
.thenReturn(true);
@@ -504,7 +402,7 @@
}
@Test
- public void onResume_shouldNotCallSearchRankingWarmupIfSmartSearchRankingDisabled(){
+ public void onResume_shouldNotCallSearchRankingWarmupIfSmartSearchRankingDisabled() {
when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any(Context.class)))
.thenReturn(false);
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultAggregatorTest.java b/tests/robotests/src/com/android/settings/search/SearchResultAggregatorTest.java
new file mode 100644
index 0000000..286d7cf9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/SearchResultAggregatorTest.java
@@ -0,0 +1,271 @@
+package com.android.settings.search;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SearchResultAggregatorTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+
+ private SearchResultAggregator mAggregator;
+
+ @Mock
+ private DatabaseResultLoader mStaticTask;
+ @Mock
+ private InstalledAppResultLoader mAppTask;
+ @Mock
+ private InputDeviceResultLoader mInputTask;
+ @Mock
+ private AccessibilityServiceResultLoader mMAccessibilityTask;
+ @Mock
+ private ExecutorService mService;
+
+
+ private String[] DB_TITLES = {"static_one", "static_two"};
+ private String[] INPUT_TITLES = {"input_one", "input_two"};
+ private String[] ACCESS_TITLES = {"access_one", "access_two"};
+ private String[] APP_TITLES = {"app_one", "app_two"};
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mAggregator = spy(SearchResultAggregator.getInstance());
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+
+ // Return mock loaders from feature provider
+ when(mFeatureFactory.searchFeatureProvider.getStaticSearchResultTask(any(Context.class),
+ anyString())).thenReturn(mStaticTask);
+ when(mFeatureFactory.searchFeatureProvider.getInstalledAppSearchTask(any(Context.class),
+ anyString())).thenReturn(mAppTask);
+ when(mFeatureFactory.searchFeatureProvider.getInputDeviceResultTask(any(Context.class),
+ anyString())).thenReturn(mInputTask);
+ when(mFeatureFactory.searchFeatureProvider.getAccessibilityServiceResultTask(
+ any(Context.class),
+ anyString())).thenReturn(mMAccessibilityTask);
+ when(mFeatureFactory.searchFeatureProvider.getExecutorService()).thenReturn(mService);
+
+ // Return fake data from the loaders
+ List<? extends SearchResult> dbResults = getDummyDbResults();
+ doReturn(dbResults).when(mStaticTask).get(anyLong(), any(TimeUnit.class));
+
+ List<? extends SearchResult> appResults = getDummyAppResults();
+ doReturn(appResults).when(mAppTask).get(anyLong(), any(TimeUnit.class));
+
+ List<? extends SearchResult> inputResults = getDummyInputDeviceResults();
+ doReturn(inputResults).when(mInputTask).get(anyLong(), any(TimeUnit.class));
+
+ List<? extends SearchResult> accessResults = getDummyAccessibilityResults();
+ doReturn(accessResults).when(mMAccessibilityTask).get(anyLong(), any(TimeUnit.class));
+ }
+
+ @Test
+ public void testStaticResults_mergedProperly() {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(8);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(5).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(6).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(7).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_staticThrowsException_dbResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mStaticTask.get(anyLong(), any(TimeUnit.class))).thenThrow(new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(2).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(4).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_appsThrowException_appResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mAppTask.get(anyLong(), any(TimeUnit.class))).thenThrow(new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_inputThrowException_inputResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mInputTask.get(anyLong(), any(TimeUnit.class))).thenThrow(new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(ACCESS_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_accessThrowException_accessResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mMAccessibilityTask.get(anyLong(), any(TimeUnit.class))).thenThrow(
+ new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testDynamicRanking_sortsWithDynamicRanking() {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(
+ true);
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(8);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(5).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(6).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(7).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ private List<? extends SearchResult> getDummyDbResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ SearchResult.Builder builder = new SearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(DB_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(DB_TITLES[0], "db"));
+ results.add(builder.build());
+
+ builder.setTitle(DB_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(DB_TITLES[1], "db"));
+ results.add(builder.build());
+
+ return results;
+ }
+
+ private List<? extends SearchResult> getDummyAppResults() {
+ List<AppSearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(APP_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(APP_TITLES[0], "app"));
+ results.add(builder.build());
+
+ builder.setTitle(APP_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(APP_TITLES[1], "app"));
+ results.add(builder.build());
+
+ return results;
+ }
+
+ public List<? extends SearchResult> getDummyInputDeviceResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(INPUT_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(INPUT_TITLES[0], "app"));
+ results.add(builder.build());
+
+ builder.setTitle(INPUT_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(INPUT_TITLES[1], "app"));
+ results.add(builder.build());
+
+ return results;
+ }
+
+ public List<? extends SearchResult> getDummyAccessibilityResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(ACCESS_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(ACCESS_TITLES[0], "app"));
+ results.add(builder.build());
+
+ builder.setTitle(ACCESS_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(ACCESS_TITLES[1], "app"));
+ results.add(builder.build());
+
+ return results;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
index a2afb35..4baf8d2 100644
--- a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
@@ -18,44 +18,32 @@
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.util.Pair;
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.settings.R;
import com.android.settings.TestConfig;
-import com.android.settings.search.SearchResult.Builder;
-import com.android.settings.search.ranking.SearchResultsRankerCallback;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowLooper;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -67,25 +55,18 @@
private SearchFeatureProvider mSearchFeatureProvider;
@Mock
private Context mMockContext;
- @Captor
- private ArgumentCaptor<Integer> mSearchResultsCountCaptor =
- ArgumentCaptor.forClass(Integer.class);
private SearchResultsAdapter mAdapter;
private Context mContext;
- private String mLoaderClassName;
-
- private String[] TITLES = {"alpha", "bravo", "charlie", "appAlpha", "appBravo", "appCharlie"};
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Robolectric.buildActivity(Activity.class).get();
- mLoaderClassName = DatabaseResultLoader.class.getName();
when(mFragment.getContext()).thenReturn(mMockContext);
when(mMockContext.getApplicationContext()).thenReturn(mContext);
when(mSearchFeatureProvider.smartSearchRankingTimeoutMs(any(Context.class)))
.thenReturn(300L);
- mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider);
+ mAdapter = new SearchResultsAdapter(mFragment);
}
@Test
@@ -95,17 +76,6 @@
}
@Test
- public void testSingleSourceMerge_exactCopyReturned() {
- Set<SearchResult> intentResults = getIntentSampleResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(intentResults, mLoaderClassName);
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> updatedResults = mAdapter.getSearchResults();
- assertThat(updatedResults).containsAllIn(intentResults);
- }
-
- @Test
public void testCreateViewHolder_returnsIntentResult() {
ViewGroup group = new FrameLayout(mContext);
SearchViewHolder view = mAdapter.onCreateViewHolder(group,
@@ -123,387 +93,13 @@
}
@Test
- public void testEndToEndSearch_properResultsMerged_correctOrder() {
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(new HashSet<>(getDummyAppResults()),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(getDummyDbResults()),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
+ public void testPostSearchResults_addsDataAndDisplays() {
+ List<SearchResult> results = getDummyDbResults();
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- }
+ mAdapter.postSearchResults(results);
- @Test
- public void testEndToEndSearch_addResults_resultsAddedInOrder() {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- // Add two individual items
- mAdapter.addSearchResults(new HashSet<>(appResults.subList(0, 1)),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(dbResults.subList(0, 1)),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- // Add super-set of items
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment, times(2)).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getAllValues().toArray())
- .isEqualTo(new Integer[] {2, 6});
- }
-
- @Test
- public void testEndToEndSearch_removeResults_resultsAdded() {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- // Add list of items
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(new HashSet<>(appResults),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(dbResults),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- // Add subset of items
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(new HashSet<>(appResults.subList(0, 1)),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(dbResults.subList(0, 1)),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]);
- assertThat(results.get(1).title).isEqualTo(TITLES[3]);
- verify(mFragment, times(2)).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getAllValues().toArray())
- .isEqualTo(new Integer[] {6, 2});
- }
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndSucceededAfterResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.SUCCEEDED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndSucceededBeforeResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.SUCCEEDED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndFailedAfterResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- mAdapter.onRankingFailed();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.FAILED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndFailedBeforeResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.onRankingFailed();
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.FAILED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndTimedoutAfterResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- waitUntilRankingTimesOut();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.TIMED_OUT);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndTimedoutBeforeResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
-
- waitUntilRankingTimesOut();
-
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.TIMED_OUT);
- }
-
- @Test
- public void testDoSmartRanking_shouldRankAppResultsAfterDbResults() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
- List<SearchResult> results = mAdapter.doAsyncRanking();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- }
-
- @Test
- public void testDoSmartRanking_shouldRankResultsWithMissingScoresAfterScoredResults() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- List<Pair<String, Float>> rankingScores = getDummyRankingScores();
- rankingScores.remove(1); // no ranking score for alpha
- mAdapter.onRankingScoresAvailable(rankingScores);
- List<SearchResult> results = mAdapter.doAsyncRanking();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(2).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- }
-
- @Test
- public void testGetUnsortedLoadedResults () {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- Set<CharSequence> expectedDbTitles = new HashSet<>(
- Arrays.asList("alpha", "bravo", "charlie"));
- Set<CharSequence> expectedAppTitles = new HashSet<>(
- Arrays.asList("appAlpha", "appBravo", "appCharlie"));
- Set<CharSequence> actualDbTitles = new HashSet<>();
- Set<CharSequence> actualAppTitles = new HashSet<>();
- for (SearchResult result : mAdapter.getUnsortedLoadedResults(SearchResultsAdapter
- .DB_RESULTS_LOADER_KEY)) {
- actualDbTitles.add(result.title);
- }
- for (SearchResult result : mAdapter.getUnsortedLoadedResults(SearchResultsAdapter
- .APP_RESULTS_LOADER_KEY)) {
- actualAppTitles.add(result.title);
- }
- assertThat(actualDbTitles).isEqualTo(expectedDbTitles);
- assertThat(actualAppTitles).isEqualTo(expectedAppTitles);
- }
-
- @Test
- public void testGetSortedLoadedResults() {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- List<? extends SearchResult> actualDbResults =
- mAdapter.getSortedLoadedResults(SearchResultsAdapter.DB_RESULTS_LOADER_KEY);
- List<? extends SearchResult> actualAppResults =
- mAdapter.getSortedLoadedResults(SearchResultsAdapter.APP_RESULTS_LOADER_KEY);
- assertThat(actualDbResults.get(0).title).isEqualTo(TITLES[0]); // charlie
- assertThat(actualDbResults.get(1).title).isEqualTo(TITLES[1]); // bravo
- assertThat(actualDbResults.get(2).title).isEqualTo(TITLES[2]); // alpha
- assertThat(actualAppResults.get(0).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(actualAppResults.get(1).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(actualAppResults.get(2).title).isEqualTo(TITLES[5]); // appCharlie
- }
-
- @Test
- public void testInitializeSearch_shouldNotRunSmartRankingIfDisabled() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(false);
- mAdapter.initializeSearch("");
- mAdapter.notifyResultsLoaded();
- verify(mSearchFeatureProvider, never()).querySearchResults(
- any(Context.class), anyString(), any(SearchResultsRankerCallback.class));
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.DISABLED);
- }
-
- @Test
- public void testInitialSearch_shouldRunSmartRankingIfEnabled() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
- mAdapter.initializeSearch("");
- mAdapter.notifyResultsLoaded();
- verify(mSearchFeatureProvider, times(1)).querySearchResults(
- any(Context.class), anyString(), any(SearchResultsRankerCallback.class));
- assertThat(mAdapter.getAsyncRankingState())
- .isEqualTo(SearchResultsAdapter.PENDING_RESULTS);
- }
-
- @Test
- public void testGetRankingScoreByStableId() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
- assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(0).stableId))
- .isWithin(1e-10f).of(0.8f);
- assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(1).stableId))
- .isWithin(1e-10f).of(0.2f);
- assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(2).stableId))
- .isWithin(1e-10f).of(0.9f);
- assertThat(mAdapter.getRankingScoreByStableId(appResults.get(0).stableId))
- .isEqualTo(-Float.MAX_VALUE);
- assertThat(mAdapter.getRankingScoreByStableId(appResults.get(1).stableId))
- .isEqualTo(-Float.MAX_VALUE);
- assertThat(mAdapter.getRankingScoreByStableId(appResults.get(2).stableId))
- .isEqualTo(-Float.MAX_VALUE);
- }
-
- private void waitUntilRankingTimesOut() {
- while (mAdapter.getHandler().hasMessages(mAdapter.MSG_RANKING_TIMED_OUT)) {
- try {
- ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // Do nothing
- }
- }
+ assertThat(mAdapter.getSearchResults()).containsExactlyElementsIn(results);
+ verify(mFragment).onSearchResultsDisplayed(anyInt());
}
private List<SearchResult> getDummyDbResults() {
@@ -511,78 +107,21 @@
ResultPayload payload = new ResultPayload(new Intent());
SearchResult.Builder builder = new SearchResult.Builder();
builder.setPayload(payload)
- .setTitle(TITLES[0])
+ .setTitle("one")
.setRank(1)
- .setStableId(Objects.hash(TITLES[0], "db"));
+ .setStableId(Objects.hash("one", "db"));
results.add(builder.build());
- builder.setTitle(TITLES[1])
+ builder.setTitle("two")
.setRank(3)
- .setStableId(Objects.hash(TITLES[1], "db"));
+ .setStableId(Objects.hash("two", "db"));
results.add(builder.build());
- builder.setTitle(TITLES[2])
+ builder.setTitle("three")
.setRank(6)
- .setStableId(Objects.hash(TITLES[2], "db"));
+ .setStableId(Objects.hash("three", "db"));
results.add(builder.build());
return results;
}
-
- private List<SearchResult> getDummyAppResults() {
- List<SearchResult> results = new ArrayList<>();
- ResultPayload payload = new ResultPayload(new Intent());
- AppSearchResult.Builder builder = new AppSearchResult.Builder();
- builder.setPayload(payload)
- .setTitle(TITLES[3])
- .setRank(1)
- .setStableId(Objects.hash(TITLES[3], "app"));
- results.add(builder.build());
-
- builder.setTitle(TITLES[4])
- .setRank(2)
- .setStableId(Objects.hash(TITLES[4], "app"));
- results.add(builder.build());
-
- builder.setTitle(TITLES[5])
- .setRank(4)
- .setStableId(Objects.hash(TITLES[5], "app"));
- results.add(builder.build());
-
- return results;
- }
-
- private Set<SearchResult> getIntentSampleResults() {
- Set<SearchResult> sampleResults = new HashSet<>();
- ArrayList<String> breadcrumbs = new ArrayList<>();
- final Drawable icon = mContext.getDrawable(R.drawable.ic_search_24dp);
- final ResultPayload payload = new ResultPayload(null);
- final SearchResult.Builder builder = new Builder();
- builder.setTitle("title")
- .setSummary("summary")
- .setRank(1)
- .addBreadcrumbs(breadcrumbs)
- .setIcon(icon)
- .setPayload(payload)
- .setStableId(Objects.hash("title", "summary", 1));
- sampleResults.add(builder.build());
-
- builder.setRank(2)
- .setStableId(Objects.hash("title", "summary", 2));
- sampleResults.add(builder.build());
-
- builder.setRank(3)
- .setStableId(Objects.hash("title", "summary", 3));
- sampleResults.add(builder.build());
- return sampleResults;
- }
-
- private List<Pair<String, Float>> getDummyRankingScores() {
- List<SearchResult> results = getDummyDbResults();
- List<Pair<String, Float>> scores = new ArrayList<>();
- scores.add(new Pair<>(Long.toString(results.get(2).stableId), 0.9f)); // charlie
- scores.add(new Pair<>(Long.toString(results.get(0).stableId), 0.8f)); // alpha
- scores.add(new Pair<>(Long.toString(results.get(1).stableId), 0.2f)); // bravo
- return scores;
- }
}
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/StaticSearchResultFutureTaskTest.java
similarity index 61%
rename from tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
rename to tests/robotests/src/com/android/settings/search/StaticSearchResultFutureTaskTest.java
index dd7b7a9..e285555 100644
--- a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/StaticSearchResultFutureTaskTest.java
@@ -21,9 +21,11 @@
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
+import android.util.Pair;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.SiteMapManager;
+import com.android.settings.search.DatabaseResultLoader.StaticSearchResultCallable;
import com.android.settings.search.indexing.IndexData;
import com.android.settings.testutils.DatabaseTestUtils;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -39,37 +41,54 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class DatabaseResultLoaderTest {
+public class StaticSearchResultFutureTaskTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mMockContext;
@Mock
private SiteMapManager mSiteMapManager;
+ @Mock
+ private ExecutorService mService;
private Context mContext;
SQLiteDatabase mDb;
+ FakeFeatureFactory mFeatureFactory;
+
+ private final String[] STATIC_TITLES = {"static one", "static two", "static three"};
+ private final int[] STABLE_IDS =
+ {"id_one".hashCode(), "id_two".hashCode(), "id_three".hashCode()};
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- FakeFeatureFactory.setupForTest(mMockContext);
- FakeFeatureFactory factory =
- (FakeFeatureFactory) FakeFeatureFactory.getFactory(mMockContext);
- when(factory.searchFeatureProvider.getSiteMapManager())
+ mFeatureFactory = FakeFeatureFactory.setupForTest(mMockContext);
+ when(mFeatureFactory.searchFeatureProvider.getExecutorService()).thenReturn(mService);
+ when(mFeatureFactory.searchFeatureProvider.getSiteMapManager())
.thenReturn(mSiteMapManager);
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
setUpDb();
@@ -81,159 +100,252 @@
}
@Test
- public void testMatchTitle() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "title", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+ public void testMatchTitle() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "title",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(2);
verify(mSiteMapManager, times(2)).buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void testMatchSummary() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "summary",
+ public void testMatchSummary() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "summary",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+
+ assertThat(loader.call()).hasSize(2);
}
@Test
- public void testMatchKeywords() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "keywords",
+ public void testMatchKeywords() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "keywords",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+
+ assertThat(loader.call()).hasSize(2);
}
@Test
- public void testMatchEntries() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "entries",
+ public void testMatchEntries() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "entries",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+
+ assertThat(loader.call()).hasSize(2);
}
@Test
- public void testSpecialCaseWord_matchesNonPrefix() {
+ public void testSpecialCaseWord_matchesNonPrefix() throws Exception {
insertSpecialCase("Data usage");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "usage", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "usage",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseDash_matchesWordNoDash() {
+ public void testSpecialCaseDash_matchesWordNoDash() throws Exception {
insertSpecialCase("wi-fi calling");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "wifi", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
- }
- @Test
- public void testSpecialCaseDash_matchesWordWithDash() {
- insertSpecialCase("priorités seulment");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "priorités",
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "wifi",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseDash_matchesWordWithoutDash() {
+ public void testSpecialCaseDash_matchesWordWithDash() throws Exception {
insertSpecialCase("priorités seulment");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "priorites",
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "priorités",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseDash_matchesEntireQueryWithoutDash() {
+ public void testSpecialCaseDash_matchesWordWithoutDash() throws Exception {
+ insertSpecialCase("priorités seulment");
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "priorites",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
+ }
+
+ @Test
+ public void testSpecialCaseDash_matchesEntireQueryWithoutDash() throws Exception {
insertSpecialCase("wi-fi calling");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "wifi calling",
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "wifi calling",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCasePrefix_matchesPrefixOfEntry() {
+ public void testSpecialCasePrefix_matchesPrefixOfEntry() throws Exception {
insertSpecialCase("Photos");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "pho", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "pho",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCasePrefix_DoesNotMatchNonPrefixSubstring() {
+ public void testSpecialCasePrefix_DoesNotMatchNonPrefixSubstring() throws Exception {
insertSpecialCase("Photos");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "hot", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(0);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "hot",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(0);
}
@Test
- public void testSpecialCaseMultiWordPrefix_matchesPrefixOfEntry() {
+ public void testSpecialCaseMultiWordPrefix_matchesPrefixOfEntry() throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "Apps", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "Apps",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseMultiWordPrefix_matchesSecondWordPrefixOfEntry() {
+ public void testSpecialCaseMultiWordPrefix_matchesSecondWordPrefixOfEntry() throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "Not", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "Not",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfFirstEntry() {
+ public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfFirstEntry()
+ throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "pp", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(0);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "pp",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(0);
}
@Test
- public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfSecondEntry() {
+ public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfSecondEntry()
+ throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "tion", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(0);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "tion",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(0);
}
@Test
- public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfEntry() {
+ public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfEntry() throws
+ Exception {
insertSpecialCase("Apps & Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "App", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "App",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfSecondEntry() {
+ public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfSecondEntry()
+ throws Exception {
insertSpecialCase("Apps & Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "No", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "No",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testResultMatchedByMultipleQueries_duplicatesRemoved() {
+ public void testResultMatchedByMultipleQueries_duplicatesRemoved() throws Exception {
String key = "durr";
insertSameValueAllFieldsCase(key);
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, key, null);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, key, null);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseTwoWords_multipleResults() {
+ public void testSpecialCaseTwoWords_multipleResults() throws Exception {
final String caseOne = "Apple pear";
final String caseTwo = "Banana apple";
insertSpecialCase(caseOne);
insertSpecialCase(caseTwo);
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "App", null);
- Set<? extends SearchResult> results = loader.loadInBackground();
- Set<CharSequence> expectedTitles = new HashSet<>(Arrays.asList(caseOne, caseTwo));
- Set<CharSequence> actualTitles = new HashSet<>();
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "App", null);
+
+ List<? extends SearchResult> results = loader.call();
+
+ Set<String> actualTitles = new HashSet<>();
for (SearchResult result : results) {
- actualTitles.add(result.title);
+ actualTitles.add(result.title.toString());
}
- assertThat(actualTitles).isEqualTo(expectedTitles);
+ assertThat(actualTitles).containsAllOf(caseOne, caseTwo);
+ }
+
+ @Test
+ public void testGetRankingScoreByStableId_sortedDynamically() throws Exception {
+ FutureTask<List<Pair<String, Float>>> task = mock(FutureTask.class);
+ when(task.get(anyLong(), any(TimeUnit.class))).thenReturn(getDummyRankingScores());
+ when(mFeatureFactory.searchFeatureProvider.getRankerTask(any(Context.class),
+ anyString())).thenReturn(task);
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(
+ true);
+
+ insertSpecialCase(STATIC_TITLES[0], STABLE_IDS[0]);
+ insertSpecialCase(STATIC_TITLES[1], STABLE_IDS[1]);
+ insertSpecialCase(STATIC_TITLES[2], STABLE_IDS[2]);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "Static",
+ null);
+
+ List<? extends SearchResult> results = loader.call();
+
+ assertThat(results.get(0).title).isEqualTo(STATIC_TITLES[2]);
+ assertThat(results.get(1).title).isEqualTo(STATIC_TITLES[0]);
+ assertThat(results.get(2).title).isEqualTo(STATIC_TITLES[1]);
+ }
+
+ @Test
+ public void testGetRankingScoreByStableId_scoresTimeout_sortedStatically() throws Exception {
+ Callable<List<Pair<String, Float>>> callable = mock(Callable.class);
+ when(callable.call()).thenThrow(new TimeoutException());
+ FutureTask<List<Pair<String, Float>>> task = new FutureTask<>(callable);
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(
+ true);
+ when(mFeatureFactory.searchFeatureProvider.getRankerTask(any(Context.class),
+ anyString())).thenReturn(task);
+ insertSpecialCase("title", STABLE_IDS[0]);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "title", null);
+
+ List<? extends SearchResult> results = loader.call();
+ assertThat(results.get(0).title).isEqualTo("title");
+ assertThat(results.get(1).title).isEqualTo("alpha_title");
+ assertThat(results.get(2).title).isEqualTo("bravo_title");
}
private void insertSpecialCase(String specialCase) {
+ insertSpecialCase(specialCase, specialCase.hashCode());
+ }
+
+ private void insertSpecialCase(String specialCase, int docId) {
String normalized = IndexData.normalizeHyphen(specialCase);
normalized = IndexData.normalizeString(normalized);
final ResultPayload payload = new ResultPayload(new Intent());
ContentValues values = new ContentValues();
- values.put(IndexDatabaseHelper.IndexColumns.DOCID, normalized.hashCode());
+ values.put(IndexDatabaseHelper.IndexColumns.DOCID, docId);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, specialCase);
@@ -373,4 +485,33 @@
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
}
+
+ private List<? extends SearchResult> getDummyDbResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ SearchResult.Builder builder = new SearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(STATIC_TITLES[0])
+ .setStableId(STABLE_IDS[0]);
+ results.add(builder.build());
+
+ builder.setTitle(STATIC_TITLES[1])
+ .setStableId(STABLE_IDS[1]);
+ results.add(builder.build());
+
+ builder.setTitle(STATIC_TITLES[2])
+ .setStableId(STABLE_IDS[2]);
+ results.add(builder.build());
+
+ return results;
+ }
+
+ private List<Pair<String, Float>> getDummyRankingScores() {
+ List<? extends SearchResult> results = getDummyDbResults();
+ List<Pair<String, Float>> scores = new ArrayList<>();
+ scores.add(new Pair<>(Long.toString(results.get(2).stableId), 0.9f)); // static_three
+ scores.add(new Pair<>(Long.toString(results.get(0).stableId), 0.8f)); // static_one
+ scores.add(new Pair<>(Long.toString(results.get(1).stableId), 0.2f)); // static_two
+ return scores;
+ }
}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/EncryptionAndCredentialTest.java b/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java
similarity index 95%
rename from tests/robotests/src/com/android/settings/EncryptionAndCredentialTest.java
rename to tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java
index 7a3875e..c87c964 100644
--- a/tests/robotests/src/com/android/settings/EncryptionAndCredentialTest.java
+++ b/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java
@@ -11,14 +11,14 @@
* 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
+ * limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.security;
import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
-import static com.android.settings.EncryptionAndCredential.SEARCH_INDEX_DATA_PROVIDER;
+import static com.android.settings.security.EncryptionAndCredential.SEARCH_INDEX_DATA_PROVIDER;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -28,6 +28,8 @@
import android.provider.SearchIndexableResource;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.TestConfig;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowVibrator.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowVibrator.java
new file mode 100644
index 0000000..9046720
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowVibrator.java
@@ -0,0 +1,56 @@
+/*
+ * 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.testutils.shadow;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.os.SystemVibrator;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.fakes.RoboVibrator;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.Map;
+
+@Implements(SystemVibrator.class)
+public class ShadowVibrator {
+
+ private static Map<String, String> getSystemServiceMap() {
+ return ReflectionHelpers.getStaticField(ShadowContextImpl.class, "SYSTEM_SERVICE_MAP");
+ }
+
+ public static void addToServiceMap() {
+ getSystemServiceMap().put(Context.VIBRATOR_SERVICE, SystemVibrator.class.getName());
+ }
+
+ public static void reset() {
+ getSystemServiceMap().put(Context.VIBRATOR_SERVICE, RoboVibrator.class.getName());
+ }
+
+ public final Vibrator delegate = mock(Vibrator.class);
+
+ @Implementation
+ public void vibrate(int uid, String opPkg, VibrationEffect vibe, AudioAttributes attributes) {
+ delegate.vibrate(uid, opPkg, vibe, attributes);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
new file mode 100644
index 0000000..d489094
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceViewHolder;
+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.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AppPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private AppPreference mPref;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.preference_app, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPref = new AppPreference(mContext);
+ }
+
+ @Test
+ public void setProgress_showProgress() {
+ mPref.setProgress(1);
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(android.R.id.progress).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void setSummary_showSummaryContainer() {
+ mPref.setSummary("test");
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void noSummary_hideSummaryContainer() {
+ mPref.setSummary(null);
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/widget/AppSwitchPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/AppSwitchPreferenceTest.java
new file mode 100644
index 0000000..a7c8d7c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/AppSwitchPreferenceTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceViewHolder;
+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.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AppSwitchPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private AppSwitchPreference mPref;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.preference_app, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPref = new AppSwitchPreference(mContext);
+ }
+
+ @Test
+ public void setSummary_showSummaryContainer() {
+ mPref.setSummary("test");
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void noSummary_hideSummaryContainer() {
+ mPref.setSummary(null);
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java b/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java
index e314ef3..40d73eb 100644
--- a/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/widget/RadioButtonPickerFragmentTest.java
@@ -16,15 +16,21 @@
package com.android.settings.widget;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.app.Activity;
import android.content.Context;
import android.os.UserManager;
import android.support.v7.preference.PreferenceScreen;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -38,12 +44,6 @@
import java.util.ArrayList;
import java.util.List;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class RadioButtonPickerFragmentTest {
@@ -99,6 +99,11 @@
assertThat(mFragment.setDefaultKeyCalled).isTrue();
}
+ @Test
+ public void shouldHaveNoCustomPreferenceLayout() {
+ assertThat(mFragment.getRadioButtonPreferenceCustomLayoutResId()).isEqualTo(0);
+ }
+
public static class TestFragment extends RadioButtonPickerFragment {
boolean setDefaultKeyCalled;
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index ec0f0b6..58fe7dd 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -10,13 +10,13 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
espresso-core \
- legacy-android-test \
- mockito-target-minus-junit4 \
- truth-prebuilt \
- ub-uiautomator \
espresso-contrib-nodep \
espresso-intents-nodep \
-
+ legacy-android-test \
+ mockito-target-minus-junit4 \
+ platform-test-annotations \
+ truth-prebuilt \
+ ub-uiautomator
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
new file mode 100644
index 0000000..c75ca13
--- /dev/null
+++ b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.core;
+
+import static junit.framework.Assert.fail;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.provider.SearchIndexableResource;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+
+import com.android.settings.search.DatabaseIndexingUtils;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableResources;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class PreferenceControllerContractTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ @Presubmit
+ public void controllersInSearchShouldImplementPreferenceControllerMixin() {
+ final Set<String> errorClasses = new ArraySet<>();
+
+ for (SearchIndexableResource page : SearchIndexableResources.values()) {
+ final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(page.className);
+
+ final Indexable.SearchIndexProvider provider =
+ DatabaseIndexingUtils.getSearchIndexProvider(clazz);
+ if (provider == null) {
+ continue;
+ }
+
+ final List<AbstractPreferenceController> controllers =
+ provider.getPreferenceControllers(mContext);
+ if (controllers == null) {
+ continue;
+ }
+ for (AbstractPreferenceController controller : controllers) {
+ if (!(controller instanceof PreferenceControllerMixin)) {
+ errorClasses.add(controller.getClass().getName());
+ }
+ }
+ }
+
+ if (!errorClasses.isEmpty()) {
+ final StringBuilder errorMessage = new StringBuilder()
+ .append("Each preference must implement PreferenceControllerMixin, ")
+ .append("the following classes don't:\n");
+ for (String c : errorClasses) {
+ errorMessage.append(c).append("\n");
+ }
+ fail(errorMessage.toString());
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/UniquePreferenceTest.java b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
similarity index 98%
rename from tests/unit/src/com/android/settings/UniquePreferenceTest.java
rename to tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
index 74f46eb..4c51772 100644
--- a/tests/unit/src/com/android/settings/UniquePreferenceTest.java
+++ b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.core;
import static junit.framework.Assert.fail;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.platform.test.annotations.Presubmit;
import android.provider.SearchIndexableResource;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
@@ -83,6 +84,7 @@
* should have a key.
*/
@Test
+ @Presubmit
public void allPreferencesShouldHaveUniqueKey()
throws IOException, XmlPullParserException, Resources.NotFoundException {
final Set<String> uniqueKeys = new HashSet<>();