Add BatteryFix Slice

Change-Id: I504df1e9caadced71cc37f8be9207ab9fd26f9ab
Fixes: 114807643
Test: manual
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index cbfae2e..0ae001d 100644
--- a/color-check-baseline.xml
+++ b/color-check-baseline.xml
@@ -2,18 +2,6 @@
 <issues format="4">
 
     <issue
-        id="LintError"
-        severity="Error"
-        message="No `.class` files were found in project &quot;.&quot;, so none of the classfile based checks could be run. Does the project need to be built first?"
-        category="Lint"
-        priority="10"
-        summary="Lint Failure"
-        explanation="This issue type represents a problem running lint itself. Examples include failure to find bytecode for source files (which means certain detectors could not be run), parsing errors in lint configuration files, etc.&#xA;These errors are not errors in your own code, but they are shown to make it clear that some checks were not completed.">
-        <location
-            file="."/>
-    </issue>
-
-    <issue
         id="HardCodedColor"
         severity="Error"
         message="Avoid using hardcoded color"
@@ -2553,7 +2541,7 @@
         errorLine2="                                                                                                                                                                       ~~~~~~~~~~~~~~~~~~~">
         <location
             file="res/values-en-rXC/strings.xml"
-            line="2530"
+            line="2533"
             column="168"/>
     </issue>
 
@@ -2569,7 +2557,7 @@
         errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="res/values-en-rAU/strings.xml"
-            line="2531"
+            line="2534"
             column="64"/>
     </issue>
 
@@ -2585,7 +2573,7 @@
         errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="res/values-en-rCA/strings.xml"
-            line="2531"
+            line="2534"
             column="64"/>
     </issue>
 
@@ -2601,7 +2589,7 @@
         errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="res/values-en-rGB/strings.xml"
-            line="2531"
+            line="2534"
             column="64"/>
     </issue>
 
@@ -2617,7 +2605,7 @@
         errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="res/values-en-rIN/strings.xml"
-            line="2531"
+            line="2534"
             column="64"/>
     </issue>
 
@@ -2633,7 +2621,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~~~~~~~">
         <location
             file="res/values/strings.xml"
-            line="5886"
+            line="5902"
             column="36"/>
     </issue>
 
@@ -2665,7 +2653,7 @@
         errorLine2="                                           ^">
         <location
             file="res/values/styles.xml"
-            line="443"
+            line="448"
             column="44"/>
     </issue>
 
@@ -2681,7 +2669,7 @@
         errorLine2="                                           ^">
         <location
             file="res/values/styles.xml"
-            line="449"
+            line="454"
             column="44"/>
     </issue>
 
@@ -2697,7 +2685,7 @@
         errorLine2="                                           ^">
         <location
             file="res/values/styles.xml"
-            line="450"
+            line="455"
             column="44"/>
     </issue>
 
@@ -2761,7 +2749,7 @@
         errorLine2="                                              ^">
         <location
             file="res/values/themes.xml"
-            line="91"
+            line="90"
             column="47"/>
     </issue>
 
@@ -2777,7 +2765,7 @@
         errorLine2="                                              ^">
         <location
             file="res/values/themes.xml"
-            line="91"
+            line="90"
             column="47"/>
     </issue>
 
@@ -2793,7 +2781,7 @@
         errorLine2="                                       ^">
         <location
             file="res/values/themes.xml"
-            line="97"
+            line="96"
             column="40"/>
     </issue>
 
@@ -2809,7 +2797,7 @@
         errorLine2="                                       ^">
         <location
             file="res/values/themes.xml"
-            line="97"
+            line="96"
             column="40"/>
     </issue>
 
@@ -2825,7 +2813,7 @@
         errorLine2="                                            ^">
         <location
             file="res/values/themes.xml"
-            line="160"
+            line="159"
             column="45"/>
     </issue>
 
@@ -2841,7 +2829,7 @@
         errorLine2="                                                ^">
         <location
             file="res/values/themes.xml"
-            line="161"
+            line="160"
             column="49"/>
     </issue>
 
@@ -2857,7 +2845,7 @@
         errorLine2="                                            ^">
         <location
             file="res/values/themes.xml"
-            line="169"
+            line="168"
             column="45"/>
     </issue>
 
@@ -2873,7 +2861,7 @@
         errorLine2="                                                ^">
         <location
             file="res/values/themes.xml"
