Merge "Update Search UX for OC" into oc-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index adbc1d6..b255123 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4474,10 +4474,10 @@
     <!-- Title for the cellular network in power use UI(i.e. Mobile network scanning: 30% of battery usage) [CHAR_LIMIT=40] -->
     <string name="device_cellular_network">Mobile network scanning</string>
 
-    <!-- Label for time since last full charge in power use UI, i.e. "7 h 20 min ago" [CHAR_LIMIT=60] -->
-    <string name="power_last_full_charge_summary"><xliff:g id="time">%1$s</xliff:g> ago</string>
-    <!-- Label for list of apps using battery in power use UI [CHAR_LIMIT=120] -->
-    <string name="power_usage_list_summary">App usage since full charge (<xliff:g id="time">%1$s</xliff:g> ago)</string>
+    <!-- Label for time since last full charge in power use UI, i.e. "7 h 20 min ago". Note: ^1 should be used in all translations [CHAR_LIMIT=60] -->
+    <string name="power_last_full_charge_summary"><xliff:g id="time">^1</xliff:g> ago</string>
+    <!-- Label for list of apps using battery in power use UI. Note: ^1 should be used in all translations[CHAR_LIMIT=120] -->
+    <string name="power_usage_list_summary">App usage since full charge (<xliff:g id="time">^1</xliff:g> ago)</string>
     <!-- Description for the screen usage item [CHAR_LIMIT=120] -->
     <string name="screen_usage_summary">Amount of time screen has been on since full charge</string>
     <!-- Label for list of different types using battery in power use UI [CHAR_LIMIT=60] -->
@@ -4676,8 +4676,8 @@
     <!-- Representation of a mAh value. [CHAR LIMIT=NONE] -->
     <string name="mah"><xliff:g id="number" example="30">%d</xliff:g> mAh</string>
 
-    <!-- Description for battery usage time for an app, i.e. Used for 30min. [CHAR LIMIT=60] -->
-    <string name="battery_used_for">Used for %1$s</string>
+    <!-- Description for battery usage time for an app, i.e. Used for 30min. Note: ^1 should be used in all translations [CHAR LIMIT=60] -->
+    <string name="battery_used_for">Used for <xliff:g id="time">^1</xliff:g></string>
     <!-- Description for battery usage info for an app, i.e. 60% used by facebook. [CHAR LIMIT=60] -->
     <string name="battery_used_by"><xliff:g id="percent">%1$s</xliff:g> used by <xliff:g id="app">%2$s</xliff:g></string>
     <!-- Description for percentage of battery usage for an app, i.e. Screen: 30% of overall battery. [CHAR LIMIT=60] -->
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java
index f97811c..f668957 100644
--- a/src/com/android/settings/ApnEditor.java
+++ b/src/com/android/settings/ApnEditor.java
@@ -788,9 +788,14 @@
      * Check the key fields' validity and save if valid.
      * @param force save even if the fields are not valid, if the app is
      *        being suspended
