Merge "[Fingerprint] Make enroll progress bar scalable"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 966e075..c987ec0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3069,6 +3069,18 @@
android:value="com.android.settings.datausage.DataUsageSummary"/>
</activity-alias>
+ <activity android:name=".Settings$AppAndNotificationDashboardActivity"
+ android:label="@string/app_and_notification_dashboard_title"
+ android:icon="@drawable/ic_settings_applications">
+ <intent-filter android:priority="8">
+ <action android:name="com.android.settings.action.SETTINGS"/>
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.applications.AppAndNotificationDashboardFragment"/>
+ <meta-data android:name="com.android.settings.category"
+ android:value="com.android.settings.category.ia.homepage"/>
+ </activity>
+
<activity-alias android:name="BatteryDashboardAlias"
android:targetActivity="Settings$PowerUsageSummaryActivity">
<intent-filter android:priority="7">
@@ -3315,6 +3327,43 @@
android:value="com.android.settings.category.ia.device" />
</activity-alias>
+ <activity-alias android:name="ManageApplicationsDashboardAlias"
+ android:targetActivity="Settings$ManageApplicationsActivity">
+ <intent-filter android:priority="200">
+ <action android:name="com.android.settings.action.SETTINGS" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.category"
+ android:value="com.android.settings.category.ia.apps" />
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.applications.ManageApplications" />
+ <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+ android:value="true" />
+ </activity-alias>
+
+ <activity-alias android:name="AppNotificationDashboardAlias"
+ android:targetActivity="Settings$NotificationAppListActivity">
+ <intent-filter android:priority="150">
+ <action android:name="com.android.settings.action.SETTINGS"/>
+ </intent-filter>
+ <meta-data android:name="com.android.settings.category"
+ android:value="com.android.settings.category.ia.apps"/>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.applications.NotificationApps"/>
+ </activity-alias>
+
+ <activity-alias android:name="ConfigureNotificationDashboardAlias"
+ android:targetActivity="Settings$ConfigureNotificationSettingsActivity">
+ <intent-filter android:priority="120">
+ <action android:name="com.android.settings.action.SETTINGS"/>
+ </intent-filter>
+ <meta-data android:name="com.android.settings.category"
+ android:value="com.android.settings.category.ia.apps"/>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.notification.ConfigureNotificationSettings" />
+ <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+ android:value="true" />
+ </activity-alias>
+
<!-- End of information architecture host activities -->
<service
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..653380c
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,4 @@
+[Hook Scripts]
+checkstyle_hook = ../../../development/tools/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+ -fw src/com/android/settings/print/
+
diff --git a/res/layout/battery_history_detail.xml b/res/layout/battery_history_detail.xml
index e254711..b782e39 100644
--- a/res/layout/battery_history_detail.xml
+++ b/res/layout/battery_history_detail.xml
@@ -14,54 +14,56 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView
+ 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:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:orientation="vertical">
+ android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:focusable="true"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical">
- <TextView
- android:id="@+id/charge"
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textSize="36sp"
- android:textColor="?android:attr/colorAccent" />
+ android:focusable="true"
+ android:orientation="vertical">
- <TextView
- android:id="@+id/estimation"
+ <TextView
+ android:id="@+id/charge"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="36sp"
+ android:textColor="?android:attr/colorAccent"/>
+
+ <TextView
+ android:id="@+id/estimation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <com.android.settingslib.graph.UsageView
+ android:id="@+id/battery_usage"
+ android:layout_width="match_parent"
+ android:layout_height="141dp"
+ settings:sideLabels="@array/battery_labels"
+ android:colorAccent="?android:attr/colorAccent"
+ android:gravity="end"
+ settings:textColor="?android:attr/textColorSecondary"/>
+ </LinearLayout>
+
+ <View
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorSecondary" />
-
- <com.android.settingslib.graph.UsageView
- android:id="@+id/battery_usage"
- android:layout_width="match_parent"
- android:layout_height="141dp"
- settings:sideLabels="@array/battery_labels"
- android:colorAccent="?android:attr/colorAccent"
- android:gravity="end"
- settings:textColor="?android:attr/textColorSecondary" />
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginTop="2dp"
- android:background="?android:attr/listDivider" />
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="1dp"
+ android:layout_marginTop="2dp"
+ android:background="?android:attr/listDivider"/>
<LinearLayout
android:layout_width="match_parent"
@@ -94,6 +96,6 @@
</LinearLayout>
- </ScrollView>
+ </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/res/layout/see_all.xml b/res/layout/see_all.xml
deleted file mode 100644
index 44b263c..0000000
--- a/res/layout/see_all.xml
+++ /dev/null
@@ -1,65 +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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical"
- android:minHeight="@dimen/dashboard_tile_minimum_height"
- android:clickable="true"
- android:focusable="true">
-
- <View
- android:layout_width="@dimen/dashboard_tile_image_size"
- android:layout_height="@dimen/dashboard_tile_image_size"
- android:layout_marginStart="@dimen/dashboard_tile_image_margin_start"
- android:layout_marginEnd="@dimen/dashboard_tile_image_margin_end"
- android:visibility="invisible" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:orientation="vertical"
- android:gravity="center_vertical"
- android:layout_weight="1">
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <TextView android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@android:style/TextAppearance.Material.Widget.Button.Inverse"
- android:textColor="?android:attr/colorAccent"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
-
- </RelativeLayout>
-
- </LinearLayout>
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/support_tile.xml b/res/layout/support_tile.xml
index e5e49f4..e294868 100644
--- a/res/layout/support_tile.xml
+++ b/res/layout/support_tile.xml
@@ -34,7 +34,7 @@
android:id="@+id/tile_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:singleLine="true"
+ android:maxLines="2"
android:textAppearance="@style/TextAppearance.TileTitle"
android:ellipsize="end"
android:fadingEdge="horizontal"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8e6336f..8d6e5ff 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5929,6 +5929,8 @@
<string name="network_dashboard_title">Network & Internet</string>
<!-- Title for setting tile leading to Connected devices settings [CHAR LIMIT=40]-->
<string name="connected_devices_dashboard_title">Connected devices</string>
+ <!-- Title for setting tile leading to Apps & Notification settings [CHAR LIMIT=40]-->
+ <string name="app_and_notification_dashboard_title">Apps & notifications</string>
<!-- Summary text for system preference tile, showing current display language of device [CHAR LIMIT=NONE]-->
<string name="system_dashboard_summary">Language: <xliff:g id="language">%1$s</xliff:g></string>
@@ -7281,11 +7283,6 @@
<!-- Conversation message timestamp of the messaging app preview screen. [CHAR LIMIT=20] -->
<string name="screen_zoom_conversation_timestamp_4">Tue 6:03PM</string>
- <!-- Button to show all top-level settings items [CHAR LIMIT=20] -->
- <string name="see_all">See all</string>
- <!-- Button to show less top-level settings items [CHAR LIMIT=20] -->
- <string name="see_less">See less</string>
-
<!-- Wi-Fi state - Disconnected [CHAR LIMIT=NONE] -->
<string name="disconnected">Disconnected</string>
diff --git a/res/xml/advanced_apps.xml b/res/xml/advanced_apps.xml
index 405d5af..7b1af10 100644
--- a/res/xml/advanced_apps.xml
+++ b/res/xml/advanced_apps.xml
@@ -19,10 +19,12 @@
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:key="applications_settings">
- <PreferenceScreen
+ <Preference
android:key="manage_perms"
android:title="@string/app_permissions"
- settings:keywords="@string/keywords_app_permissions" />
+ settings:keywords="@string/keywords_app_permissions">
+ <intent android:action="android.intent.action.MANAGE_PERMISSIONS"/>
+ </Preference>
<PreferenceCategory
android:title="@string/default_apps_title">
diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml
new file mode 100644
index 0000000..4aa109e
--- /dev/null
+++ b/res/xml/app_and_notification.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+ <Preference
+ android:key="manage_perms"
+ android:title="@string/app_permissions"
+ android:order="-130"
+ settings:keywords="@string/keywords_app_permissions">
+ <intent android:action="android.intent.action.MANAGE_PERMISSIONS"/>
+ </Preference>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java
index 32aaaf0..b9bc439 100644
--- a/src/com/android/settings/DevelopmentSettings.java
+++ b/src/com/android/settings/DevelopmentSettings.java
@@ -1958,7 +1958,8 @@
Settings.Global.putInt(getActivity().getContentResolver(),
Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
mKeepScreenOn.isChecked() ?
- (BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB) : 0);
+ (BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB
+ | BatteryManager.BATTERY_PLUGGED_WIRELESS) : 0);
} else if (preference == mBtHciSnoopLog) {
writeBtHciSnoopLogOptions();
} else if (preference == mEnableOemUnlock && mEnableOemUnlock.isEnabled()) {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 91db7fa..bfecbbf 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -166,6 +166,7 @@
// Top level categories for new IA
public static class NetworkDashboardActivity extends SettingsActivity {}
public static class ConnectedDeviceDashboardActivity extends SettingsActivity {}
+ public static class AppAndNotificationDashboardActivity extends SettingsActivity {}
public static class StorageDashboardActivity extends SettingsActivity {}
public static class SystemDashboardActivity extends SettingsActivity {}
public static class SupportDashboardActivity extends SettingsActivity {}
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index b434777..11f8105 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -61,6 +61,7 @@
import com.android.settings.accounts.AccountSyncSettings;
import com.android.settings.accounts.ChooseAccountActivity;
import com.android.settings.accounts.ManagedProfileSettings;
+import com.android.settings.applications.AppAndNotificationDashboardFragment;
import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.applications.DrawOverlayDetails;
import com.android.settings.applications.InstalledAppDetails;
@@ -272,23 +273,24 @@
// New IA
// Home page
Settings.NetworkDashboardActivity.class.getName(),
+ Settings.ConnectedDeviceDashboardActivity.class.getName(),
+ Settings.AppAndNotificationDashboardActivity.class.getName(),
"com.android.settings.Settings.BatteryDashboardAlias",
"com.android.settings.Settings.DisplayDashboardAlias",
"com.android.settings.Settings.SoundDashboardAlias",
"com.android.settings.Settings.SecurityDashboardAlias",
Settings.SystemDashboardActivity.class.getName(),
Settings.SupportDashboardActivity.class.getName(),
+ // Home page > Apps & Notifications
+ "com.android.settings.Settings.ManageApplicationsDashboardAlias",
// Home page > Network & Internet
"com.android.settings.Settings.WifiDashboardAlias",
"com.android.settings.Settings.DataUsageDashboardAlias",
- // Home page > Connected devices
- Settings.ConnectedDeviceDashboardActivity.class.getName(),
// Home page > System
"com.android.settings.Settings.LanguageAndInputDashboardAlias",
"com.android.settings.Settings.DateTimeDashboardAlias",
"com.android.settings.Settings.AccessibilityDashboardAlias",
"com.android.settings.Settings.AboutDeviceDashboardAlias",
-
};
private static final String[] ENTRY_FRAGMENTS = {
@@ -393,6 +395,7 @@
SystemDashboardFragment.class.getName(),
NetworkDashboardFragment.class.getName(),
ConnectedDeviceDashboardFragment.class.getName(),
+ AppAndNotificationDashboardFragment.class.getName(),
};
diff --git a/src/com/android/settings/applications/AdvancedAppSettings.java b/src/com/android/settings/applications/AdvancedAppSettings.java
index db9d8de..59c1fbe 100644
--- a/src/com/android/settings/applications/AdvancedAppSettings.java
+++ b/src/com/android/settings/applications/AdvancedAppSettings.java
@@ -15,57 +15,48 @@
*/
package com.android.settings.applications;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
-import android.support.v7.preference.Preference;
+
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
-import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class AdvancedAppSettings extends SettingsPreferenceFragment implements
- Indexable {
+public class AdvancedAppSettings extends DashboardFragment {
static final String TAG = "AdvancedAppSettings";
- private static final String KEY_APP_PERM = "manage_perms";
- private static final String KEY_APP_DOMAIN_URLS = "domain_urls";
- private static final String KEY_HIGH_POWER_APPS = "high_power_apps";
- private static final String KEY_SYSTEM_ALERT_WINDOW = "system_alert_window";
- private static final String KEY_WRITE_SETTINGS_APPS = "write_settings_apps";
-
- private Preference mAppPermsPreference;
- private Preference mAppDomainURLsPreference;
- private Preference mHighPowerPreference;
- private Preference mSystemAlertWindowPreference;
- private Preference mWriteSettingsPreference;
-
- private BroadcastReceiver mPermissionReceiver;
-
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- addPreferencesFromResource(R.xml.advanced_apps);
+ }
- Preference permissions = getPreferenceScreen().findPreference(KEY_APP_PERM);
- permissions.setIntent(new Intent(Intent.ACTION_MANAGE_PERMISSIONS));
+ @Override
+ protected String getCategoryKey() {
+ return "";
+ }
- mAppPermsPreference = findPreference(KEY_APP_PERM);
- mAppDomainURLsPreference = findPreference(KEY_APP_DOMAIN_URLS);
- mHighPowerPreference = findPreference(KEY_HIGH_POWER_APPS);
- mSystemAlertWindowPreference = findPreference(KEY_SYSTEM_ALERT_WINDOW);
- mWriteSettingsPreference = findPreference(KEY_WRITE_SETTINGS_APPS);
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.advanced_apps;
+ }
+
+ @Override
+ protected List<PreferenceController> getPreferenceControllers(Context context) {
+ return null;
}
@Override
@@ -73,25 +64,6 @@
return MetricsEvent.APPLICATIONS_ADVANCED;
}
- private final PermissionsResultCallback mPermissionCallback = new PermissionsResultCallback() {
- @Override
- public void onAppWithPermissionsCountsResult(int standardGrantedPermissionAppCount,
- int standardUsedPermissionAppCount) {
- if (getActivity() == null) {
- return;
- }
- mPermissionReceiver = null;
- if (standardUsedPermissionAppCount != 0) {
- mAppPermsPreference.setSummary(getContext().getString(
- R.string.app_permissions_summary,
- standardGrantedPermissionAppCount,
- standardUsedPermissionAppCount));
- } else {
- mAppPermsPreference.setSummary(null);
- }
- }
- };
-
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
diff --git a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
new file mode 100644
index 0000000..8630541
--- /dev/null
+++ b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
@@ -0,0 +1,56 @@
+/*
+ * 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.applications;
+
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.drawer.CategoryKey;
+
+import java.util.List;
+
+public class AppAndNotificationDashboardFragment extends DashboardFragment {
+
+ private static final String TAG = "AppAndNotifDashboard";
+
+ @Override
+ public int getMetricsCategory() {
+ return APP_AND_NOTIFICATION_CATEGORY_FRAGMENT;
+ }
+
+ @Override
+ protected String getCategoryKey() {
+ return CategoryKey.CATEGORY_APPS;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.app_and_notification;
+ }
+
+ @Override
+ protected List<PreferenceController> getPreferenceControllers(Context context) {
+ return null;
+ }
+}
diff --git a/src/com/android/settings/core/InstrumentedFragment.java b/src/com/android/settings/core/InstrumentedFragment.java
index 7297953..ca683e3 100644
--- a/src/com/android/settings/core/InstrumentedFragment.java
+++ b/src/com/android/settings/core/InstrumentedFragment.java
@@ -39,6 +39,7 @@
protected final int STORAGE_CATEGORY_FRAGMENT = PLACEHOLDER_METRIC + 2;
protected final int NETWORK_CATEGORY_FRAGMENT = PLACEHOLDER_METRIC + 3;
protected final int CONNECTED_DEVICE_CATEGORY_FRAGMENT = PLACEHOLDER_METRIC + 4;
+ protected final int APP_AND_NOTIFICATION_CATEGORY_FRAGMENT = PLACEHOLDER_METRIC + 5;
public InstrumentedFragment() {
// Mixin that logs visibility change for activity.
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index f501dfa..9ea48b0 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -20,6 +20,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
+import android.support.v7.util.DiffUtil;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
@@ -34,7 +35,6 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
@@ -48,82 +48,92 @@
import java.util.List;
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
- implements View.OnClickListener, SummaryLoader.SummaryConsumer {
+ implements SummaryLoader.SummaryConsumer {
public static final String TAG = "DashboardAdapter";
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
private static final String STATE_CATEGORY_LIST = "category_list";
- private static final String STATE_IS_SHOWING_ALL = "is_showing_all";
private static final String STATE_SUGGESTION_MODE = "suggestion_mode";
- private static final int NS_SPACER = 0;
- private static final int NS_SUGGESTION = 1000;
- private static final int NS_ITEMS = 2000;
- private static final int NS_CONDITION = 3000;
- private static int SUGGESTION_MODE_DEFAULT = 0;
- private static int SUGGESTION_MODE_COLLAPSED = 1;
- private static int SUGGESTION_MODE_EXPANDED = 2;
-
- private static final int DEFAULT_SUGGESTION_COUNT = 2;
-
- private final List<Object> mItems = new ArrayList<>();
- private final List<Integer> mTypes = new ArrayList<>();
- private final List<Integer> mIds = new ArrayList<>();
private final IconCache mCache;
-
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
-
- private List<DashboardCategory> mCategories;
- private List<Condition> mConditions;
- private List<Tile> mSuggestions;
-
- private boolean mIsShowingAll;
- // Used for counting items;
- private int mId;
-
- private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
-
- private Condition mExpandedCondition = null;
+ private DashboardData mDashboardData;
private SuggestionParser mSuggestionParser;
+ private View.OnClickListener mTileClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ //TODO: get rid of setTag/getTag
+ final Tile tile = (Tile) mDashboardData.getItemEntityByPosition((int) v.getTag());
+ ((SettingsActivity) mContext).openTile(tile);
+ }
+ };
+
+ private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Condition expandedCondition = mDashboardData.getExpandedCondition();
+
+ //TODO: get rid of setTag/getTag
+ if (v.getTag() == expandedCondition) {
+ mMetricsFeatureProvider.action(mContext,
+ MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
+ expandedCondition.getMetricsConstant());
+ expandedCondition.onPrimaryClick();
+ } else {
+ expandedCondition = (Condition) v.getTag();
+ mMetricsFeatureProvider.action(mContext,
+ MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
+ expandedCondition.getMetricsConstant());
+
+ updateExpandedCondition(expandedCondition);
+ }
+ }
+ };
+
public DashboardAdapter(Context context, SuggestionParser parser,
MetricsFeatureProvider metricsFeatureProvider, Bundle savedInstanceState,
List<Condition> conditions) {
+ List<Tile> suggestions = null;
+ List<DashboardCategory> categories = null;
+ int suggestionMode = DashboardData.SUGGESTION_MODE_DEFAULT;
+
mContext = context;
mMetricsFeatureProvider = metricsFeatureProvider;
mCache = new IconCache(context);
mSuggestionParser = parser;
- mConditions = conditions;
setHasStableIds(true);
- boolean showAll = true;
if (savedInstanceState != null) {
- mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
- mCategories = savedInstanceState.getParcelableArrayList(STATE_CATEGORY_LIST);
- showAll = savedInstanceState.getBoolean(STATE_IS_SHOWING_ALL, true);
- mSuggestionMode = savedInstanceState.getInt(
- STATE_SUGGESTION_MODE, SUGGESTION_MODE_DEFAULT);
+ suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
+ categories = savedInstanceState.getParcelableArrayList(STATE_CATEGORY_LIST);
+ suggestionMode = savedInstanceState.getInt(
+ STATE_SUGGESTION_MODE, DashboardData.SUGGESTION_MODE_DEFAULT);
}
- setShowingAll(showAll);
+
+ mDashboardData = new DashboardData.Builder()
+ .setConditions(conditions)
+ .setSuggestions(suggestions)
+ .setCategories(categories)
+ .setSuggestionMode(suggestionMode)
+ .build();
}
public List<Tile> getSuggestions() {
- return mSuggestions;
+ return mDashboardData.getSuggestions();
}
public void setCategoriesAndSuggestions(List<DashboardCategory> categories,
List<Tile> suggestions) {
- mSuggestions = suggestions;
- mCategories = categories;
-
// TODO: Better place for tinting?
TypedValue tintColor = new TypedValue();
mContext.getTheme().resolveAttribute(com.android.internal.R.attr.colorAccent,
tintColor, true);
for (int i = 0; i < categories.size(); i++) {
for (int j = 0; j < categories.get(i).tiles.size(); j++) {
- Tile tile = categories.get(i).tiles.get(j);
+ final Tile tile = categories.get(i).tiles.get(j);
if (!mContext.getPackageName().equals(
tile.intent.getComponent().getPackageName())) {
@@ -133,85 +143,49 @@
}
}
}
- recountItems();
+
+ final DashboardData prevData = mDashboardData;
+ mDashboardData = new DashboardData.Builder(prevData)
+ .setSuggestions(suggestions)
+ .setCategories(categories)
+ .build();
+ notifyDashboardDataChanged(prevData);
}
public void setConditions(List<Condition> conditions) {
- mConditions = conditions;
- recountItems();
+ final DashboardData prevData = mDashboardData;
+ mDashboardData = new DashboardData.Builder(prevData)
+ .setConditions(conditions)
+ .build();
+ notifyDashboardDataChanged(prevData);
}
@Override
public void notifySummaryChanged(Tile tile) {
- notifyDataSetChanged();
- }
+ final int position = mDashboardData.getPositionByTile(tile);
+ if (position != DashboardData.POSITION_NOT_FOUND) {
+ final Tile targetTile = (Tile) mDashboardData.getItemEntityByPosition(position);
+ if (!TextUtils.equals(tile.summary, targetTile.summary)) {
- public void setShowingAll(boolean showingAll) {
- mIsShowingAll = showingAll;
- recountItems();
- }
-
- private void recountItems() {
- reset();
- boolean hasConditions = false;
- for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
- boolean shouldShow = mConditions.get(i).shouldShow();
- hasConditions |= shouldShow;
- countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
- }
- boolean hasSuggestions = mSuggestions != null && mSuggestions.size() != 0;
- countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
- countItem(null, R.layout.suggestion_header, hasSuggestions, NS_SPACER);
- resetCount();
- if (mSuggestions != null) {
- int maxSuggestions = getDisplayableSuggestionCount();
- for (int i = 0; i < mSuggestions.size(); i++) {
- countItem(mSuggestions.get(i), R.layout.suggestion_tile, i < maxSuggestions,
- NS_SUGGESTION);
+ // Since usually tile in parameter and tile in mCategories are same instance,
+ // which is hard to be detected by DiffUtil, so we notifyItemChanged directly.
+ notifyItemChanged(position);
}
}
- resetCount();
- for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
- DashboardCategory category = mCategories.get(i);
- countItem(category, R.layout.dashboard_category, mIsShowingAll
- && !TextUtils.isEmpty(category.title), NS_ITEMS);
- for (int j = 0; j < category.tiles.size(); j++) {
- Tile tile = category.tiles.get(j);
- countItem(tile, R.layout.dashboard_tile, mIsShowingAll
- || ArrayUtils.contains(DashboardSummary.INITIAL_ITEMS,
- tile.intent.getComponent().getClassName()), NS_ITEMS);
- }
+ }
+
+ // TODO: move this method to SuggestionParser or some other util class
+ public void disableSuggestion(Tile suggestion) {
+ if (mSuggestionParser == null) {
+ return;
}
- notifyDataSetChanged();
- }
-
- private void resetCount() {
- mId = 0;
- }
-
- private void reset() {
- mItems.clear();
- mTypes.clear();
- mIds.clear();
- mId = 0;
- }
-
- private void countItem(Object object, int type, boolean add, int nameSpace) {
- if (add) {
- mItems.add(object);
- mTypes.add(type);
- // TODO: Counting namespaces for handling of suggestions/conds appearing/disappearing.
- mIds.add(mId + nameSpace);
+ if (mSuggestionParser.dismissSuggestion(suggestion)) {
+ mContext.getPackageManager().setComponentEnabledSetting(
+ suggestion.intent.getComponent(),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ mSuggestionParser.markCategoryDone(suggestion.category);
}
- mId++;
- }
-
- private int getDisplayableSuggestionCount() {
- final int suggestionSize = mSuggestions.size();
- return mSuggestionMode == SUGGESTION_MODE_DEFAULT
- ? Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize)
- : mSuggestionMode == SUGGESTION_MODE_EXPANDED
- ? suggestionSize : 0;
}
@Override
@@ -222,21 +196,24 @@
@Override
public void onBindViewHolder(DashboardItemHolder holder, int position) {
- switch (mTypes.get(position)) {
+ final int type = mDashboardData.getItemTypeByPosition(position);
+ switch (type) {
case R.layout.dashboard_category:
- onBindCategory(holder, (DashboardCategory) mItems.get(position));
+ onBindCategory(holder,
+ (DashboardCategory) mDashboardData.getItemEntityByPosition(position));
break;
case R.layout.dashboard_tile:
- final Tile tile = (Tile) mItems.get(position);
+ final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
onBindTile(holder, tile);
- holder.itemView.setTag(tile);
- holder.itemView.setOnClickListener(this);
+ holder.itemView.setTag(position);
+ holder.itemView.setOnClickListener(mTileClickListener);
break;
case R.layout.suggestion_header:
- onBindSuggestionHeader(holder);
+ onBindSuggestionHeader(holder, (DashboardData.SuggestionHeaderData)
+ mDashboardData.getItemEntityByPosition(position));
break;
case R.layout.suggestion_tile:
- final Tile suggestion = (Tile) mItems.get(position);
+ final Tile suggestion = (Tile) mDashboardData.getItemEntityByPosition(position);
onBindTile(holder, suggestion);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
@@ -255,12 +232,12 @@
}
});
break;
- case R.layout.see_all:
- onBindSeeAll(holder);
- break;
case R.layout.condition_card:
- ConditionAdapterUtils.bindViews((Condition) mItems.get(position), holder,
- mItems.get(position) == mExpandedCondition, this,
+ final boolean isExpanded = mDashboardData.getItemEntityByPosition(position)
+ == mDashboardData.getExpandedCondition();
+ ConditionAdapterUtils.bindViews(
+ (Condition) mDashboardData.getItemEntityByPosition(position),
+ holder, isExpanded, mConditionClickListener,
new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -271,45 +248,103 @@
}
}
+ @Override
+ public long getItemId(int position) {
+ return mDashboardData.getItemIdByPosition(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mDashboardData.getItemTypeByPosition(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDashboardData.size();
+ }
+
+ public void onExpandClick(View v) {
+ Condition expandedCondition = mDashboardData.getExpandedCondition();
+ if (v.getTag() == expandedCondition) {
+ mMetricsFeatureProvider.action(mContext,
+ MetricsEvent.ACTION_SETTINGS_CONDITION_COLLAPSE,
+ expandedCondition.getMetricsConstant());
+ expandedCondition = null;
+ } else {
+ expandedCondition = (Condition) v.getTag();
+ mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
+ expandedCondition.getMetricsConstant());
+ }
+
+ updateExpandedCondition(expandedCondition);
+ }
+
+ public Object getItem(long itemId) {
+ return mDashboardData.getItemEntityById(itemId);
+ }
+
+ public static String getSuggestionIdentifier(Context context, Tile suggestion) {
+ String packageName = suggestion.intent.getComponent().getPackageName();
+ if (packageName.equals(context.getPackageName())) {
+ // Since Settings provides several suggestions, fill in the class instead of the
+ // package for these.
+ packageName = suggestion.intent.getComponent().getClassName();
+ }
+ return packageName;
+ }
+
+ private void notifyDashboardDataChanged(DashboardData prevData) {
+ if (prevData != null) {
+ final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
+ .ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
+ diffResult.dispatchUpdatesTo(this);
+ } else {
+ notifyDataSetChanged();
+ }
+ }
+
+ private void updateExpandedCondition(Condition condition) {
+ final DashboardData prevData = mDashboardData;
+ mDashboardData = new DashboardData.Builder(prevData)
+ .setExpandedCondition(condition)
+ .build();
+ notifyDashboardDataChanged(prevData);
+ }
+
private void showRemoveOption(View v, final Tile suggestion) {
- PopupMenu popup = new PopupMenu(
+ final PopupMenu popup = new PopupMenu(
new ContextThemeWrapper(mContext, R.style.Theme_AppCompat_DayNight), v);
popup.getMenu().add(R.string.suggestion_remove).setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- mMetricsFeatureProvider.action(
- mContext, MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION,
- DashboardAdapter.getSuggestionIdentifier(mContext, suggestion));
- disableSuggestion(suggestion);
- mSuggestions.remove(suggestion);
- recountItems();
- return true;
- }
- });
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ mMetricsFeatureProvider.action(
+ mContext, MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION,
+ DashboardAdapter.getSuggestionIdentifier(mContext, suggestion));
+ disableSuggestion(suggestion);
+ List<Tile> suggestions = mDashboardData.getSuggestions();
+ suggestions.remove(suggestion);
+
+ DashboardData prevData = mDashboardData;
+ mDashboardData = new DashboardData.Builder(prevData)
+ .setSuggestions(suggestions)
+ .build();
+ notifyDashboardDataChanged(prevData);
+
+ return true;
+ }
+ });
popup.show();
}
- public void disableSuggestion(Tile suggestion) {
- if (mSuggestionParser == null) {
- return;
- }
- if (mSuggestionParser.dismissSuggestion(suggestion)) {
- mContext.getPackageManager().setComponentEnabledSetting(
- suggestion.intent.getComponent(),
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- mSuggestionParser.markCategoryDone(suggestion.category);
- }
- }
+ private void onBindSuggestionHeader(final DashboardItemHolder holder, DashboardData
+ .SuggestionHeaderData data) {
+ final boolean moreSuggestions = data.hasMoreSuggestions;
+ final int undisplayedSuggestionCount = data.undisplayedSuggestionCount;
- private void onBindSuggestionHeader(final DashboardItemHolder holder) {
- final boolean moreSuggestions = hasMoreSuggestions();
- final int undisplayedSuggestionCount =
- mSuggestions.size() - getDisplayableSuggestionCount();
holder.icon.setImageResource(moreSuggestions ? R.drawable.ic_expand_more
: R.drawable.ic_expand_less);
- holder.title.setText(mContext.getString(R.string.suggestions_title, mSuggestions.size()));
+ holder.title.setText(mContext.getString(R.string.suggestions_title, data.suggestionSize));
String summaryContentDescription;
if (moreSuggestions) {
summaryContentDescription = mContext.getResources().getQuantityString(
@@ -329,22 +364,22 @@
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (hasMoreSuggestions()) {
- mSuggestionMode = SUGGESTION_MODE_EXPANDED;
+ final int suggestionMode;
+ if (moreSuggestions) {
+ suggestionMode = DashboardData.SUGGESTION_MODE_EXPANDED;
} else {
- mSuggestionMode = SUGGESTION_MODE_COLLAPSED;
+ suggestionMode = DashboardData.SUGGESTION_MODE_COLLAPSED;
}
- recountItems();
+
+ DashboardData prevData = mDashboardData;
+ mDashboardData = new DashboardData.Builder(prevData)
+ .setSuggestionMode(suggestionMode)
+ .build();
+ notifyDashboardDataChanged(prevData);
}
});
}
- private boolean hasMoreSuggestions() {
- return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
- || (mSuggestionMode == SUGGESTION_MODE_DEFAULT
- && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
- }
-
private void onBindTile(DashboardItemHolder holder, Tile tile) {
holder.icon.setImageDrawable(mCache.getIcon(tile.icon));
holder.title.setText(tile.title);
@@ -360,98 +395,21 @@
holder.title.setText(category.title);
}
- private void onBindSeeAll(DashboardItemHolder holder) {
- holder.title.setText(mIsShowingAll ? R.string.see_less
- : R.string.see_all);
- holder.itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- setShowingAll(!mIsShowingAll);
- }
- });
- }
-
- @Override
- public long getItemId(int position) {
- return mIds.get(position);
- }
-
- @Override
- public int getItemViewType(int position) {
- return mTypes.get(position);
- }
-
- @Override
- public int getItemCount() {
- return mIds.size();
- }
-
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.dashboard_tile) {
- ((SettingsActivity) mContext).openTile((Tile) v.getTag());
- return;
- }
- if (v.getTag() == mExpandedCondition) {
- mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
- mExpandedCondition.getMetricsConstant());
- mExpandedCondition.onPrimaryClick();
- } else {
- mExpandedCondition = (Condition) v.getTag();
- mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
- mExpandedCondition.getMetricsConstant());
- notifyDataSetChanged();
- }
- }
-
- public void onExpandClick(View v) {
- if (v.getTag() == mExpandedCondition) {
- mMetricsFeatureProvider.action(mContext,
- MetricsEvent.ACTION_SETTINGS_CONDITION_COLLAPSE,
- mExpandedCondition.getMetricsConstant());
- mExpandedCondition = null;
- } else {
- mExpandedCondition = (Condition) v.getTag();
- mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
- mExpandedCondition.getMetricsConstant());
- }
- notifyDataSetChanged();
- }
-
- public Object getItem(long itemId) {
- for (int i = 0; i < mIds.size(); i++) {
- if (mIds.get(i) == itemId) {
- return mItems.get(i);
- }
- }
- return null;
- }
-
- public static String getSuggestionIdentifier(Context context, Tile suggestion) {
- String packageName = suggestion.intent.getComponent().getPackageName();
- if (packageName.equals(context.getPackageName())) {
- // Since Settings provides several suggestions, fill in the class instead of the
- // package for these.
- packageName = suggestion.intent.getComponent().getClassName();
- }
- return packageName;
- }
-
void onSaveInstanceState(Bundle outState) {
- if (mSuggestions != null) {
+ final List<Tile> suggestions = mDashboardData.getSuggestions();
+ final List<DashboardCategory> categories = mDashboardData.getCategories();
+ if (suggestions != null) {
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
- new ArrayList<Tile>(mSuggestions));
+ new ArrayList<Tile>(suggestions));
}
- if (mCategories != null) {
+ if (categories != null) {
outState.putParcelableArrayList(STATE_CATEGORY_LIST,
- new ArrayList<DashboardCategory>(mCategories));
+ new ArrayList<DashboardCategory>(categories));
}
- outState.putBoolean(STATE_IS_SHOWING_ALL, mIsShowingAll);
- outState.putInt(STATE_SUGGESTION_MODE, mSuggestionMode);
+ outState.putInt(STATE_SUGGESTION_MODE, mDashboardData.getSuggestionMode());
}
private static class IconCache {
-
private final Context mContext;
private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
diff --git a/src/com/android/settings/dashboard/DashboardData.java b/src/com/android/settings/dashboard/DashboardData.java
new file mode 100644
index 0000000..0fc994b
--- /dev/null
+++ b/src/com/android/settings/dashboard/DashboardData.java
@@ -0,0 +1,488 @@
+/*
+ * 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.dashboard;
+
+import android.annotation.IntDef;
+import android.support.v7.util.DiffUtil;
+import android.text.TextUtils;
+import com.android.settings.dashboard.conditional.Condition;
+import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.Tile;
+import com.android.settings.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description about data list used in the DashboardAdapter. In the data list each item can be
+ * Condition, suggestion or category tile.
+ * <p>
+ * ItemsData has inner class Item, which represents the Item in data list.
+ */
+public class DashboardData {
+ public static final int SUGGESTION_MODE_DEFAULT = 0;
+ public static final int SUGGESTION_MODE_COLLAPSED = 1;
+ public static final int SUGGESTION_MODE_EXPANDED = 2;
+ public static final int POSITION_NOT_FOUND = -1;
+ public static final int DEFAULT_SUGGESTION_COUNT = 2;
+
+ // id namespace for different type of items.
+ private static final int NS_SPACER = 0;
+ private static final int NS_SUGGESTION = 1000;
+ private static final int NS_ITEMS = 2000;
+ private static final int NS_CONDITION = 3000;
+
+ private final List<Item> mItems;
+ private final List<DashboardCategory> mCategories;
+ private final List<Condition> mConditions;
+ private final List<Tile> mSuggestions;
+ private final int mSuggestionMode;
+ private final Condition mExpandedCondition;
+ private int mId;
+
+ private DashboardData(Builder builder) {
+ mCategories = builder.mCategories;
+ mConditions = builder.mConditions;
+ mSuggestions = builder.mSuggestions;
+ mSuggestionMode = builder.mSuggestionMode;
+ mExpandedCondition = builder.mExpandedCondition;
+
+ mItems = new ArrayList<>();
+ mId = 0;
+
+ buildItemsData();
+ }
+
+ public int getItemIdByPosition(int position) {
+ return mItems.get(position).id;
+ }
+
+ public int getItemTypeByPosition(int position) {
+ return mItems.get(position).type;
+ }
+
+ public Object getItemEntityByPosition(int position) {
+ return mItems.get(position).entity;
+ }
+
+ public List<Item> getItemList() {
+ return mItems;
+ }
+
+ public int size() {
+ return mItems.size();
+ }
+
+ public Object getItemEntityById(long id) {
+ for (final Item item : mItems) {
+ if (item.id == id) {
+ return item.entity;
+ }
+ }
+ return null;
+ }
+
+ public List<DashboardCategory> getCategories() {
+ return mCategories;
+ }
+
+ public List<Condition> getConditions() {
+ return mConditions;
+ }
+
+ public List<Tile> getSuggestions() {
+ return mSuggestions;
+ }
+
+ public int getSuggestionMode() {
+ return mSuggestionMode;
+ }
+
+ public Condition getExpandedCondition() {
+ return mExpandedCondition;
+ }
+
+ /**
+ * Find the position of the object in mItems list, using the equals method to compare
+ *
+ * @param entity the object that need to be found in list
+ * @return position of the object, return POSITION_NOT_FOUND if object isn't in the list
+ */
+ public int getPositionByEntity(Object entity) {
+ if (entity == null) return POSITION_NOT_FOUND;
+
+ final int size = mItems.size();
+ for (int i = 0; i < size; i++) {
+ final Object item = mItems.get(i).entity;
+ if (entity.equals(item)) {
+ return i;
+ }
+ }
+
+ return POSITION_NOT_FOUND;
+ }
+
+ /**
+ * Find the position of the Tile object.
+ * <p>
+ * First, try to find the exact identical instance of the tile object, if not found,
+ * then try to find a tile has the same title.
+ *
+ * @param tile tile that need to be found
+ * @return position of the object, return INDEX_NOT_FOUND if object isn't in the list
+ */
+ public int getPositionByTile(Tile tile) {
+ final int size = mItems.size();
+ for (int i = 0; i < size; i++) {
+ final Object entity = mItems.get(i).entity;
+ if (entity == tile) {
+ return i;
+ } else if (entity instanceof Tile && tile.title.equals(((Tile) entity).title)) {
+ return i;
+ }
+ }
+
+ return POSITION_NOT_FOUND;
+ }
+
+ /**
+ * Get the count of suggestions to display
+ *
+ * The displayable count mainly depends on the {@link #mSuggestionMode}
+ * and the size of suggestions list.
+ *
+ * When in default mode, displayable count couldn't larger than
+ * {@link #DEFAULT_SUGGESTION_COUNT}.
+ *
+ * When in expanded mode, display all the suggestions.
+ * @return the count of suggestions to display
+ */
+ public int getDisplayableSuggestionCount() {
+ final int suggestionSize = mSuggestions.size();
+ return mSuggestionMode == SUGGESTION_MODE_DEFAULT
+ ? Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize)
+ : mSuggestionMode == SUGGESTION_MODE_EXPANDED
+ ? suggestionSize : 0;
+ }
+
+ public boolean hasMoreSuggestions() {
+ return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
+ || (mSuggestionMode == SUGGESTION_MODE_DEFAULT
+ && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
+ }
+
+ private void resetCount() {
+ mId = 0;
+ }
+
+ /**
+ * Count the item and add it into list when {@paramref add} is true.
+ *
+ * Note that {@link #mId} will increment automatically and the real
+ * id stored in {@link Item} is shifted by {@paramref nameSpace}. This is a
+ * simple way to keep the id stable.
+ *
+ * @param object maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null
+ * @param type type of the item, and value is the layout id
+ * @param add flag about whether to add item into list
+ * @param nameSpace namespace based on the type
+ */
+ private void countItem(Object object, int type, boolean add, int nameSpace) {
+ if (add) {
+ mItems.add(new Item(object, type, mId + nameSpace, object == mExpandedCondition));
+ }
+ mId++;
+ }
+
+ /**
+ * Build the mItems list using mConditions, mSuggestions, mCategories data
+ * and mIsShowingAll, mSuggestionMode flag.
+ */
+ private void buildItemsData() {
+ boolean hasConditions = false;
+ for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
+ boolean shouldShow = mConditions.get(i).shouldShow();
+ hasConditions |= shouldShow;
+ countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
+ }
+
+ resetCount();
+ final boolean hasSuggestions = mSuggestions != null && mSuggestions.size() != 0;
+ countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
+ countItem(buildSuggestionHeaderData(), R.layout.suggestion_header, hasSuggestions,
+ NS_SPACER);
+
+ resetCount();
+ if (mSuggestions != null) {
+ int maxSuggestions = getDisplayableSuggestionCount();
+ for (int i = 0; i < mSuggestions.size(); i++) {
+ countItem(mSuggestions.get(i), R.layout.suggestion_tile, i < maxSuggestions,
+ NS_SUGGESTION);
+ }
+ }
+ resetCount();
+ for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
+ DashboardCategory category = mCategories.get(i);
+ countItem(category, R.layout.dashboard_category,
+ !TextUtils.isEmpty(category.title), NS_ITEMS);
+ for (int j = 0; j < category.tiles.size(); j++) {
+ Tile tile = category.tiles.get(j);
+ countItem(tile, R.layout.dashboard_tile, true, NS_ITEMS);
+ }
+ }
+ }
+
+ private SuggestionHeaderData buildSuggestionHeaderData() {
+ SuggestionHeaderData data;
+ if (mSuggestions == null) {
+ data = new SuggestionHeaderData();
+ } else {
+ final boolean hasMoreSuggestions = hasMoreSuggestions();
+ final int suggestionSize = mSuggestions.size();
+ final int undisplayedSuggestionCount = suggestionSize - getDisplayableSuggestionCount();
+ data = new SuggestionHeaderData(hasMoreSuggestions, suggestionSize,
+ undisplayedSuggestionCount);
+ }
+
+ return data;
+ }
+
+ /**
+ * Builder used to build the ItemsData
+ * <p>
+ * {@link #mExpandedCondition} and {@link #mSuggestionMode} have default value
+ * while others are not.
+ */
+ public static class Builder {
+ private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
+ private Condition mExpandedCondition = null;
+
+ private List<DashboardCategory> mCategories;
+ private List<Condition> mConditions;
+ private List<Tile> mSuggestions;
+
+
+ public Builder() {
+ }
+
+ public Builder(DashboardData dashboardData) {
+ mCategories = dashboardData.mCategories;
+ mConditions = dashboardData.mConditions;
+ mSuggestions = dashboardData.mSuggestions;
+ mSuggestionMode = dashboardData.mSuggestionMode;
+ mExpandedCondition = dashboardData.mExpandedCondition;
+ }
+
+ public Builder setCategories(List<DashboardCategory> categories) {
+ this.mCategories = categories;
+ return this;
+ }
+
+ public Builder setConditions(List<Condition> conditions) {
+ this.mConditions = conditions;
+ return this;
+ }
+
+ public Builder setSuggestions(List<Tile> suggestions) {
+ this.mSuggestions = suggestions;
+ return this;
+ }
+
+ public Builder setSuggestionMode(int suggestionMode) {
+ this.mSuggestionMode = suggestionMode;
+ return this;
+ }
+
+ public Builder setExpandedCondition(Condition expandedCondition) {
+ this.mExpandedCondition = expandedCondition;
+ return this;
+ }
+
+ public DashboardData build() {
+ return new DashboardData(this);
+ }
+ }
+
+ /**
+ * A DiffCallback to calculate the difference between old and new Item
+ * List in DashboardData
+ */
+ public static class ItemsDataDiffCallback extends DiffUtil.Callback {
+ final private List<Item> mOldItems;
+ final private List<Item> mNewItems;
+
+ public ItemsDataDiffCallback(List<Item> oldItems, List<Item> newItems) {
+ mOldItems = oldItems;
+ mNewItems = newItems;
+ }
+
+ @Override
+ public int getOldListSize() {
+ return mOldItems.size();
+ }
+
+ @Override
+ public int getNewListSize() {
+ return mNewItems.size();
+ }
+
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+ return mOldItems.get(oldItemPosition).id == mNewItems.get(newItemPosition).id;
+ }
+
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+ return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
+ }
+ }
+
+ /**
+ * An item contains the data needed in the DashboardData.
+ */
+ private static class Item {
+ // valid types in field type
+ private static final int TYPE_DASHBOARD_CATEGORY = R.layout.dashboard_category;
+ private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
+ private static final int TYPE_SUGGESTION_HEADER = R.layout.suggestion_header;
+ private static final int TYPE_SUGGESTION_TILE = R.layout.suggestion_tile;
+ private static final int TYPE_CONDITION_CARD = R.layout.condition_card;
+ private static final int TYPE_DASHBOARD_SPACER = R.layout.dashboard_spacer;
+
+ @IntDef({TYPE_DASHBOARD_CATEGORY, TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_HEADER,
+ TYPE_SUGGESTION_TILE, TYPE_CONDITION_CARD, TYPE_DASHBOARD_SPACER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ItemTypes{}
+
+ /**
+ * The main data object in item, usually is a {@link Tile}, {@link Condition} or
+ * {@link DashboardCategory} object. This object can also be null when the
+ * item is an divider line. Please refer to {@link #buildItemsData()} for
+ * detail usage of the Item.
+ */
+ public final Object entity;
+
+ /**
+ * The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile)
+ */
+ public final @ItemTypes int type;
+
+ /**
+ * Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item.
+ */
+ public final int id;
+
+ /**
+ * To store whether the condition is expanded, useless when {@link #type} is not
+ * {@link #TYPE_CONDITION_CARD}
+ */
+ public final boolean conditionExpanded;
+
+ public Item(Object entity, @ItemTypes int type, int id, boolean conditionExpanded) {
+ this.entity = entity;
+ this.type = type;
+ this.id = id;
+ this.conditionExpanded = conditionExpanded;
+ }
+
+ /**
+ * Override it to make comparision in the {@link ItemsDataDiffCallback}
+ * @param obj object to compared with
+ * @return true if the same object or has equal value.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Item)) {
+ return false;
+ }
+
+ final Item targetItem = (Item) obj;
+ if (type != targetItem.type || id != targetItem.id) {
+ return false;
+ }
+
+ switch (type) {
+ case TYPE_DASHBOARD_CATEGORY:
+ // Only check title for dashboard category
+ return TextUtils.equals(((DashboardCategory)entity).title,
+ ((DashboardCategory) targetItem.entity).title);
+ case TYPE_DASHBOARD_TILE:
+ final Tile localTile = (Tile)entity;
+ final Tile targetTile = (Tile)targetItem.entity;
+
+ // Only check title and summary for dashboard tile
+ return TextUtils.equals(localTile.title, targetTile.title)
+ && TextUtils.equals(localTile.summary, targetTile.summary);
+ case TYPE_CONDITION_CARD:
+ // First check conditionExpanded for quick return
+ if (conditionExpanded != targetItem.conditionExpanded) {
+ return false;
+ }
+ // After that, go to default to do final check
+ default:
+ return entity == null ? targetItem.entity == null
+ : entity.equals(targetItem.entity);
+ }
+ }
+ }
+
+ /**
+ * This class contains the data needed to build the header. The data can also be
+ * used to check the diff in DiffUtil.Callback
+ */
+ public static class SuggestionHeaderData {
+ public final boolean hasMoreSuggestions;
+ public final int suggestionSize;
+ public final int undisplayedSuggestionCount;
+
+ public SuggestionHeaderData(boolean moreSuggestions, int suggestionSize, int
+ undisplayedSuggestionCount) {
+ this.hasMoreSuggestions = moreSuggestions;
+ this.suggestionSize = suggestionSize;
+ this.undisplayedSuggestionCount = undisplayedSuggestionCount;
+ }
+
+ public SuggestionHeaderData() {
+ hasMoreSuggestions = false;
+ suggestionSize = 0;
+ undisplayedSuggestionCount = 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof SuggestionHeaderData)) {
+ return false;
+ }
+
+ SuggestionHeaderData targetData = (SuggestionHeaderData) obj;
+
+ return hasMoreSuggestions == targetData.hasMoreSuggestions
+ && suggestionSize == targetData.suggestionSize
+ && undisplayedSuggestionCount == targetData.undisplayedSuggestionCount;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 9ab8cf4..37bc447 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -277,11 +277,6 @@
displayResourceTiles();
refreshDashboardTiles(TAG);
-
- if (!mProgressiveDisclosureMixin.isCollapsed()
- && mProgressiveDisclosureMixin.shouldCollapse(getPreferenceScreen())) {
- mProgressiveDisclosureMixin.collapse(getPreferenceScreen());
- }
}
/**
diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java
index e284bed..c99a4ab 100644
--- a/src/com/android/settings/datausage/DataUsageSummary.java
+++ b/src/com/android/settings/datausage/DataUsageSummary.java
@@ -77,6 +77,11 @@
private int mDataUsageTemplate;
@Override
+ protected int getHelpResource() {
+ return R.string.help_url_data_usage;
+ }
+
+ @Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
diff --git a/src/com/android/settings/gestures/GesturePreference.java b/src/com/android/settings/gestures/GesturePreference.java
index 1909dcd..5a64a5a 100644
--- a/src/com/android/settings/gestures/GesturePreference.java
+++ b/src/com/android/settings/gestures/GesturePreference.java
@@ -150,6 +150,10 @@
playButton.setVisibility(View.VISIBLE);
}
}
+ if (mMediaPlayer != null && !mMediaPlayer.isPlaying() &&
+ playButton.getVisibility() != View.VISIBLE) {
+ playButton.setVisibility(View.VISIBLE);
+ }
}
});
@@ -175,4 +179,10 @@
}
}
+ void onViewInvisible() {
+ if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+ mMediaPlayer.pause();
+ }
+ }
+
}
diff --git a/src/com/android/settings/gestures/GestureSettings.java b/src/com/android/settings/gestures/GestureSettings.java
index a8f70d9..6fc7abe 100644
--- a/src/com/android/settings/gestures/GestureSettings.java
+++ b/src/com/android/settings/gestures/GestureSettings.java
@@ -145,6 +145,14 @@
}
@Override
+ public void onStop() {
+ super.onStop();
+ for (GesturePreference preference : mPreferences) {
+ preference.onViewInvisible();
+ }
+ }
+
+ @Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean enabled = (boolean) newValue;
String key = preference.getKey();
diff --git a/tests/app/src/com/android/settings/tests/KeepOnScreenTest.java b/tests/app/src/com/android/settings/tests/KeepOnScreenTest.java
new file mode 100644
index 0000000..0b1308e
--- /dev/null
+++ b/tests/app/src/com/android/settings/tests/KeepOnScreenTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.tests;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import com.android.settings.R;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.*;
+import static junit.framework.Assert.assertEquals;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class KeepOnScreenTest {
+ private static int EXPECTED_FLAG = BatteryManager.BATTERY_PLUGGED_AC
+ | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS;
+
+ @Test
+ public void testStayAwake_turnOn_StayAwakeWhileWirelessCharging() throws Exception{
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.startActivitySync(new Intent(android.provider.Settings
+ .ACTION_APPLICATION_DEVELOPMENT_SETTINGS));
+
+ final Context targetContext = instrumentation.getTargetContext();
+ final int prevFlag = Settings.Global.getInt(targetContext.getContentResolver(), Settings
+ .Global.STAY_ON_WHILE_PLUGGED_IN);
+
+ // Turn on "Stay Awake" if needed
+ if (prevFlag == 0) {
+ onView(withText(R.string.keep_screen_on)).perform(click());
+ }
+
+ final int currentFlag = Settings.Global.getInt(targetContext.getContentResolver(), Settings
+ .Global.STAY_ON_WHILE_PLUGGED_IN);
+
+ assertEquals(EXPECTED_FLAG, currentFlag);
+
+ // Since this app doesn't have permission(and shouldn't have) to change global setting, we
+ // can only tearDown in this way
+ if (prevFlag != currentFlag) {
+ onView(withText(R.string.keep_screen_on)).perform(click());
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
new file mode 100644
index 0000000..52c2103
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
@@ -0,0 +1,336 @@
+/*
+ * 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.dashboard;
+
+import android.support.annotation.NonNull;
+import android.support.v7.util.DiffUtil;
+import android.support.v7.util.ListUpdateCallback;
+import com.android.settings.TestConfig;
+import com.android.settings.dashboard.conditional.AirplaneModeCondition;
+import com.android.settings.dashboard.conditional.Condition;
+import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.Tile;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DashboardDataTest {
+ private static final String TEST_SUGGESTION_TITLE = "Use fingerprint";
+ private static final String TEST_CATEGORY_TILE_TITLE = "Display";
+
+ private DashboardData mDashboardDataWithOneConditions;
+ private DashboardData mDashboardDataWithTwoConditions;
+ private DashboardData mDashboardDataWithNoItems;
+ @Mock
+ private Tile mTestCategoryTile;
+ @Mock
+ private Tile mTestSuggestion;
+ @Mock
+ private DashboardCategory mDashboardCategory;
+ @Mock
+ private Condition mTestCondition;
+ @Mock
+ private Condition mSecondCondition; // condition used to test insert in DiffUtil
+
+ @Before
+ public void SetUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // Build suggestions
+ final List<Tile> suggestions = new ArrayList<>();
+ mTestSuggestion.title = TEST_SUGGESTION_TITLE;
+ suggestions.add(mTestSuggestion);
+
+ // Build oneItemConditions
+ final List<Condition> oneItemConditions = new ArrayList<>();
+ when(mTestCondition.shouldShow()).thenReturn(true);
+ oneItemConditions.add(mTestCondition);
+
+ // Build twoItemConditions
+ final List<Condition> twoItemsConditions = new ArrayList<>();
+ when(mSecondCondition.shouldShow()).thenReturn(true);
+ twoItemsConditions.add(mTestCondition);
+ twoItemsConditions.add(mSecondCondition);
+
+ // Build categories
+ final List<DashboardCategory> categories = new ArrayList<>();
+ mTestCategoryTile.title = TEST_CATEGORY_TILE_TITLE;
+ mDashboardCategory.title = "test";
+ mDashboardCategory.tiles = new ArrayList<>();
+ mDashboardCategory.tiles.add(mTestCategoryTile);
+ categories.add(mDashboardCategory);
+
+ // Build DashboardData
+ mDashboardDataWithOneConditions = new DashboardData.Builder()
+ .setConditions(oneItemConditions)
+ .setCategories(categories)
+ .setSuggestions(suggestions)
+ .build();
+
+ mDashboardDataWithTwoConditions = new DashboardData.Builder()
+ .setConditions(twoItemsConditions)
+ .setCategories(categories)
+ .setSuggestions(suggestions)
+ .build();
+
+ mDashboardDataWithNoItems = new DashboardData.Builder()
+ .setConditions(null)
+ .setCategories(null)
+ .setSuggestions(null)
+ .build();
+ }
+
+ @Test
+ public void testBuildItemsData_containsAllData() {
+ final DashboardData.SuggestionHeaderData data =
+ new DashboardData.SuggestionHeaderData(false, 1, 0);
+ final Object[] expectedObjects = {mTestCondition, null, data, mTestSuggestion,
+ mDashboardCategory, mTestCategoryTile};
+ final int expectedSize = expectedObjects.length;
+
+ assertThat(mDashboardDataWithOneConditions.getItemList().size())
+ .isEqualTo(expectedSize);
+ for (int i = 0; i < expectedSize; i++) {
+ if (mDashboardDataWithOneConditions.getItemEntityByPosition(i)
+ instanceof DashboardData.SuggestionHeaderData) {
+ // SuggestionHeaderData is created inside when build, we can only use isEqualTo
+ assertThat(mDashboardDataWithOneConditions.getItemEntityByPosition(i))
+ .isEqualTo(expectedObjects[i]);
+ } else {
+ assertThat(mDashboardDataWithOneConditions.getItemEntityByPosition(i))
+ .isSameAs(expectedObjects[i]);
+ }
+ }
+ }
+
+ @Test
+ public void testGetPositionByEntity_selfInstance_returnPositionFound() {
+ final int position = mDashboardDataWithOneConditions
+ .getPositionByEntity(mTestCondition);
+ assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByEntity_notExisted_returnNotFound() {
+ final Condition condition = mock(AirplaneModeCondition.class);
+ final int position = mDashboardDataWithOneConditions.getPositionByEntity(condition);
+ assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByTile_selfInstance_returnPositionFound() {
+ final int position = mDashboardDataWithOneConditions
+ .getPositionByTile(mTestCategoryTile);
+ assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByTile_equalTitle_returnPositionFound() {
+ final Tile tile = mock(Tile.class);
+ tile.title = TEST_CATEGORY_TILE_TITLE;
+ final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
+ assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testGetPositionByTile_notExisted_returnNotFound() {
+ final Tile tile = mock(Tile.class);
+ tile.title = "";
+ final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
+ assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
+ }
+
+ @Test
+ public void testDiffUtil_DataEqual_noResultData() {
+ List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
+ testDiffUtil(mDashboardDataWithOneConditions,
+ mDashboardDataWithOneConditions, testResultData);
+ }
+
+ @Test
+ public void testDiffUtil_InsertOneCondition_ResultDataOneInserted() {
+ //Build testResultData
+ final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
+ testResultData.add(new ListUpdateResult.ResultData(
+ ListUpdateResult.ResultData.TYPE_OPERATION_INSERT, 1, 1));
+
+ testDiffUtil(mDashboardDataWithOneConditions,
+ mDashboardDataWithTwoConditions, testResultData);
+ }
+
+ @Test
+ public void testDiffUtil_DeleteAllData_ResultDataOneDeleted() {
+ //Build testResultData
+ final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
+ testResultData.add(new ListUpdateResult.ResultData(
+ ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 6));
+
+ testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
+ }
+
+ /**
+ * Test when using the
+ * {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
+ * to transfer List from {@paramref baseDashboardData} to {@paramref diffDashboardData}, whether
+ * the transform data result is equals to {@paramref testResultData}
+ * <p>
+ * The steps are described below:
+ * 1. Calculate a {@link android.support.v7.util.DiffUtil.DiffResult} from
+ * {@paramref baseDashboardData} to {@paramref diffDashboardData}
+ * <p>
+ * 2. Dispatch the {@link android.support.v7.util.DiffUtil.DiffResult} calculated from step 1
+ * into {@link ListUpdateResult}
+ * <p>
+ * 3. Get result data(a.k.a. baseResultData) from {@link ListUpdateResult} and compare it to
+ * {@paramref testResultData}
+ * <p>
+ * Because baseResultData and {@paramref testResultData} don't have sequence. When do the
+ * comparison, we will sort them first and then compare the inside data from them one by one.
+ *
+ * @param baseDashboardData
+ * @param diffDashboardData
+ * @param testResultData
+ */
+ private void testDiffUtil(DashboardData baseDashboardData, DashboardData diffDashboardData,
+ List<ListUpdateResult.ResultData> testResultData) {
+ final DiffUtil.DiffResult diffUtilResult = DiffUtil.calculateDiff(
+ new DashboardData.ItemsDataDiffCallback(
+ baseDashboardData.getItemList(), diffDashboardData.getItemList()));
+
+ // Dispatch to listUpdateResult, then listUpdateResult will have result data
+ final ListUpdateResult listUpdateResult = new ListUpdateResult();
+ diffUtilResult.dispatchUpdatesTo(listUpdateResult);
+
+ final List<ListUpdateResult.ResultData> baseResultData = listUpdateResult.getResultData();
+ assertThat(testResultData.size()).isEqualTo(baseResultData.size());
+
+ // Sort them so we can compare them one by one using a for loop
+ Collections.sort(baseResultData);
+ Collections.sort(testResultData);
+ final int size = baseResultData.size();
+ for (int i = 0; i < size; i++) {
+ // Refer to equals method in ResultData
+ assertThat(baseResultData.get(i)).isEqualTo(testResultData.get(i));
+ }
+ }
+
+ /**
+ * This class contains the result about how the changes made to convert one
+ * list to another list. It implements ListUpdateCallback to record the result data.
+ */
+ private static class ListUpdateResult implements ListUpdateCallback {
+ final private List<ResultData> mResultData;
+
+ public ListUpdateResult() {
+ mResultData = new ArrayList<>();
+ }
+
+ public List<ResultData> getResultData() {
+ return mResultData;
+ }
+
+ @Override
+ public void onInserted(int position, int count) {
+ mResultData.add(new ResultData(ResultData.TYPE_OPERATION_INSERT, position, count));
+ }
+
+ @Override
+ public void onRemoved(int position, int count) {
+ mResultData.add(new ResultData(ResultData.TYPE_OPERATION_REMOVE, position, count));
+ }
+
+ @Override
+ public void onMoved(int fromPosition, int toPosition) {
+ mResultData.add(
+ new ResultData(ResultData.TYPE_OPERATION_MOVE, fromPosition, toPosition));
+ }
+
+ @Override
+ public void onChanged(int position, int count, Object payload) {
+ mResultData.add(new ResultData(ResultData.TYPE_OPERATION_CHANGE, position, count));
+ }
+
+ /**
+ * This class contains general type and field to record the operation data generated
+ * in {@link ListUpdateCallback}. Please refer to {@link ListUpdateCallback} for more info.
+ * <p>
+ * The following are examples about the data stored in this class:
+ * <p>
+ * "The data starts from position(arg1) with count number(arg2) is changed(operation)"
+ * or "The data is moved(operation) from position1(arg1) to position2(arg2)"
+ */
+ private static class ResultData implements Comparable<ResultData> {
+ public static final int TYPE_OPERATION_INSERT = 0;
+ public static final int TYPE_OPERATION_REMOVE = 1;
+ public static final int TYPE_OPERATION_MOVE = 2;
+ public static final int TYPE_OPERATION_CHANGE = 3;
+
+ public final int operation;
+ public final int arg1;
+ public final int arg2;
+
+ public ResultData(int operation, int arg1, int arg2) {
+ this.operation = operation;
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof ResultData)) {
+ return false;
+ }
+
+ ResultData targetData = (ResultData) obj;
+
+ return operation == targetData.operation && arg1 == targetData.arg1
+ && arg2 == targetData.arg2;
+ }
+
+ @Override
+ public int compareTo(@NonNull ResultData resultData) {
+ if (this.operation != resultData.operation) {
+ return operation - resultData.operation;
+ }
+
+ if (arg1 != resultData.arg1) {
+ return arg1 - resultData.arg1;
+ }
+
+ return arg2 - resultData.arg2;
+ }
+ }
+ }
+}