Add BatteryTipDialogFragment.

This DialogFragment handles all the tip related dialogs. This cl makes:
1. All the tips parcelable.
2. Add dialog for HighUsageTip. It also need adapter to populate app
list in dialog.
3. Add and update tests

Bug: 70570352
Test: RunSettingsRoboTests
Change-Id: Ie4c986172cfc73d8746abc7457d966c8600c6145
diff --git a/res/layout/app_high_usage_item.xml b/res/layout/app_high_usage_item.xml
new file mode 100755
index 0000000..473315f
--- /dev/null
+++ b/res/layout/app_high_usage_item.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="4dp"
+    android:paddingStart="?android:dialogPreferredPadding"
+    android:paddingEnd="?android:dialogPreferredPadding"
+    android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/app_icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_gravity="center_vertical"/>
+    <TextView
+        android:id="@+id/app_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="24dp"
+        android:textColor="?android:textColorPrimary"
+        android:paddingEnd="7dp"/>
+    <TextView
+        android:id="@+id/app_screen_time"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:paddingEnd="16dp"
+        android:textAlignment="viewEnd"
+        android:textColor="?android:textColorPrimary"/>
+</LinearLayout>
diff --git a/res/layout/recycler_view.xml b/res/layout/recycler_view.xml
new file mode 100644
index 0000000..a7dabe5
--- /dev/null
+++ b/res/layout/recycler_view.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<android.support.v7.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/recycler_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingTop="8dp"
+    android:scrollbars="vertical"/>
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index cd64799..ad95121 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -94,6 +94,7 @@
 import android.text.format.DateUtils;
 import android.text.style.TtsSpan;
 import android.util.ArraySet;
+import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
@@ -1382,4 +1383,18 @@
         }
         return new BitmapDrawable(null, bitmap);
     }