-     * @return true if the data was saved
+     * @return true if there's no error
      */
     private boolean validateAndSave(boolean force) {
+        // nothing to do if it's a read only APN
+        if (mReadOnlyApn) {
+            return true;
+        }
+
         String name = checkNotSet(mName.getText());
         String apn = checkNotSet(mApn.getText());
         String mcc = checkNotSet(mMcc.getText());
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index fa9410d..69f563e 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -284,11 +284,13 @@
                     .getAuthenticatorTypesAsUser(profileId);
             final int M = descs.length;
 
-            View titleView = Utils.inflateCategoryHeader(inflater, contents);
-            final TextView titleText = (TextView) titleView.findViewById(android.R.id.title);
-            titleText.setText(userInfo.isManagedProfile() ? R.string.category_work
-                    : R.string.category_personal);
-            contents.addView(titleView);
+            if (profilesSize > 1) {
+                View titleView = Utils.inflateCategoryHeader(inflater, contents);
+                final TextView titleText = (TextView) titleView.findViewById(android.R.id.title);
+                titleText.setText(userInfo.isManagedProfile() ? R.string.category_work
+                        : R.string.category_personal);
+                contents.addView(titleView);
+            }
 
             for (int i = 0; i < N; i++) {
                 Account account = accounts[i];
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index d04ae8d..f6f980a 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -81,6 +81,8 @@
 import android.telephony.TelephonyManager;
 import android.text.Spannable;
 import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.style.TtsSpan;
@@ -770,8 +772,9 @@
      * @param withSeconds include seconds?
      * @return the formatted elapsed time
      */
-    public static String formatElapsedTime(Context context, double millis, boolean withSeconds) {
-        StringBuilder sb = new StringBuilder();
+    public static CharSequence formatElapsedTime(Context context, double millis,
+            boolean withSeconds) {
+        SpannableStringBuilder sb = new SpannableStringBuilder();
         int seconds = (int) Math.floor(millis / 1000);
         if (!withSeconds) {
             // Round up.
@@ -812,9 +815,15 @@
                         hours, minutes));
             } else {
                 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
+
+                // Add ttsSpan if it only have minute value, because it will be read as "meters"
+                TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+                        .setUnit("minute").build();
+                sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             }
         }
-        return sb.toString();
+
+        return sb;
     }
 
     /**
@@ -1256,4 +1265,5 @@
         return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
                 && volume.isMountedReadable();
     }
+
 }
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 0676f56..5ad5e48 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -99,6 +99,7 @@
     private static final String KEY_CLEAR_URI = "clear_uri_button";
 
     private static final String KEY_CACHE_CLEARED = "cache_cleared";
+    private static final String KEY_DATA_CLEARED = "data_cleared";
 
     // Views related to cache info
     private Preference mCacheSize;
@@ -115,6 +116,7 @@
 
     private boolean mCanClearData = true;
     private boolean mCacheCleared;
+    private boolean mDataCleared;
 
     private AppStorageSizesController mSizeController;
 
@@ -130,6 +132,8 @@
         super.onCreate(savedInstanceState);
         if (savedInstanceState != null) {
             mCacheCleared = savedInstanceState.getBoolean(KEY_CACHE_CLEARED, false);
+            mDataCleared = savedInstanceState.getBoolean(KEY_DATA_CLEARED, false);
+            mCacheCleared = mCacheCleared || mDataCleared;
         }
 
         addPreferencesFromResource(R.xml.app_storage_settings);
@@ -147,6 +151,7 @@
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putBoolean(KEY_CACHE_CLEARED, mCacheCleared);
+        outState.putBoolean(KEY_DATA_CLEARED, mDataCleared);
     }
 
     private void setupViews() {
@@ -527,6 +532,9 @@
         if (mCacheCleared) {
             mSizeController.setCacheCleared(true);
         }
+        if (mDataCleared) {
+            mSizeController.setDataCleared(true);
+        }
 
         mSizeController.updateUi(getContext());
 
@@ -538,7 +546,7 @@
             long dataSize = result.getDataBytes();
             long cacheSize = result.getCacheBytes();
 
-            if (dataSize <= 0 || !mCanClearData) {
+            if (dataSize <= 0 || !mCanClearData || mDataCleared) {
                 mClearDataButton.setEnabled(false);
             } else {
                 mClearDataButton.setEnabled(true);
@@ -564,6 +572,8 @@
             }
             switch (msg.what) {
                 case MSG_CLEAR_USER_DATA:
+                    mDataCleared = true;
+                    mCacheCleared = true;
                     processClearMsg(msg);
                     break;
                 case MSG_CLEAR_CACHE:
diff --git a/src/com/android/settings/applications/AppStorageSizesController.java b/src/com/android/settings/applications/AppStorageSizesController.java
index 23a3eb2..45ece6e 100644
--- a/src/com/android/settings/applications/AppStorageSizesController.java
+++ b/src/com/android/settings/applications/AppStorageSizesController.java
@@ -40,6 +40,7 @@
     private StorageStatsSource.AppStorageStats mLastResult;
     private boolean mLastResultFailed;
     private boolean mCachedCleared;
+    private boolean mDataCleared;
     private long mLastCodeSize = -1;
     private long mLastDataSize = -1;
     private long mLastCacheSize = -1;
@@ -69,7 +70,7 @@
             mTotalSize.setSummary(errorRes);
         } else {
             long codeSize = mLastResult.getCodeBytes();
-            long dataSize = mLastResult.getDataBytes();
+            long dataSize = mDataCleared ? 0 : mLastResult.getDataBytes();
             if (mLastCodeSize != codeSize) {
                 mLastCodeSize = codeSize;
                 mAppSize.setSummary(getSizeStr(context, codeSize));
@@ -78,7 +79,7 @@
                 mLastDataSize = dataSize;
                 mDataSize.setSummary(getSizeStr(context, dataSize));
             }
-            long cacheSize = mCachedCleared ? 0 : mLastResult.getCacheBytes();
+            long cacheSize = (mDataCleared || mCachedCleared) ? 0 : mLastResult.getCacheBytes();
             if (mLastCacheSize != cacheSize) {
                 mLastCacheSize = cacheSize;
                 mCacheSize.setSummary(getSizeStr(context, cacheSize));
@@ -111,6 +112,15 @@
     }
 
     /**
+     * Sets if we have cleared data and should zero the data bytes.
+     * When the data is cleared, the directory are recreated. Directories have some size, but are
+     * empty. We zero this out to best match user expectations.
+     */
+    public void setDataCleared(boolean isCleared) {
+        mDataCleared = isCleared;
+    }
+
+    /**
      * Returns the last result calculated, if it exists. If it does not, returns null.
      */
     public StorageStatsSource.AppStorageStats getLastResult() {
diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
index 282f613..5a2dcb2 100644
--- a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
@@ -17,14 +17,22 @@
 package com.android.settings.bluetooth;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+
 import com.android.settings.R;
 import com.android.settings.widget.SummaryUpdater;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Helper class that listeners to bluetooth callback and notify client when there is update in
@@ -98,7 +106,7 @@
         }
         switch (mConnectionState) {
             case BluetoothAdapter.STATE_CONNECTED:
-                return mContext.getString(R.string.bluetooth_connected_summary);
+                return getConnectedDeviceSummary();
             case BluetoothAdapter.STATE_CONNECTING:
                 return mContext.getString(R.string.bluetooth_connecting);
             case BluetoothAdapter.STATE_DISCONNECTING:
@@ -145,4 +153,27 @@
             : null;
     }
 
+    @VisibleForTesting
+    String getConnectedDeviceSummary() {
+        String deviceName = null;
+        int count = 0;
+        final Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
+        if (devices == null || devices.isEmpty()) {
+            return null;
+        }
+
+        for (BluetoothDevice device : devices) {
+            if (device.isConnected()) {
+                deviceName = device.getName();
+                count++;
+                if (count > 1) {
+                    break;
+                }
+            }
+        }
+
+        return count > 1 ? mContext.getString(R.string.bluetooth_connected_multiple_devices_summary)
+                : mContext.getString(R.string.bluetooth_connected_summary, deviceName);
+    }
+
 }
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index a73902a..eede37a 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -28,6 +28,7 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v14.preference.PreferenceFragment;
 import android.support.v7.preference.Preference;
+import android.text.TextUtils;
 import android.view.View;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -82,6 +83,8 @@
     ApplicationsState mState;
     @VisibleForTesting
     ApplicationsState.AppEntry mAppEntry;
+    @VisibleForTesting
+    BatteryUtils mBatteryUtils;
 
     private Preference mForegroundPreference;
     private Preference mBackgroundPreference;
@@ -137,6 +140,7 @@
                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE));
         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
         mPackageManager = activity.getPackageManager();
+        mBatteryUtils = BatteryUtils.getInstance(getContext());
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 48e2421..c8af4a0 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -15,14 +15,13 @@
  */
 package com.android.settings.fuelgauge;
 
-import android.annotation.IntDef;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.BatteryStats;
 import android.os.SystemClock;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
 import android.util.Log;
 
 import com.android.internal.os.BatterySipper;
@@ -64,7 +63,8 @@
         return sInstance;
     }
 