-            line="170"
+            line="169"
             column="49"/>
     </issue>
 
@@ -2885,6 +2873,54 @@
         priority="4"
         summary="Using hardcoded color"
         explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+        errorLine1="        &lt;item name=&quot;batteryGoodColor&quot;>@color/battery_good_color_light&lt;/item>"
+        errorLine2="                                      ^">
+        <location
+            file="res/values/themes.xml"
+            line="185"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="HardCodedColor"
+        severity="Error"
+        message="Avoid using hardcoded color"
+        category="Correctness"
+        priority="4"
+        summary="Using hardcoded color"
+        explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+        errorLine1="        &lt;item name=&quot;batteryMaybeColor&quot;>@color/battery_maybe_color_light&lt;/item>"
+        errorLine2="                                       ^">
+        <location
+            file="res/values/themes.xml"
+            line="186"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="HardCodedColor"
+        severity="Error"
+        message="Avoid using hardcoded color"
+        category="Correctness"
+        priority="4"
+        summary="Using hardcoded color"
+        explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+        errorLine1="        &lt;item name=&quot;batteryBadColor&quot;>@color/battery_bad_color_light&lt;/item>"
+        errorLine2="                                     ^">
+        <location
+            file="res/values/themes.xml"
+            line="187"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="HardCodedColor"
+        severity="Error"
+        message="Avoid using hardcoded color"
+        category="Correctness"
+        priority="4"
+        summary="Using hardcoded color"
+        explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
         errorLine1="        &lt;item name=&quot;wifi_signal_color&quot;>@color/setup_wizard_wifi_color_dark&lt;/item>"
         errorLine2="                                       ^">
         <location
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 4f44a36..b280482 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -180,6 +180,11 @@
 
         <!-- action bar, needed for search bar icon tinting -->
         <item name="android:actionBarTheme">@*android:style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
+
+        <!-- For battery status icons in -->
+        <item name="batteryGoodColor">@color/battery_good_color_light</item>
+        <item name="batteryMaybeColor">@color/battery_maybe_color_light</item>
+        <item name="batteryBadColor">@color/battery_bad_color_light</item>
     </style>
 
     <style name="Theme.Settings.Home" parent="Theme.Settings.HomeBase">
diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
index b5c39a1..0266689 100644
--- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
@@ -26,6 +26,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.Utils;
+import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -115,6 +116,6 @@
                 mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_SAVER);
             }
         }
+        BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext);
     }
-
 }
\ No newline at end of file
diff --git a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
index f58ec74..db3fddc 100644
--- a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 
 import com.android.settings.homepage.contextualcards.deviceinfo.BatterySlice;
+import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
 import com.android.settings.homepage.contextualcards.slices.ConnectedDeviceSlice;
 import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
 import com.android.settings.intelligence.ContextualCardProto.ContextualCard;
@@ -61,11 +62,18 @@
                         .setCardName(LowStorageSlice.PATH_LOW_STORAGE)
                         .setCardCategory(ContextualCard.Category.IMPORTANT)
                         .build();
+        final ContextualCard batteryFixCard =
+                ContextualCard.newBuilder()
+                        .setSliceUri(BatteryFixSlice.BATTERY_FIX_URI.toString())
+                        .setCardName(BatteryFixSlice.PATH_BATTERY_FIX)
+                        .setCardCategory(ContextualCard.Category.IMPORTANT)
+                        .build();
         final ContextualCardList cards = ContextualCardList.newBuilder()
                 .addCard(wifiCard)
                 .addCard(batteryInfoCard)
                 .addCard(connectedDeviceCard)
                 .addCard(lowStorageCard)