+
+    /**
+     * Get the {@link Drawable} that represents the app icon
+     */
+    public static Drawable getBadgedIcon(IconDrawableFactory iconDrawableFactory,
+            PackageManager packageManager, String packageName, int userId) {
+        try {
+            final ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName,
+                    PackageManager.GET_META_DATA);
+            return iconDrawableFactory.getBadgedIcon(appInfo, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            return packageManager.getDefaultActivityIcon();
+        }
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java b/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java
index 0d73511..143733d 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java
@@ -31,6 +31,7 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.Utils;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
@@ -151,12 +152,6 @@
 
     @VisibleForTesting
     Drawable getBadgedIcon(String packageName, int userId) {
-        try {
-            final ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName,
-                    PackageManager.GET_META_DATA);
-            return mIconDrawableFactory.getBadgedIcon(appInfo, userId);
-        } catch (PackageManager.NameNotFoundException e) {
-            return mPackageManager.getDefaultActivityIcon();
-        }
+        return Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, packageName, userId);
     }
 }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 507043f..205ac0b 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -266,7 +266,7 @@
                 KEY_APP_LIST, lifecycle, activity, this);
         controllers.add(mBatteryAppListPreferenceController);
         mBatteryTipPreferenceController = new BatteryTipPreferenceController(context,
-                KEY_BATTERY_TIP, this);
+                KEY_BATTERY_TIP, this, this);
         controllers.add(mBatteryTipPreferenceController);
         controllers.add(new BatterySaverController(context, getLifecycle()));
         controllers.add(new BatteryPercentagePreferenceController(context));
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
new file mode 100644
index 0000000..3e091b3
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -0,0 +1,104 @@
+/*
+ * 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.fuelgauge.batterytip;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController.BatteryTipListener;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
+
+/**
+ * Dialog Fragment to show action dialog for each anomaly
+ */
+public class BatteryTipDialogFragment extends InstrumentedDialogFragment implements
+        DialogInterface.OnClickListener {
+
+    private static final String ARG_BATTERY_TIP = "battery_tip";
+
+    @VisibleForTesting
+    BatteryTip mBatteryTip;
+
+    public static BatteryTipDialogFragment newInstance(BatteryTip batteryTip) {
+        BatteryTipDialogFragment dialogFragment = new BatteryTipDialogFragment();
+
+        Bundle args = new Bundle(1);
+        args.putParcelable(ARG_BATTERY_TIP, batteryTip);
+        dialogFragment.setArguments(args);
+
+        return dialogFragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Bundle bundle = getArguments();
+        final Context context = getContext();
+
+        mBatteryTip = bundle.getParcelable(ARG_BATTERY_TIP);
+
+        switch (mBatteryTip.getType()) {
+            case BatteryTip.TipType.SUMMARY:
+            case BatteryTip.TipType.LOW_BATTERY:
+                //TODO(b/70570352): add dialog
+                return null;
+            case BatteryTip.TipType.HIGH_DEVICE_USAGE:
+                final HighUsageTip highUsageTip = (HighUsageTip) mBatteryTip;
+                final RecyclerView view = (RecyclerView) LayoutInflater.from(context).inflate(
+                        R.layout.recycler_view,
+                        null);
+                view.setLayoutManager(new LinearLayoutManager(context));
+                view.setAdapter(new HighUsageAdapter(context,
+                        highUsageTip.getHighUsageAppList()));
+
+                return new AlertDialog.Builder(context)
+                        .setMessage(getString(R.string.battery_tip_dialog_message,
+                                highUsageTip.getScreenTimeMs()))
+                        .setView(view)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .create();
+            default:
+                throw new IllegalArgumentException("unknown type " + mBatteryTip.getType());
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        //TODO(b/70570352): add correct metric id
+        return 0;
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        final BatteryTipListener lsn = (BatteryTipListener) getTargetFragment();
+        if (lsn == null) {
+            return;
+        }
+        mBatteryTip.action();
+        lsn.onBatteryTipHandled(mBatteryTip);
+    }
+
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
index f611405..9aa70c5 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.PreferenceFragment;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
 import android.support.v7.preference.PreferenceScreen;
@@ -34,6 +35,9 @@
  * Controller in charge of the battery tip group
  */
 public class BatteryTipPreferenceController extends BasePreferenceController {
+    private static final String TAG = "BatteryTipPreferenceController";
+    private static final int REQUEST_ANOMALY_ACTION = 0;
+
     private BatteryTipListener mBatteryTipListener;
     private List<BatteryTip> mBatteryTips;
     private Map<String, BatteryTip> mBatteryTipMap;
@@ -41,16 +45,18 @@
     PreferenceGroup mPreferenceGroup;
     @VisibleForTesting
     Context mPrefContext;
+    PreferenceFragment mFragment;
 
     public BatteryTipPreferenceController(Context context, String preferenceKey) {
-        this(context, preferenceKey, null);
+        this(context, preferenceKey, null, null);
     }
 
     public BatteryTipPreferenceController(Context context, String preferenceKey,
-            BatteryTipListener batteryTipListener) {
+            PreferenceFragment fragment, BatteryTipListener batteryTipListener) {
         super(context, preferenceKey);
         mBatteryTipListener = batteryTipListener;
         mBatteryTipMap = new HashMap<>();
+        mFragment = fragment;
     }
 
     @Override
@@ -96,7 +102,10 @@
         final BatteryTip batteryTip = mBatteryTipMap.get(preference.getKey());
         if (batteryTip != null) {
             if (batteryTip.shouldShowDialog()) {
-                // build and show the dialog
+                BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance(
+                        batteryTip);
+                dialogFragment.setTargetFragment(mFragment, REQUEST_ANOMALY_ACTION);
+                dialogFragment.show(mFragment.getFragmentManager(), TAG);
             } else {
                 batteryTip.action();
                 if (mBatteryTipListener != null) {
diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
new file mode 100644
index 0000000..8b74394
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
@@ -0,0 +1,87 @@
+/*
+ * 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.fuelgauge.batterytip;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.support.v7.widget.RecyclerView;
+import android.util.IconDrawableFactory;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+
+import java.util.List;
+
+/**
+ * Adapter for the high usage app list
+ */
+public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.ViewHolder> {
+    private final Context mContext;
+    private final IconDrawableFactory mIconDrawableFactory;
+    private final PackageManager mPackageManager;
+    private final List<HighUsageApp> mHighUsageAppList;
+
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        public View view;
+        public ImageView appIcon;
+        public TextView appName;
+        public TextView appTime;
+
+        public ViewHolder(View v) {
+            super(v);
+            view = v;
+            appIcon = v.findViewById(R.id.app_icon);
+            appName = v.findViewById(R.id.app_name);
+            appTime = v.findViewById(R.id.app_screen_time);
+        }
+    }
+
+    public HighUsageAdapter(Context context, List<HighUsageApp> highUsageAppList) {
+        mContext = context;
+        mHighUsageAppList = highUsageAppList;
+        mIconDrawableFactory = IconDrawableFactory.newInstance(context);
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        final View view = LayoutInflater.from(mContext).inflate(R.layout.app_high_usage_item,
+                parent, false);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        final HighUsageApp app = mHighUsageAppList.get(position);
+        holder.appIcon.setImageDrawable(
+                Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, app.packageName,
+                        UserHandle.myUserId()));
+        holder.appName.setText(Utils.getApplicationLabel(mContext, app.packageName));
+        holder.appTime.setText(Utils.formatElapsedTime(mContext, app.screenOnTimeMs, false));
+    }
+
+    @Override
+    public int getItemCount() {
+        return mHighUsageAppList.size();
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageApp.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageApp.java
new file mode 100644
index 0000000..f75ecf0
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageApp.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batterytip;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class representing app with high screen usage
+ */
+public class HighUsageApp implements Comparable<HighUsageApp>, Parcelable {
+    public final String packageName;
+    public final long screenOnTimeMs;
+
+    public HighUsageApp(String packageName, long screenOnTimeMs) {
+        this.packageName = packageName;
+        this.screenOnTimeMs = screenOnTimeMs;
+    }
+
+    private HighUsageApp(Parcel in) {
+        packageName = in.readString();
+        screenOnTimeMs = in.readLong();
+    }
+
+    @Override
+    public int compareTo(HighUsageApp o) {
+        return Long.compare(screenOnTimeMs, o.screenOnTimeMs);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(packageName);
+        dest.writeLong(screenOnTimeMs);
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public HighUsageApp createFromParcel(Parcel in) {
+            return new HighUsageApp(in);
+        }
+
+        public HighUsageApp[] newArray(int size) {
+            return new HighUsageApp[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
index 5c2ecad..237f430 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
@@ -26,6 +26,7 @@
 import com.android.settings.Utils;
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.HighUsageApp;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
 import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
@@ -41,7 +42,7 @@
 public class HighUsageDetector implements BatteryTipDetector {
     private BatteryTipPolicy mPolicy;
     private BatteryStatsHelper mBatteryStatsHelper;
-    private List<HighUsageTip.HighUsageApp> mHighUsageAppList;
+    private List<HighUsageApp> mHighUsageAppList;
     private Context mContext;
     @VisibleForTesting
     BatteryUtils mBatteryUtils;
@@ -67,7 +68,7 @@
                     final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
                             BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
                             BatteryStats.STATS_SINCE_CHARGED);
-                    mHighUsageAppList.add(new HighUsageTip.HighUsageApp(
+                    mHighUsageAppList.add(new HighUsageApp(
                             mBatteryUtils.getPackageName(batterySipper.getUid()),
                             foregroundTimeMs));
                 }
@@ -78,7 +79,6 @@
             Collections.sort(mHighUsageAppList, Collections.reverseOrder());
         }
 
-        return new HighUsageTip(Utils.formatElapsedTime(mContext, screenUsageTimeMs, false),
-                mHighUsageAppList);
+        return new HighUsageTip(screenUsageTimeMs, mHighUsageAppList);
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index 17e395e..eadd0e1 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -16,8 +16,9 @@
 
 package com.android.settings.fuelgauge.batterytip.tips;
 
-import android.app.Dialog;
 import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.support.annotation.IdRes;
 import android.support.annotation.IntDef;
 import android.support.v7.preference.Preference;
@@ -31,7 +32,7 @@
  * Each {@link BatteryTip} contains basic data(e.g. title, summary, icon) as well as the
  * pre-defined action(e.g. turn on battery saver)
  */
-public abstract class BatteryTip implements Comparable<BatteryTip> {
+public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({StateType.NEW,
             StateType.HANDLED,
@@ -62,12 +63,34 @@
 
     private static final String KEY_PREFIX = "key_battery_tip";
 
-    @TipType
     protected int mType;
-    @StateType
     protected int mState;
     protected boolean mShowDialog;
 
+    BatteryTip(Parcel in) {
+        mType = in.readInt();
+        mState = in.readInt();
+        mShowDialog = in.readBoolean();
+    }
+
+    BatteryTip(int type, int state, boolean showDialog) {
+        mType = type;
+        mState = state;
+        mShowDialog = showDialog;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeInt(mState);
+        dest.writeBoolean(mShowDialog);
+    }
+
     public abstract CharSequence getTitle(Context context);
 
     public abstract CharSequence getSummary(Context context);
@@ -77,6 +100,7 @@
 
     /**
      * Update the current {@link #mState} using the new {@code tip}.
+     *
      * @param tip used to update
      */
     public abstract void updateState(BatteryTip tip);
@@ -86,12 +110,6 @@
      */
     public abstract void action();
 
-    /**
-     * Build the dialog to display either the info about {@link BatteryTip} or confirmation
-     * about the action.
-     */
-    public abstract Dialog buildDialog();
-
     public Preference buildPreference(Context context) {
         Preference preference = new Preference(context);
 
@@ -110,6 +128,10 @@
         return KEY_PREFIX + mType;
     }
 
+    public int getType() {
+        return mType;
+    }
+
     @StateType
     public int getState() {
         return mState;
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java
index 38f2a26..001a48e 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.java
@@ -16,10 +16,14 @@
 
 package com.android.settings.fuelgauge.batterytip.tips;
 
-import android.app.Dialog;
 import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.VisibleForTesting;
 
 import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.fuelgauge.batterytip.HighUsageApp;
 
 import java.util.List;
 
@@ -28,15 +32,29 @@
  */
 public class HighUsageTip extends BatteryTip {
 
-    private final CharSequence mScreenTimeText;
-    private final List<HighUsageApp> mHighUsageAppList;
+    private final long mScreenTimeMs;
+    @VisibleForTesting
+    final List<HighUsageApp> mHighUsageAppList;
 
-    public HighUsageTip(CharSequence screenTimeText, List<HighUsageApp> appList) {
-        mShowDialog = true;
-        mScreenTimeText = screenTimeText;
-        mType = TipType.HIGH_DEVICE_USAGE;
+    public HighUsageTip(long screenTimeMs, List<HighUsageApp> appList) {
+        super(TipType.HIGH_DEVICE_USAGE, appList.isEmpty() ? StateType.INVISIBLE : StateType.NEW,
+                true /* showDialog */);
+        mScreenTimeMs = screenTimeMs;
         mHighUsageAppList = appList;
-        mState = appList.isEmpty() ? StateType.INVISIBLE : StateType.NEW;
+    }
+
+    @VisibleForTesting
+    HighUsageTip(Parcel in) {
+        super(in);
+        mScreenTimeMs = in.readLong();
+        mHighUsageAppList = in.createTypedArrayList(HighUsageApp.CREATOR);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeLong(mScreenTimeMs);
+        dest.writeTypedList(mHighUsageAppList);
     }
 
     @Override
@@ -46,7 +64,8 @@
 
     @Override
     public CharSequence getSummary(Context context) {
-        return context.getString(R.string.battery_tip_high_usage_summary, mScreenTimeText);
+        return context.getString(R.string.battery_tip_high_usage_summary,
+                Utils.formatElapsedTime(context, mScreenTimeMs, false));
     }
 
     @Override
@@ -64,27 +83,22 @@
         // do nothing
     }
 
-    @Override
-    public Dialog buildDialog() {
-        //TODO(b/70570352): build the real dialog
-        return null;
+    public long getScreenTimeMs() {
+        return mScreenTimeMs;
     }
 
-    /**
-     * Class representing app with high screen usage
-     */
-    public static class HighUsageApp implements Comparable<HighUsageApp> {
-        public final String packageName;
-        public final long screenOnTimeMs;
-
-        public HighUsageApp(String packageName, long screenOnTimeMs) {
-            this.packageName = packageName;
-            this.screenOnTimeMs = screenOnTimeMs;
-        }
-
-        @Override
-        public int compareTo(HighUsageApp o) {
-            return Long.compare(screenOnTimeMs, o.screenOnTimeMs);
-        }
+    public List<HighUsageApp> getHighUsageAppList() {
+        return mHighUsageAppList;
     }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public BatteryTip createFromParcel(Parcel in) {
+            return new HighUsageTip(in);
+        }
+
+        public BatteryTip[] newArray(int size) {
+            return new HighUsageTip[size];
+        }
+    };
+
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
index 8605fbb..4a207e0 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
@@ -16,8 +16,9 @@
 
 package com.android.settings.fuelgauge.batterytip.tips;
 
-import android.app.Dialog;
 import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import com.android.settings.R;
 
@@ -27,9 +28,11 @@
 public class LowBatteryTip extends BatteryTip {
 
     public LowBatteryTip(@StateType int state) {
-        mShowDialog = false;
-        mState = state;
-        mType = TipType.LOW_BATTERY;
+        super(TipType.LOW_BATTERY, state, false /* showDialog */);
+    }
+
+    private LowBatteryTip(Parcel in) {
+        super(in);
     }
 
     @Override
@@ -57,9 +60,14 @@
         // do nothing
     }
 
-    @Override
-    public Dialog buildDialog() {
-        //TODO(b/70570352): create the dialog for low battery tip and add test
-        return null;
-    }
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public BatteryTip createFromParcel(Parcel in) {
+            return new LowBatteryTip(in);
+        }
+
+        public BatteryTip[] newArray(int size) {
+            return new LowBatteryTip[size];
+        }
+    };
+
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
index 2a2deab..51019a8 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
@@ -16,8 +16,9 @@
 
 package com.android.settings.fuelgauge.batterytip.tips;
 
-import android.app.Dialog;
 import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import com.android.settings.R;
 
@@ -27,9 +28,11 @@
 public class SummaryTip extends BatteryTip {
 
     public SummaryTip(@StateType int state) {
-        mShowDialog = false;
-        mState = state;
-        mType = TipType.SUMMARY;
+        super(TipType.SUMMARY, state, false /* showDialog */);
+    }
+
+    private SummaryTip(Parcel in) {
+        super(in);
     }
 
     @Override
@@ -57,9 +60,13 @@
         // do nothing
     }
 
-    @Override
-    public Dialog buildDialog() {
-        //TODO(b/70570352): create the dialog for summary tip and add test
-        return null;
-    }
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public BatteryTip createFromParcel(Parcel in) {
+            return new SummaryTip(in);
+        }
+
+        public BatteryTip[] newArray(int size) {
+            return new SummaryTip[size];
+        }
+    };
 }
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index f813457..fb571bb 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -4,13 +4,16 @@
 
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
 import android.net.LinkAddress;
@@ -25,6 +28,7 @@
 import android.text.SpannableStringBuilder;
 import android.text.format.DateUtils;
 import android.text.style.TtsSpan;
+import android.util.IconDrawableFactory;
 import android.widget.EditText;
 import android.widget.TextView;
 
@@ -46,8 +50,8 @@
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class UtilsTest {
-
     private static final String PACKAGE_NAME = "com.android.app";
+    private static final int USER_ID = 1;
 
     @Mock
     private WifiManager wifiManager;
@@ -59,6 +63,12 @@
     private DevicePolicyManagerWrapper mDevicePolicyManager;
     @Mock
     private UserManager mUserManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private IconDrawableFactory mIconDrawableFactory;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
     private Context mContext;
 
     @Before
@@ -332,4 +342,17 @@
 
         assertThat(editText.getSelectionEnd()).isEqualTo(length);
     }
+
+    @Test
+    public void testGetBadgedIcon_usePackageNameAndUserId() throws
+            PackageManager.NameNotFoundException {
+        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME,
+                PackageManager.GET_META_DATA);
+
+        Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, PACKAGE_NAME, USER_ID);
+
+        // Verify that it uses the correct user id
+        verify(mIconDrawableFactory).getBadgedIcon(mApplicationInfo, USER_ID);
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetailsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetailsTest.java
index c992d0a..8aa0659 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetailsTest.java
@@ -67,7 +67,6 @@
     private static final String PACKAGE_NAME_1 = "com.android.app1";
     private static final String PACKAGE_NAME_2 = "com.android.app2";
     private static final String PACKAGE_NAME_3 = "com.android.app3";
-    private static final int USER_ID = 1;
 
     @Mock
     private SettingsActivity mSettingsActivity;
@@ -198,16 +197,4 @@
         assertThat(mBundle.getParcelableArrayList(
                 PowerUsageAnomalyDetails.EXTRA_ANOMALY_LIST)).isEqualTo(mAnomalyList);
     }
-
-    @Test
-    public void testGetBadgedIcon_usePackageNameAndUserId() throws
-            PackageManager.NameNotFoundException {
-        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME_1,
-                PackageManager.GET_META_DATA);
-
-        mFragment.getBadgedIcon(PACKAGE_NAME_1, USER_ID);
-
-        // Verify that it uses the correct user id
-        verify(mIconDrawableFactory).getBadgedIcon(mApplicationInfo, USER_ID);
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
index 944587f..9f0c61f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
@@ -85,7 +85,7 @@
         mNewBatteryTips.add(new SummaryTip(BatteryTip.StateType.INVISIBLE));
 
         mBatteryTipPreferenceController = new BatteryTipPreferenceController(mContext, KEY_PREF,
-                mBatteryTipListener);
+                null, mBatteryTipListener);
         mBatteryTipPreferenceController.mPreferenceGroup = mPreferenceGroup;
         mBatteryTipPreferenceController.mPrefContext = mContext;
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
index eec8e76..1c6c868 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
@@ -19,6 +19,8 @@
 
 import android.app.Dialog;
 import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.support.annotation.IdRes;
 import android.support.v7.preference.Preference;
 
@@ -58,10 +60,32 @@
         assertThat(preference.getIcon()).isEqualTo(mContext.getDrawable(ICON_ID));
     }
 
+    @Test
+    public void testParcelable() {
+        final BatteryTip batteryTip = new TestBatteryTip();
+
+        Parcel parcel = Parcel.obtain();
+        batteryTip.writeToParcel(parcel, batteryTip.describeContents());
+        parcel.setDataPosition(0);
+
+        final BatteryTip parcelTip = new TestBatteryTip(parcel);
+
+        assertThat(parcelTip.getTitle(mContext)).isEqualTo(TITLE);
+        assertThat(parcelTip.getSummary(mContext)).isEqualTo(SUMMARY);
+        assertThat(parcelTip.getIconId()).isEqualTo(ICON_ID);
+    }
+
     /**
      * Used to test the non abstract methods in {@link TestBatteryTip}
      */
-    public class TestBatteryTip extends BatteryTip {
+    public static class TestBatteryTip extends BatteryTip {
+        TestBatteryTip() {
+            super(TipType.SUMMARY, StateType.NEW, true);
+        }
+
+        TestBatteryTip(Parcel in) {
+            super(in);
+        }
 
         @Override
         public String getTitle(Context context) {
@@ -88,10 +112,15 @@
             // do nothing
         }
 
-        @Override
-        public Dialog buildDialog() {
-            return null;
-        }
+        public final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+            public BatteryTip createFromParcel(Parcel in) {
+                return new TestBatteryTip(in);
+            }
+
+            public BatteryTip[] newArray(int size) {
+                return new TestBatteryTip[size];
+            }
+        };
     }
 
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTipTest.java
new file mode 100644
index 0000000..e2f8a26
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTipTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.fuelgauge.batterytip.tips;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.text.format.DateUtils;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.HighUsageApp;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class HighUsageTipTest {
+    private static final String PACKAGE_NAME = "com.android.app";
+    private static final long SCREEN_TIME = 30 * DateUtils.MINUTE_IN_MILLIS;
+
+    private Context mContext;
+    private HighUsageTip mBatteryTip;
+    private List<HighUsageApp> mUsageAppList;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+
+        mUsageAppList = new ArrayList<>();
+        mUsageAppList.add(new HighUsageApp(PACKAGE_NAME, SCREEN_TIME));
+        mBatteryTip = new HighUsageTip(SCREEN_TIME, mUsageAppList);
+    }
+
+    @Test
+    public void testParcelable() {
+
+        Parcel parcel = Parcel.obtain();
+        mBatteryTip.writeToParcel(parcel, mBatteryTip.describeContents());
+        parcel.setDataPosition(0);
+
+        final HighUsageTip parcelTip = new HighUsageTip(parcel);
+
+        assertThat(parcelTip.getTitle(mContext)).isEqualTo("Phone used heavily");
+        assertThat(parcelTip.getType()).isEqualTo(BatteryTip.TipType.HIGH_DEVICE_USAGE);
+        assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
+        assertThat(parcelTip.getScreenTimeMs()).isEqualTo(SCREEN_TIME);
+        assertThat(parcelTip.mHighUsageAppList.size()).isEqualTo(1);
+        final HighUsageApp app = parcelTip.mHighUsageAppList.get(0);
+        assertThat(app.packageName).isEqualTo(PACKAGE_NAME);
+        assertThat(app.screenOnTimeMs).isEqualTo(SCREEN_TIME);
+    }
+}