-    private BatteryUtils(Context context) {
+    @VisibleForTesting
+    BatteryUtils(Context context) {
         mPackageManager = context.getPackageManager();
         mPowerUsageFeatureProvider = FeatureFactory.getFactory(
                 context).getPowerUsageFeatureProvider(context);
diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
index bdadf4c..b124c81 100644
--- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java
+++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
@@ -79,11 +79,15 @@
         return mProgress.toString();
     }
 
-    public void setSubtitle(String subtitle) {
+    public void setSubtitle(CharSequence subtitle) {
         mProgress = subtitle;
         notifyChanged();
     }
 
+    public CharSequence getSubtitle() {
+        return mProgress;
+    }
+
     BatteryEntry getInfo() {
         return mInfo;
     }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
index b718f8f..28dfe1d 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
@@ -29,6 +29,7 @@
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
+import android.text.TextUtils;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.BatterySipper;
@@ -67,13 +68,15 @@
             UsageType.APP,
             UsageType.UNACCOUNTED,
             UsageType.OVERCOUNTED};
+
+    @VisibleForTesting
+    BatteryUtils mBatteryUtils;
     private BatteryHistoryPreference mHistPref;
     private PreferenceGroup mUsageListGroup;
     private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
     private PackageManager mPackageManager;
     private UserManager mUserManager;
     private Map<Integer, PowerUsageData> mBatteryDataMap;
-    private BatteryUtils mBatteryUtils;
 
     Handler mHandler = new Handler() {
 
@@ -270,8 +273,10 @@
             return;
         }
         if (usageData.usageList.size() <= 1) {
-            usageData.summary = getString(R.string.battery_used_for,
-                    Utils.formatElapsedTime(getContext(), usageData.totalUsageTimeMs, false));
+            CharSequence timeSequence = Utils.formatElapsedTime(getContext(),
+                    usageData.totalUsageTimeMs, false);
+            usageData.summary = TextUtils.expandTemplate(getText(R.string.battery_used_for),
+                    timeSequence);
         } else {
             BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
             BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
@@ -346,7 +351,7 @@
 
         @StringRes
         public int titleResId;
-        public String summary;
+        public CharSequence summary;
         public double percentage;
         public double totalPowerMah;
         public long totalUsageTimeMs;
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 078a9b3..1bc67af 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -418,15 +418,16 @@
         final TypedValue value = new TypedValue();
         context.getTheme().resolveAttribute(android.R.attr.colorControlNormal, value, true);
         final int colorControl = context.getColor(value.resourceId);
-        final String usedTime = context.getString(R.string.battery_used_for);
         final int dischargeAmount = USE_FAKE_DATA ? 5000
                 : stats != null ? stats.getDischargeAmount(mStatsType) : 0;
 
         final long runningTime = calculateRunningTimeBasedOnStatsType();
         updateScreenPreference();
         updateLastFullChargePreference(runningTime);
-        mAppListGroup.setTitle(getString(R.string.power_usage_list_summary,
-                Utils.formatElapsedTime(context, runningTime, false)));
+
+        final CharSequence timeSequence = Utils.formatElapsedTime(context, runningTime, false);
+        mAppListGroup.setTitle(
+                TextUtils.expandTemplate(getText(R.string.power_usage_list_summary), timeSequence));
 
         if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
             final List<BatterySipper> usageList = getCoalescedUsageList(
@@ -497,7 +498,7 @@
                     sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
                             BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
                 }
-                setUsageSummary(pref, usedTime, sipper.usageTimeMs);
+                setUsageSummary(pref, sipper.usageTimeMs);
                 if ((sipper.drainType != DrainType.APP
                         || sipper.uidObj.getUid() == Process.ROOT_UID)
                         && sipper.drainType != DrainType.USER) {
@@ -534,16 +535,17 @@
     void updateScreenPreference() {
         final BatterySipper sipper = findBatterySipperByType(
                 mStatsHelper.getUsageList(), DrainType.SCREEN);
-        final Context context = getContext();
         final long usageTimeMs = sipper != null ? sipper.usageTimeMs : 0;
 
-        mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(context, usageTimeMs, false));
+        mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(getContext(), usageTimeMs, false));
     }
 
     @VisibleForTesting
     void updateLastFullChargePreference(long timeMs) {
-        mLastFullChargePref.setSubtitle(getString(R.string.power_last_full_charge_summary,
-                Utils.formatElapsedTime(getContext(), timeMs, false)));
+        final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), timeMs, false);
+        mLastFullChargePref.setSubtitle(
+                TextUtils.expandTemplate(getText(R.string.power_last_full_charge_summary),
+                        timeSequence));
     }
 
     @VisibleForTesting
@@ -583,11 +585,13 @@
     }
 
     @VisibleForTesting
-    void setUsageSummary(Preference preference, String usedTimePrefix, long usageTimeMs) {
+    void setUsageSummary(Preference preference, long usageTimeMs) {
         // Only show summary when usage time is longer than one minute
         if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
-            preference.setSummary(String.format(usedTimePrefix,
-                    Utils.formatElapsedTime(getContext(), usageTimeMs, false)));
+            final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), usageTimeMs,
+                    false);
+            preference.setSummary(
+                    TextUtils.expandTemplate(getText(R.string.battery_used_for), timeSequence));
         }
     }
 
diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java
index c0e50fb..ccc5283 100644
--- a/src/com/android/settings/search2/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search2/DatabaseIndexingManager.java
@@ -515,7 +515,7 @@
             if (count > 0) {
                 while (cursor.moveToNext()) {
                     final int providerRank = cursor.getInt(COLUMN_INDEX_XML_RES_RANK);
-
+                    // TODO remove provider rank
                     final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);
 
                     final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME);
@@ -560,7 +560,7 @@
             if (count > 0) {
                 while (cursor.moveToNext()) {
                     final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK);
-
+                    // TODO Remove rank
                     final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE);
                     final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
                     final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF);
diff --git a/src/com/android/settings/search2/SearchFragment.java b/src/com/android/settings/search2/SearchFragment.java
index 8e1e1b4..c5db08d 100644
--- a/src/com/android/settings/search2/SearchFragment.java
+++ b/src/com/android/settings/search2/SearchFragment.java
@@ -168,6 +168,17 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+        if (TextUtils.isEmpty(mQuery)) {
+            return;
+        }
+        final String query = mQuery;
+        mQuery = "";
+        onQueryTextChange(query);
+    }
+
+    @Override
     public void onStop() {
         super.onStop();
         final Activity activity = getActivity();
@@ -206,7 +217,6 @@
         mResultClickCount = 0;
         mNeverEnteredQuery = false;
         mQuery = query;
-        mSearchAdapter.clearResults();
 
         if (isEmptyQuery) {
             final LoaderManager loaderManager = getLoaderManager();
@@ -252,7 +262,13 @@
             return;
         }
         final int resultCount = mSearchAdapter.displaySearchResults();
-        mNoResultsView.setVisibility(resultCount == 0 ? View.VISIBLE : View.GONE);
+
+        if (resultCount == 0) {
+            mNoResultsView.setVisibility(View.VISIBLE);
+        } else {
+            mNoResultsView.setVisibility(View.GONE);
+            mResultsRecyclerView.scrollToPosition(0);
+        }
         mSearchFeatureProvider.showFeedbackButton(this, getView());
     }
 