+                .addCard(batteryFixCard)
                 .build();
 
         return cards;
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
new file mode 100644
index 0000000..23ff172
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage.contextualcards.slices;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
+import androidx.slice.builders.SliceAction;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.R;
+import com.android.settings.SubSettings;
+import com.android.settings.Utils;
+import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
+import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.slices.CustomSliceable;
+import com.android.settings.slices.SettingsSliceProvider;
+import com.android.settings.slices.SliceBackgroundWorker;
+import com.android.settings.slices.SliceBuilderUtils;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.List;
+
+public class BatteryFixSlice implements CustomSliceable {
+
+    /**
+     * Unique name of Battery Fix Slice.
+     */
+    public static final String PATH_BATTERY_FIX = "battery_fix";
+
+    /**
+     * Uri for Battery Fix Slice.
+     */
+    public static final Uri BATTERY_FIX_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendPath(PATH_BATTERY_FIX)
+            .build();
+
+    @VisibleForTesting
+    static final String PREFS = "battery_fix_prefs";
+    @VisibleForTesting
+    static final String KEY_CURRENT_TIPS_TYPE = "current_tip_type";
+
+    private static final String TAG = "BatteryFixSlice";
+
+    private final Context mContext;
+
+    public BatteryFixSlice(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public Uri getUri() {
+        return BATTERY_FIX_URI;
+    }
+
+    /**
+     * Return a Slice bound to {@link #BATTERY_FIX_URI}.
+     */
+    @Override
+    public Slice getSlice() {
+        IconCompat icon;
+        SliceAction primaryAction;
+        Slice slice = null;
+
+        // TipType.SUMMARY is battery good
+        if (readBatteryTipAvailabilityCache(mContext) == BatteryTip.TipType.SUMMARY) {
+            return null;
+        }
+
+        final List<BatteryTip> batteryTips = SliceBackgroundWorker.getInstance(mContext,
+                this).getResults();
+
+        if (batteryTips != null) {
+            for (BatteryTip batteryTip : batteryTips) {
+                if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) {
+                    icon = IconCompat.createWithResource(mContext, batteryTip.getIconId());
+                    primaryAction = new SliceAction(getPrimaryAction(),
+                            icon,
+                            batteryTip.getTitle(mContext));
+                    slice = new ListBuilder(mContext, BATTERY_FIX_URI, ListBuilder.INFINITY)
+                            .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
+                            .addRow(new RowBuilder()
+                                    .setTitle(batteryTip.getTitle(mContext))
+                                    .setSubtitle(batteryTip.getSummary(mContext))
+                                    .setPrimaryAction(primaryAction)
+                                    .addEndItem(icon, ListBuilder.ICON_IMAGE))
+                            .build();
+                    break;
+                }
+            }
+        } else {
+            icon = IconCompat.createWithResource(mContext,
+                    R.drawable.ic_battery_status_good_24dp);
+            final String title = mContext.getString(R.string.power_usage_summary_title);
+            primaryAction = new SliceAction(getPrimaryAction(), icon, title);
+            slice = new ListBuilder(mContext, BATTERY_FIX_URI, ListBuilder.INFINITY)
+                    .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
+                    .addRow(new RowBuilder()
+                            .setTitle(title)
+                            .setPrimaryAction(primaryAction)
+                            .addEndItem(icon, ListBuilder.ICON_IMAGE))
+                    .build();
+        }
+        return slice;
+    }
+
+    @Override
+    public Intent getIntent() {
+        final String screenTitle = mContext.getText(R.string.power_usage_summary_title)
+                .toString();
+        final Uri contentUri = new Uri.Builder().appendPath(PATH_BATTERY_FIX).build();
+
+        return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+                PowerUsageSummary.class.getName(), PATH_BATTERY_FIX,
+                screenTitle,
+                MetricsProto.MetricsEvent.SLICE)
+                .setClassName(mContext.getPackageName(), SubSettings.class.getName())
+                .setData(contentUri);
+    }
+
+    @Override
+    public void onNotifyChange(Intent intent) {
+    }
+
+    @Override
+    public Class getBackgroundWorkerClass() {
+        return BatteryTipWorker.class;
+    }
+
+    private PendingIntent getPrimaryAction() {
+        final Intent intent = getIntent();
+        return PendingIntent.getActivity(mContext, 0  /* requestCode */, intent, 0  /* flags */);
+    }
+
+    // TODO(b/114807643): we should find a better way to get current battery tip type quickly
+    // Now we save battery tip type to shared preference when battery level changes
+    public static void updateBatteryTipAvailabilityCache(Context context) {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            refreshBatteryTips(context);
+        });
+    }
+
+    @VisibleForTesting
+    static int readBatteryTipAvailabilityCache(Context context) {
+        final SharedPreferences prefs = context.getSharedPreferences(PREFS, MODE_PRIVATE);
+        return prefs.getInt(KEY_CURRENT_TIPS_TYPE, BatteryTip.TipType.SUMMARY);
+    }
+
+    @WorkerThread
+    private static List<BatteryTip> refreshBatteryTips(Context context) {
+        final BatteryStatsHelperLoader statsLoader = new BatteryStatsHelperLoader(context);
+        final BatteryStatsHelper statsHelper = statsLoader.loadInBackground();
+        final BatteryTipLoader loader = new BatteryTipLoader(context, statsHelper);
+        final List<BatteryTip> batteryTips = loader.loadInBackground();
+        for (BatteryTip batteryTip : batteryTips) {
+            if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) {
+                SharedPreferences.Editor editor = context.getSharedPreferences(PREFS,
+                        MODE_PRIVATE).edit();
+                editor.putInt(KEY_CURRENT_TIPS_TYPE, batteryTip.getType());
+                editor.apply();
+                break;
+            }
+        }
+        return batteryTips;
+    }
+
+    public static class BatteryTipWorker extends SliceBackgroundWorker<BatteryTip> {
+
+        private final Context mContext;
+
+        public BatteryTipWorker(Context context, Uri uri) {
+            super(context, uri);
+            mContext = context;
+        }
+
+        @Override
+        protected void onSlicePinned() {
+            ThreadUtils.postOnBackgroundThread(() -> {
+                final List<BatteryTip> batteryTips = refreshBatteryTips(mContext);
+                updateResults(batteryTips);
+            });
+        }
+
+        @Override
+        protected void onSliceUnpinned() {
+        }
+
+        @Override
+        public void close() {
+        }
+    }
+}
diff --git a/src/com/android/settings/slices/CustomSliceManager.java b/src/com/android/settings/slices/CustomSliceManager.java
index 5b25498..99e22a1 100644
--- a/src/com/android/settings/slices/CustomSliceManager.java
+++ b/src/com/android/settings/slices/CustomSliceManager.java
@@ -20,10 +20,13 @@
 import android.net.Uri;
 import android.util.ArrayMap;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.settings.homepage.contextualcards.deviceinfo.BatterySlice;
 import com.android.settings.homepage.contextualcards.deviceinfo.DataUsageSlice;
 import com.android.settings.homepage.contextualcards.deviceinfo.DeviceInfoSlice;
 import com.android.settings.homepage.contextualcards.deviceinfo.StorageSlice;