diff --git a/src/com/android/settings/search2/SearchResult.java b/src/com/android/settings/search2/SearchResult.java
index 6b27d89..ecc41b6 100644
--- a/src/com/android/settings/search2/SearchResult.java
+++ b/src/com/android/settings/search2/SearchResult.java
@@ -93,7 +93,7 @@
         icon = builder.mIcon;
         payload = builder.mResultPayload;
         viewType = payload.getType();
-        stableId = Objects.hash(title, summary, breadcrumbs, rank, icon, payload, viewType);
+        stableId = Objects.hash(title, summary, breadcrumbs, rank, viewType);
     }
 
     @Override
@@ -104,6 +104,22 @@
         return this.rank - searchResult.rank;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SearchResult)) {
+            return false;
+        }
+        return this.stableId == ((SearchResult) obj).stableId;
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) stableId;
+    }
+
     public static class Builder {
         protected CharSequence mTitle;
         protected CharSequence mSummary;
@@ -127,19 +143,19 @@
             return this;
         }
 
-        public Builder  addRank(int rank) {
+        public Builder addRank(int rank) {
             if (rank >= 0 && rank <= 9) {
                 mRank = rank;
             }
             return this;
         }
 
-        public Builder  addIcon(Drawable icon) {
+        public Builder addIcon(Drawable icon) {
             mIcon = icon;
             return this;
         }
 
-        public Builder  addPayload(ResultPayload payload) {
+        public Builder addPayload(ResultPayload payload) {
             mResultPayload = payload;
             return this;
         }
diff --git a/src/com/android/settings/search2/SearchResultDiffCallback.java b/src/com/android/settings/search2/SearchResultDiffCallback.java
new file mode 100644
index 0000000..9bd1bde
--- /dev/null
+++ b/src/com/android/settings/search2/SearchResultDiffCallback.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.search2;
+
+import android.support.v7.util.DiffUtil;
+
+import java.util.List;
+
+/**
+ * Callback for DiffUtil to elegantly update search data when the query changes.
+ */
+public class SearchResultDiffCallback extends DiffUtil.Callback {
+
+    private List<SearchResult> mOldList;
+    private List<SearchResult> mNewList;
+
+    public SearchResultDiffCallback(List<SearchResult> oldList, List<SearchResult> newList) {
+        mOldList = oldList;
+        mNewList = newList;
+    }
+
+    @Override
+    public int getOldListSize() {
+        return mOldList.size();
+    }
+
+    @Override
+    public int getNewListSize() {
+        return mNewList.size();
+    }
+
+    @Override
+    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+        return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
+    }
+
+    @Override
+    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+        return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
+    }
+}
diff --git a/src/com/android/settings/search2/SearchResultsAdapter.java b/src/com/android/settings/search2/SearchResultsAdapter.java
index 6ff68b1..60f2f7c 100644
--- a/src/com/android/settings/search2/SearchResultsAdapter.java
+++ b/src/com/android/settings/search2/SearchResultsAdapter.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 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.view.LayoutInflater;
@@ -37,8 +38,9 @@
 
 public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder> {
 
-    private final List<SearchResult> mSearchResults;
     private final SearchFragment mFragment;
+
+    private List<SearchResult> mSearchResults;
     private Map<String, List<? extends SearchResult>> mResultsMap;
 
     public SearchResultsAdapter(SearchFragment fragment) {
@@ -128,7 +130,7 @@
                 .get(InstalledAppResultLoader.class.getName());
         final int dbSize = (databaseResults != null) ? databaseResults.size() : 0;
         final int appSize = (installedAppResults != null) ? installedAppResults.size() : 0;
-        final List<SearchResult> results = new ArrayList<>(dbSize + appSize);
+        final List<SearchResult> newResults = new ArrayList<>(dbSize + appSize);
 
         int dbIndex = 0;
         int appIndex = 0;
@@ -136,23 +138,25 @@
 
         while (rank <= BOTTOM_RANK) {
             while ((dbIndex < dbSize) && (databaseResults.get(dbIndex).rank == rank)) {
-                results.add(databaseResults.get(dbIndex++));
+                newResults.add(databaseResults.get(dbIndex++));
             }
             while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
-                results.add(installedAppResults.get(appIndex++));
+                newResults.add(installedAppResults.get(appIndex++));
             }
             rank++;
         }
 
         while (dbIndex < dbSize) {
-            results.add(databaseResults.get(dbIndex++));
+            newResults.add(databaseResults.get(dbIndex++));
         }
         while (appIndex < appSize) {
-            results.add(installedAppResults.get(appIndex++));
+            newResults.add(installedAppResults.get(appIndex++));
         }
 
-        mSearchResults.addAll(results);
-        notifyDataSetChanged();
+        final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
+                new SearchResultDiffCallback(mSearchResults, newResults), false /* detectMoves */);
+        mSearchResults = newResults;
+        diffResult.dispatchUpdatesTo(this);
 
         return mSearchResults.size();
     }
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index 8b7605c..8dfd32a 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -19,7 +19,10 @@
 import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
 import android.text.format.DateUtils;
+import android.text.style.TtsSpan;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -35,10 +38,14 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class UtilsTest {
 
+    private static final String TIME_DESCRIPTION = "1 day 20 hours 30 minutes";
     private Context mContext;
-    @Mock private WifiManager wifiManager;
-    @Mock private Network network;
-    @Mock private ConnectivityManager connectivityManager;
+    @Mock
+    private WifiManager wifiManager;
+    @Mock
+    private Network network;
+    @Mock
+    private ConnectivityManager connectivityManager;
 
     @Before
     public void setUp() {
@@ -47,7 +54,7 @@
         mContext = spy(RuntimeEnvironment.application);
         when(mContext.getSystemService(WifiManager.class)).thenReturn(wifiManager);
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
-            .thenReturn(connectivityManager);
+                .thenReturn(connectivityManager);
     }
 
     @Test
@@ -89,7 +96,8 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "5m 0s";
 
-        assertThat(Utils.formatElapsedTime(mContext, testMillis, true)).isEqualTo(expectedTime);
+        assertThat(Utils.formatElapsedTime(mContext, testMillis, true).toString()).isEqualTo(
+                expectedTime);
     }
 
     @Test
@@ -97,7 +105,8 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "5m";
 
-        assertThat(Utils.formatElapsedTime(mContext, testMillis, false)).isEqualTo(expectedTime);
+        assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo(
+                expectedTime);
     }
 
     @Test
@@ -106,7 +115,23 @@
                 + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "2d 4h 15m";
 
-        assertThat(Utils.formatElapsedTime(mContext, testMillis, false)).isEqualTo(expectedTime);
+        assertThat(Utils.formatElapsedTime(mContext, testMillis, false).toString()).isEqualTo(
+                expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
+        final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
+
+        final CharSequence charSequence = Utils.formatElapsedTime(mContext, testMillis, false);
+        assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
+
+        final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
+        final TtsSpan[] ttsSpans = expectedString.getSpans(0, expectedString.length(),
+                TtsSpan.class);
+
+        assertThat(ttsSpans).asList().hasSize(1);
+        assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_MEASURE);
     }
 
     @Test
@@ -119,13 +144,13 @@
     }
 
     @Test
-    public void getInstallationStatus_notInstalled_shouldReturnUninstalled() {
+    public void testGetInstallationStatus_notInstalled_shouldReturnUninstalled() {
         assertThat(Utils.getInstallationStatus(new ApplicationInfo()))
                 .isEqualTo(R.string.not_installed);
     }
 
     @Test
-    public void getInstallationStatus_enabled_shouldReturnInstalled() {
+    public void testGetInstallationStatus_enabled_shouldReturnInstalled() {
         final ApplicationInfo info = new ApplicationInfo();
         info.flags = ApplicationInfo.FLAG_INSTALLED;
         info.enabled = true;
@@ -134,7 +159,7 @@
     }
 
     @Test
-    public void getInstallationStatus_disabled_shouldReturnDisabled() {
+    public void testGetInstallationStatus_disabled_shouldReturnDisabled() {
         final ApplicationInfo info = new ApplicationInfo();
         info.flags = ApplicationInfo.FLAG_INSTALLED;
         info.enabled = false;
diff --git a/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
index 127100f..36d78c1 100644
--- a/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppStorageSizesControllerTest.java
@@ -110,4 +110,22 @@
         assertThat(mDataPreference.getSummary()).isEqualTo("100B");
         assertThat(mTotalPreference.getSummary()).isEqualTo("101B");
     }
+
+    @Test
+    public void fakeDataFlagSetsDataAndCacheToZero() {
+        AppStorageStats result = mock(AppStorageStats.class);
+        when(result.getCodeBytes()).thenReturn(1L);
+        when(result.getCacheBytes()).thenReturn(10L);
+        when(result.getDataBytes()).thenReturn(100L);
+        when(result.getTotalBytes()).thenReturn(111L);
+
+        mController.setResult(result);
+        mController.setDataCleared(true);
+        mController.updateUi(mContext);
+
+        assertThat(mAppPreference.getSummary()).isEqualTo("1.00B");
+        assertThat(mCachePreference.getSummary()).isEqualTo("0.00B");
+        assertThat(mDataPreference.getSummary()).isEqualTo("0.00B");
+        assertThat(mTotalPreference.getSummary()).isEqualTo("1.00B");
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
index f372836..9c91ccf 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
@@ -87,6 +87,8 @@
     private BatteryStatsHelper mBatteryStatsHelper;
     @Mock
     private BatteryStats.Uid mUid;
+    @Mock
+    private PackageManager mPackageManager;
 
     private InstalledAppDetails mAppDetail;
     private Context mShadowContext;
@@ -94,14 +96,15 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mShadowContext = RuntimeEnvironment.application;
 
+        mShadowContext = RuntimeEnvironment.application;
         mAppDetail = spy(new InstalledAppDetails());
 
         mBatterySipper.drainType = BatterySipper.DrainType.IDLE;
         mBatterySipper.uidObj = mUid;
         doReturn(mActivity).when(mAppDetail).getActivity();
         doReturn(mShadowContext).when(mAppDetail).getContext();
+        doReturn(mPackageManager).when(mActivity).getPackageManager();
 
         // Default to not considering any apps to be instant (individual tests can override this).
         ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
index cc059ad..79daa0e 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryUpdaterTest.java
@@ -16,7 +16,10 @@
 
 package com.android.settings.bluetooth;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 
 import com.android.settings.R;
@@ -35,29 +38,38 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowBluetoothAdapter;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+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;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class BluetoothSummaryUpdaterTest {
+    private static final String DEVICE_NAME = "Nightshade";
+    private static final String DEVICE_KEYBOARD_NAME = "Bluetooth Keyboard";
 
     private Context mContext;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private LocalBluetoothManager mBluetoothManager;
     @Mock
     private LocalBluetoothAdapter mBtAdapter;
-
-    private BluetoothSummaryUpdater mSummaryUpdater;
+    @Mock
+    private BluetoothDevice mConnectedDevice;
+    @Mock
+    private BluetoothDevice mConnectedKeyBoardDevice;
     @Mock
     private SummaryListener mListener;
 
+    private BluetoothSummaryUpdater mSummaryUpdater;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -84,10 +96,12 @@
 
     @Test
     public void register_true_shouldSendSummaryChange() {
+        prepareConnectedDevice(false);
+
         mSummaryUpdater.register(true);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.bluetooth_connected_summary));
+                mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
     }
 
     @Test
@@ -100,11 +114,13 @@
 
     @Test
     public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() {
+        prepareConnectedDevice(false);
+
         mSummaryUpdater.register(true);
         mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.bluetooth_connected_summary));
+                mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
     }
 
     @Test
@@ -114,7 +130,7 @@
         mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.disconnected));
+                mContext.getString(R.string.disconnected));
     }
 
     @Test