+import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
 import com.android.settings.homepage.contextualcards.slices.ConnectedDeviceSlice;
 import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
 import com.android.settings.wifi.WifiSlice;
@@ -34,13 +37,11 @@
 /**
  * Manages custom {@link androidx.slice.Slice Slices}, which are all Slices not backed by
  * preferences.
- * <p>
- *     By default, all Slices in Settings should be built by a
- * </p>
  */
 public class CustomSliceManager {
 
-    protected final Map<Uri, Class<? extends CustomSliceable>> mUriMap;
+    @VisibleForTesting
+    final Map<Uri, Class<? extends CustomSliceable>> mUriMap;
 
     private final Context mContext;
     private final Map<Uri, CustomSliceable> mSliceableCache;
@@ -107,5 +108,6 @@
         mUriMap.put(BatterySlice.BATTERY_CARD_URI, BatterySlice.class);
         mUriMap.put(ConnectedDeviceSlice.CONNECTED_DEVICE_URI, ConnectedDeviceSlice.class);
         mUriMap.put(LowStorageSlice.LOW_STORAGE_URI, LowStorageSlice.class);
+        mUriMap.put(BatteryFixSlice.BATTERY_FIX_URI, BatteryFixSlice.class);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
index e074440..182c8bf 100644
--- a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
@@ -40,6 +40,7 @@
 import android.widget.ImageView;
 
 import com.android.settings.homepage.SettingsHomepageActivity;
+import com.android.settings.homepage.contextualcards.slices.BatteryFixSliceTest;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
@@ -99,7 +100,11 @@
     }
 
     @Test