@@ -123,26 +139,28 @@
         devices.add(mock(CachedBluetoothDevice.class));
         when(devices.get(0).isConnected()).thenReturn(true);
         when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy())
-            .thenReturn(devices);
+                .thenReturn(devices);
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
+        prepareConnectedDevice(false);
+
         mSummaryUpdater.register(true);
 
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_CONNECTED);
+                BluetoothAdapter.STATE_CONNECTED);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.bluetooth_connected_summary));
+                mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
     }
 
     @Test
     public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() {
         mSummaryUpdater.register(true);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_CONNECTED);
+                BluetoothAdapter.STATE_CONNECTED);
 
         verify(mListener).onSummaryChanged(
-            mContext.getString(R.string.disconnected));
+                mContext.getString(R.string.disconnected));
     }
 
     @Test
@@ -150,7 +168,7 @@
         mSummaryUpdater.register(true);
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_CONNECTING);
+                BluetoothAdapter.STATE_CONNECTING);
 
         verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connecting));
     }
@@ -160,11 +178,44 @@
         mSummaryUpdater.register(true);
         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTING);
         mSummaryUpdater.onConnectionStateChanged(null /* device */,
-            BluetoothAdapter.STATE_DISCONNECTING);
+                BluetoothAdapter.STATE_DISCONNECTING);
 
         verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disconnecting));
     }
 
+    @Test
+    public void getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary() {
+        prepareConnectedDevice(false);
+        final String expectedSummary = mContext.getString(R.string.bluetooth_connected_summary,
+                DEVICE_NAME);
+
+        assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
+    }
+
+    @Test
+    public void getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary() {
+        prepareConnectedDevice(true);
+        final String expectedSummary = mContext.getString(
+                R.string.bluetooth_connected_multiple_devices_summary);
+
+        assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
+    }
+
+    private void prepareConnectedDevice(boolean multipleDevices) {
+        final Set<BluetoothDevice> devices = new HashSet<>();
+        doReturn(DEVICE_NAME).when(mConnectedDevice).getName();
+        doReturn(true).when(mConnectedDevice).isConnected();
+        devices.add(mConnectedDevice);
+        if (multipleDevices) {
+            // Add one more device if we need to test multiple devices
+            doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName();
+            doReturn(true).when(mConnectedKeyBoardDevice).isConnected();
+            devices.add(mConnectedKeyBoardDevice);
+        }
+
+        doReturn(devices).when(mBtAdapter).getBondedDevices();
+    }
+
     private class SummaryListener implements OnSummaryChangeListener {
         String summary;
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index 15fec3c..4305dc7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -57,6 +57,8 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import org.robolectric.RuntimeEnvironment;
+
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
@@ -143,6 +145,7 @@
 
         mFragment.mHeaderPreference = mHeaderPreference;
         mFragment.mState = mState;
+        mFragment.mBatteryUtils = new BatteryUtils(RuntimeEnvironment.application);
         mAppEntry.info = mock(ApplicationInfo.class);
 
         mTestActivity = spy(new SettingsActivity());
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index e5383c8..52cfe4c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -33,6 +33,9 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import static android.os.BatteryStats.Uid.PROCESS_STATE_BACKGROUND;
 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND;
 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -47,9 +50,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.when;
-
-import java.util.ArrayList;
-import java.util.List;
+import static org.mockito.Mockito.spy;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -127,6 +128,8 @@
 
         mBatteryUtils = BatteryUtils.getInstance(RuntimeEnvironment.application);
         mBatteryUtils.mPowerUsageFeatureProvider = mProvider;
+
+        mBatteryUtils = spy(new BatteryUtils(RuntimeEnvironment.application));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
index 41b1c96..7a1284a 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.UserManager;
+import android.text.TextUtils;
 
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatterySipper.DrainType;
@@ -105,9 +106,12 @@
         when(mPowerUsageAdvanced.getContext()).thenReturn(mShadowContext);
         doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any(), any());
         doReturn(STUB_STRING).when(mPowerUsageAdvanced).getString(anyInt(), any());
+        doReturn(mShadowContext.getText(R.string.battery_used_for)).when(
+                mPowerUsageAdvanced).getText(R.string.battery_used_for);
         mPowerUsageAdvanced.setPackageManager(mPackageManager);
         mPowerUsageAdvanced.setPowerUsageFeatureProvider(mPowerUsageFeatureProvider);
         mPowerUsageAdvanced.setUserManager(mUserManager);
+        mPowerUsageAdvanced.mBatteryUtils = spy(new BatteryUtils(mShadowContext));
 
         mPowerUsageData = new PowerUsageData(UsageType.APP);
         mMaxBatterySipper.totalPowerMah = TYPE_BLUETOOTH_USAGE;
@@ -176,10 +180,12 @@
 
     @Test
     public void testUpdateUsageDataSummary_onlyOneApp_showUsageTime() {
+        final String expectedSummary = "Used for 0m";
         mPowerUsageData.usageList.add(mNormalBatterySipper);
+
         mPowerUsageAdvanced.updateUsageDataSummary(mPowerUsageData, TOTAL_POWER, DISCHARGE_AMOUNT);
 
-        verify(mPowerUsageAdvanced).getString(eq(R.string.battery_used_for), any());
+        assertThat(mPowerUsageData.summary.toString()).isEqualTo(expectedSummary);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 5611371..5cb60d2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -88,15 +88,16 @@
 public class PowerUsageSummaryTest {
     private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
     private static final String TIME_LEFT = "2h30min";
+    private static final String STUB_STRING = "stub_string";
     private static final int BATTERY_LEVEL = 55;
     private static final int UID = 123;
     private static final int POWER_MAH = 100;
     private static final long REMAINING_TIME_US = 100000;
-    private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 25000;
+    private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
     private static final long TIME_SINCE_LAST_FULL_CHARGE_US =
             TIME_SINCE_LAST_FULL_CHARGE_MS * 1000;
     private static final int DISCHARGE_AMOUNT = 100;
-    private static final long USAGE_TIME_MS = 10000;
+    private static final long USAGE_TIME_MS = 65 * 60 * 1000;
     private static final double TOTAL_POWER = 200;
     private static final double BATTERY_SCREEN_USAGE = 300;
     private static final double BATTERY_SYSTEM_USAGE = 600;
@@ -125,8 +126,6 @@
     @Mock
     private BatterySipper mCellBatterySipper;
     @Mock
-    private PowerGaugePreference mPreference;
-    @Mock
     private LayoutPreference mBatteryLayoutPref;
     @Mock
     private TextView mBatteryPercentText;
@@ -134,10 +133,6 @@
     private TextView mSummary1;
     @Mock
     private BatteryInfo mBatteryInfo;
-    @Mock
-    private PowerGaugePreference mScreenUsagePref;
-    @Mock
-    private PowerGaugePreference mLastFullChargePref;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private BatteryStatsHelper mBatteryHelper;
     @Mock
@@ -150,6 +145,9 @@
     private TestFragment mFragment;
     private FakeFeatureFactory mFeatureFactory;
     private BatteryMeterView mBatteryMeterView;
+    private PowerGaugePreference mPreference;
+    private PowerGaugePreference mScreenUsagePref;
+    private PowerGaugePreference mLastFullChargePref;
 
     @Before
     public void setUp() {
@@ -160,6 +158,9 @@
         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
         when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
 
+        mPreference = new PowerGaugePreference(mRealContext);
+        mScreenUsagePref = new PowerGaugePreference(mRealContext);
+        mLastFullChargePref = new PowerGaugePreference(mRealContext);
         mFragment = spy(new TestFragment(mContext));
         mFragment.initFeatureProvider();
         mBatteryMeterView = new BatteryMeterView(mRealContext);
@@ -202,6 +203,7 @@
         when(mBatteryHelper.getUsageList()).thenReturn(mUsageList);
         mFragment.mScreenUsagePref = mScreenUsagePref;
         mFragment.mLastFullChargePref = mLastFullChargePref;
+        mFragment.mBatteryUtils = spy(new BatteryUtils(mRealContext));
 
         mBatteryInfo.batteryLevel = BATTERY_LEVEL;
     }
@@ -306,16 +308,21 @@
     public void testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary() {
         final long usageTimeMs = 59 * DateUtils.SECOND_IN_MILLIS;
 
-        mFragment.setUsageSummary(mPreference, "", usageTimeMs);
-        verify(mPreference, never()).setSummary(anyString());
+        mFragment.setUsageSummary(mPreference, usageTimeMs);
+        assertThat(mPreference.getSummary()).isNull();
     }
 
     @Test
     public void testSetUsageSummary_timeMoreThanOneMinute_setSummary() {
         final long usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
+        doReturn(mRealContext.getText(R.string.battery_used_for)).when(mFragment).getText(
+                R.string.battery_used_for);
+        doReturn(mRealContext).when(mFragment).getContext();
+        final String expectedSummary = "Used for 2m";
 
-        mFragment.setUsageSummary(mPreference, "", usageTimeMs);
-        verify(mPreference).setSummary(anyString());
+        mFragment.setUsageSummary(mPreference, usageTimeMs);
+
+        assertThat(mPreference.getSummary().toString()).isEqualTo(expectedSummary);
     }
 
     @Test
@@ -382,32 +389,37 @@
 
     @Test
     public void testUpdateScreenPreference_showCorrectSummary() {
-        final String expectedUsedTime = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS, false);
         doReturn(mScreenBatterySipper).when(mFragment).findBatterySipperByType(any(), any());
         doReturn(mRealContext).when(mFragment).getContext();
+        final CharSequence expectedSummary = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS,
+                false);
 
         mFragment.updateScreenPreference();
 
-        verify(mScreenUsagePref).setSubtitle(expectedUsedTime);
+        assertThat(mScreenUsagePref.getSubtitle()).isEqualTo(expectedSummary);
     }
 
     @Test
     public void testUpdateLastFullChargePreference_showCorrectSummary() {
+        final CharSequence formattedString = mRealContext.getText(
+                R.string.power_last_full_charge_summary);
+        final CharSequence timeSequence = Utils.formatElapsedTime(mRealContext,
+                TIME_SINCE_LAST_FULL_CHARGE_MS, false);
+        final CharSequence expectedSummary = TextUtils.expandTemplate(
+                formattedString, timeSequence);
+        doReturn(formattedString).when(mFragment).getText(R.string.power_last_full_charge_summary);
         doReturn(mRealContext).when(mFragment).getContext();
-        final String expected = mRealContext.getString(R.string.power_last_full_charge_summary,
-                Utils.formatElapsedTime(mRealContext, TIME_SINCE_LAST_FULL_CHARGE_MS, false));
-        doReturn(expected).when(mFragment).getString(eq(R.string.power_last_full_charge_summary),
-                any());
 
         mFragment.updateLastFullChargePreference(TIME_SINCE_LAST_FULL_CHARGE_MS);
 
-        verify(mLastFullChargePref).setSubtitle(expected);
+        assertThat(mLastFullChargePref.getSubtitle()).isEqualTo(expectedSummary);
     }
 
     @Test
     public void testUpdatePreference_usageListEmpty_shouldNotCrash() {
         when(mBatteryHelper.getUsageList()).thenReturn(new ArrayList<BatterySipper>());
-        doReturn("").when(mFragment).getString(anyInt(), any());
+        doReturn(STUB_STRING).when(mFragment).getString(anyInt(), any());
+        doReturn(mRealContext).when(mFragment).getContext();
 
         // Should not crash when update
         mFragment.updateScreenPreference();
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
index 647d68c..645dfe5 100644
--- a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
@@ -63,6 +63,8 @@
     private Context mContext;
     private String mLoaderClassName;
 
+    private String[] TITLES = {"alpha", "bravo", "charlie", "appAlpha", "appBravo", "appCharlie"};
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -72,13 +74,13 @@
     }
 
     @Test