-    @Config(qualifiers = "mcc999")
+    @Config(qualifiers = "mcc999",
+            shadows = {
+                    BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
+                    BatteryFixSliceTest.ShadowBatteryTipLoader.class
+            })
     public void onStart_useMockAvatarViewMixin_shouldBeExecuted() {
         final AvatarViewMixin mockAvatar = spy(new AvatarViewMixin(mActivity, mImageView));
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
index fb34640..02a6761 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
@@ -33,6 +33,7 @@
 import android.os.PowerManager;
 
 import com.android.settings.Utils;
+import com.android.settings.homepage.contextualcards.slices.BatteryFixSliceTest;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
@@ -42,6 +43,11 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
 @RunWith(SettingsRobolectricTestRunner.class)
 public class BatteryBroadcastReceiverTest {
 
@@ -74,6 +80,10 @@
     }
 
     @Test
+    @Config(shadows = {
+            BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
+            BatteryFixSliceTest.ShadowBatteryTipLoader.class
+    })
     public void testOnReceive_batteryLevelChanged_dataUpdated() {
         mBatteryBroadcastReceiver.onReceive(mContext, mChargingIntent);
 
@@ -85,6 +95,10 @@
     }
 
     @Test
+    @Config(shadows = {
+            BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
+            BatteryFixSliceTest.ShadowBatteryTipLoader.class
+    })
     public void testOnReceive_powerSaveModeChanged_listenerInvoked() {
         mBatteryBroadcastReceiver.onReceive(mContext,
                 new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
@@ -93,6 +107,10 @@
     }
 
     @Test
+    @Config(shadows = {
+            BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
+            BatteryFixSliceTest.ShadowBatteryTipLoader.class
+    })
     public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() {
         final String batteryLevel = Utils.getBatteryPercentage(mChargingIntent);
         final String batteryStatus =
@@ -108,6 +126,10 @@
     }
 
     @Test
+    @Config(shadows = {
+            BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
+            BatteryFixSliceTest.ShadowBatteryTipLoader.class
+    })
     public void testRegister_updateBatteryStatus() {
         doReturn(mChargingIntent).when(mContext).registerReceiver(any(), any());
 
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
new file mode 100644
index 0000000..1df5277
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage.contextualcards.slices;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import static com.android.settings.homepage.contextualcards.slices.BatteryFixSlice.KEY_CURRENT_TIPS_TYPE;
+import static com.android.settings.homepage.contextualcards.slices.BatteryFixSlice.PREFS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.slice.SliceProvider;
+import androidx.slice.widget.SliceLiveData;
+
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
+import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.EarlyWarningTip;
+import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class BatteryFixSliceTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+
+        // Set-up specs for SliceMetadata.
+        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowBatteryTipLoader.reset();
+    }
+
+    @Test
+    public void readBatteryTipfromPref_readCorrectValue() {
+        int target = 111;
+        final SharedPreferences.Editor editor = mContext.getSharedPreferences(PREFS,
+                MODE_PRIVATE).edit();
+        editor.putInt(KEY_CURRENT_TIPS_TYPE, target);
+
+        editor.commit();
+
+        assertThat(BatteryFixSlice.readBatteryTipAvailabilityCache(mContext)).isEqualTo(target);
+    }
+
+    @Test
+    @Config(shadows = {
+            ShadowBatteryStatsHelperLoader.class,
+            ShadowBatteryTipLoader.class
+    })
+    public void updateBatteryTipAvailabilityCache_writeCorrectValue() {
+        final List<BatteryTip> tips = new ArrayList<>();
+        tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, ""));
+        tips.add(new EarlyWarningTip(BatteryTip.StateType.HANDLED, false));
+        ShadowBatteryTipLoader.setBatteryTips(tips);
+
+        BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext);
+
+        assertThat(BatteryFixSlice.readBatteryTipAvailabilityCache(mContext)).isEqualTo(
+                BatteryTip.TipType.BATTERY_SAVER);
+    }
+
+    @Implements(BatteryStatsHelperLoader.class)
+    public static class ShadowBatteryStatsHelperLoader {
+
+        @Implementation
+        protected BatteryStatsHelper loadInBackground() {
+            return null;
+        }
+    }
+
+    @Implements(BatteryTipLoader.class)
+    public static class ShadowBatteryTipLoader {
+
+        private static List<BatteryTip> sBatteryTips = new ArrayList<>();
+
+        @Resetter
+        public static void reset() {
+            sBatteryTips = new ArrayList<>();
+        }
+
+        @Implementation
+        protected List<BatteryTip> loadInBackground() {
+            return sBatteryTips;
+        }
+
+        public static void setBatteryTips(List<BatteryTip> tips) {
+            sBatteryTips = tips;
+        }
+    }
+}