-    public void testNoResultsAdded_EmptyListReturned() {
+    public void testNoResultsAdded_emptyListReturned() {
         List<SearchResult> updatedResults = mAdapter.getSearchResults();
         assertThat(updatedResults).isEmpty();
     }
 
     @Test
-    public void testSingleSourceMerge_ExactCopyReturned() {
+    public void testSingleSourceMerge_exactCopyReturned() {
         ArrayList<SearchResult> intentResults = getIntentSampleResults();
         mAdapter.addSearchResults(intentResults, mLoaderClassName);
         mAdapter.displaySearchResults();
@@ -88,7 +90,7 @@
     }
 
     @Test
-    public void testCreateViewHolder_ReturnsIntentResult() {
+    public void testCreateViewHolder_returnsIntentResult() {
         ViewGroup group = new FrameLayout(mContext);
         SearchViewHolder view = mAdapter.onCreateViewHolder(group,
                 ResultPayload.PayloadType.INTENT);
@@ -96,7 +98,7 @@
     }
 
     @Test
-    public void testCreateViewHolder_ReturnsInlineSwitchResult() {
+    public void testCreateViewHolder_returnsInlineSwitchResult() {
         ViewGroup group = new FrameLayout(mContext);
         SearchViewHolder view = mAdapter.onCreateViewHolder(group,
                 ResultPayload.PayloadType.INLINE_SWITCH);
@@ -104,21 +106,54 @@
     }
 
     @Test
-    public void testEndToEndSearch_ProperResultsMerged() {
-        mAdapter.addSearchResults(getDummyAppResults(),
+    public void testEndToEndSearch_properResultsMerged_correctOrder() {
+        mAdapter.addSearchResults(getDummyAppResults(), InstalledAppResultLoader.class.getName());
+        mAdapter.addSearchResults(getDummyDbResults(), DatabaseResultLoader.class.getName());
+        mAdapter.displaySearchResults();
+
+        List<SearchResult> results = mAdapter.getSearchResults();
+        List<SearchResult> sortedDummyResults  = getSortedDummyResults();
+
+        assertThat(results).containsExactlyElementsIn(sortedDummyResults).inOrder();
+    }
+
+    @Test
+    public void testEndToEndSearch_addResults_resultsAddedInOrder() {
+        List<AppSearchResult> appResults = getDummyAppResults();
+        List<SearchResult> dbResults = getDummyDbResults();
+        // Add two individual items
+        mAdapter.addSearchResults(appResults.subList(0,1),
                 InstalledAppResultLoader.class.getName());
-        mAdapter.addSearchResults(getDummyDbResults(),
-                DatabaseResultLoader.class.getName());
+        mAdapter.addSearchResults(dbResults.subList(0,1), DatabaseResultLoader.class.getName());
+        mAdapter.displaySearchResults();
+        // Add super-set of items
+        mAdapter.addSearchResults(appResults, InstalledAppResultLoader.class.getName());
+        mAdapter.addSearchResults(dbResults, DatabaseResultLoader.class.getName());
+        mAdapter.displaySearchResults();
+
+        List<SearchResult> results = mAdapter.getSearchResults();
+        List<SearchResult> sortedDummyResults  = getSortedDummyResults();
+        assertThat(results).containsExactlyElementsIn(sortedDummyResults).inOrder();
+    }
+
+    @Test
+    public void testEndToEndSearch_removeResults_resultsAdded() {
+        List<AppSearchResult> appResults = getDummyAppResults();
+        List<SearchResult> dbResults = getDummyDbResults();
+        // Add list of items
+        mAdapter.addSearchResults(appResults, InstalledAppResultLoader.class.getName());
+        mAdapter.addSearchResults(dbResults, DatabaseResultLoader.class.getName());
+        mAdapter.displaySearchResults();
+        // Add subset of items
+        mAdapter.addSearchResults(appResults.subList(0,1),
+                InstalledAppResultLoader.class.getName());
+        mAdapter.addSearchResults(dbResults.subList(0,1), DatabaseResultLoader.class.getName());
         int count = mAdapter.displaySearchResults();
 
         List<SearchResult> results = mAdapter.getSearchResults();
-        assertThat(results.get(0).title).isEqualTo("alpha");
-        assertThat(results.get(1).title).isEqualTo("appAlpha");
-        assertThat(results.get(2).title).isEqualTo("appBravo");
-        assertThat(results.get(3).title).isEqualTo("bravo");
-        assertThat(results.get(4).title).isEqualTo("appCharlie");
-        assertThat(results.get(5).title).isEqualTo("Charlie");
-        assertThat(count).isEqualTo(6);
+        assertThat(results.get(0).title).isEqualTo(TITLES[0]);
+        assertThat(results.get(1).title).isEqualTo(TITLES[3]);
+        assertThat(count).isEqualTo(2);
     }
 
     private List<SearchResult> getDummyDbResults() {
@@ -127,15 +162,15 @@
         SearchResult.Builder builder = new SearchResult.Builder();
         builder.addPayload(payload);
 
-        builder.addTitle("alpha")
+        builder.addTitle(TITLES[0])
                 .addRank(1);
         results.add(builder.build());
 
-        builder.addTitle("bravo")
+        builder.addTitle(TITLES[1])
                 .addRank(3);
         results.add(builder.build());
 
-        builder.addTitle("Charlie")
+        builder.addTitle(TITLES[2])
                 .addRank(6);
         results.add(builder.build());
 
@@ -148,21 +183,35 @@
         AppSearchResult.Builder builder = new AppSearchResult.Builder();
         builder.addPayload(payload);
 
-        builder.addTitle("appAlpha")
+        builder.addTitle(TITLES[3])
                 .addRank(1);
         results.add(builder.build());
 
-        builder.addTitle("appBravo")
+        builder.addTitle(TITLES[4])
                 .addRank(2);
         results.add(builder.build());
 
-        builder.addTitle("appCharlie")
+        builder.addTitle(TITLES[5])
                 .addRank(4);
         results.add(builder.build());
 
         return results;
     }
 
+    private List<SearchResult> getSortedDummyResults() {
+        List<AppSearchResult> appResults = getDummyAppResults();
+        List<SearchResult> dbResults = getDummyDbResults();
+        List<SearchResult> sortedResults = new ArrayList<>(appResults.size() + dbResults.size());
+        sortedResults.add(dbResults.get(0)); // alpha
+        sortedResults.add(appResults.get(0)); // appAlpha
+        sortedResults.add(appResults.get(1)); // appBravo
+        sortedResults.add(dbResults.get(1)); // bravo
+        sortedResults.add(appResults.get(2)); // appCharlie
+        sortedResults.add(dbResults.get(2)); // Charlie
+
+        return sortedResults;
+    }
+
     private ArrayList<SearchResult> getIntentSampleResults() {
         ArrayList<SearchResult> sampleResults = new ArrayList<>();
         ArrayList<String> breadcrumbs = new ArrayList<>();