diff --git a/res/layout/app_high_usage_item.xml b/res/layout/app_high_usage_item.xml new file mode 100755 index 00000000000..473315f2ade --- /dev/null +++ b/res/layout/app_high_usage_item.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/res/layout/recycler_view.xml b/res/layout/recycler_view.xml new file mode 100644 index 00000000000..a7dabe5d720 --- /dev/null +++ b/res/layout/recycler_view.xml @@ -0,0 +1,23 @@ + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 52bcdbfbee7..8c7b6f4789f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4773,6 +4773,21 @@ Low battery capacity Battery can\'t provide good battery life + + Phone used heavily + + Tablet used heavily + + Device used heavily + + About %1$s used since last full charge + + Your phone was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your phone was used for about %1$s since last full charge.\n\n Total usage: + + Your tablet was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your tablet was used for about %1$s since last full charge.\n\n Total usage: + + Your device was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your device was used for about %1$s since last full charge.\n\n Total usage: + Smart battery manager diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index cd64799f804..ad951216781 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -94,6 +94,7 @@ import android.text.TextUtils; 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 @@ public final class Utils extends com.android.settingslib.Utils { } 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/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index 68677fab2de..0952f1f095d 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -345,6 +345,17 @@ public class BatteryUtils { } + /** + * Calculate the screen usage time since last full charge. + * @param batteryStatsHelper utility class that contains the screen usage data + * @return time in millis + */ + public long calculateScreenUsageTime(BatteryStatsHelper batteryStatsHelper) { + final BatterySipper sipper = findBatterySipperByType( + batteryStatsHelper.getUsageList(), BatterySipper.DrainType.SCREEN); + return sipper != null ? sipper.usageTimeMs : 0; + } + public static void logRuntime(String tag, String message, long startTime) { Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms"); } @@ -432,6 +443,20 @@ public class BatteryUtils { return batteryInfo; } + /** + * Find the {@link BatterySipper} with the corresponding {@link BatterySipper.DrainType} + */ + public BatterySipper findBatterySipperByType(List usageList, + BatterySipper.DrainType type) { + for (int i = 0, size = usageList.size(); i < size; i++) { + final BatterySipper sipper = usageList.get(i); + if (sipper.drainType == type) { + return sipper; + } + } + return null; + } + private boolean isDataCorrupted() { return mPackageManager == null || mAppOpsManager == null; } diff --git a/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java b/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java index 0d73511e84d..143733db2b8 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.annotations.VisibleForTesting; 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 @@ public class PowerUsageAnomalyDetails extends DashboardFragment implements @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 0315f032e66..205ac0b15a8 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -266,7 +266,7 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList 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)); @@ -369,8 +369,9 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList restartBatteryInfoLoader(); final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper, System.currentTimeMillis()); - updateScreenPreference(); updateLastFullChargePreference(lastFullChargeTime); + mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(getContext(), + mBatteryUtils.calculateScreenUsageTime(mStatsHelper), false)); final CharSequence timeSequence = Utils.formatRelativeTime(context, lastFullChargeTime, false); @@ -393,26 +394,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList return new AnomalyDetectionPolicy(getContext()); } - @VisibleForTesting - BatterySipper findBatterySipperByType(List usageList, DrainType type) { - for (int i = 0, size = usageList.size(); i < size; i++) { - final BatterySipper sipper = usageList.get(i); - if (sipper.drainType == type) { - return sipper; - } - } - return null; - } - - @VisibleForTesting - void updateScreenPreference() { - final BatterySipper sipper = findBatterySipperByType( - mStatsHelper.getUsageList(), DrainType.SCREEN); - final long usageTimeMs = sipper != null ? sipper.usageTimeMs : 0; - - mScreenUsagePref.setSubtitle(Utils.formatElapsedTime(getContext(), usageTimeMs, false)); - } - @VisibleForTesting void updateLastFullChargePreference(long timeMs) { final CharSequence timeSequence = Utils.formatRelativeTime(getContext(), timeMs, false); 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 00000000000..3e091b3b44c --- /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/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java index 9c3f48c0a6b..a1db57a409a 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java @@ -23,6 +23,7 @@ import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.detectors.BatteryTipDetector; +import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector; import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector; import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; @@ -65,6 +66,8 @@ public class BatteryTipLoader extends AsyncLoader> { mVisibleTips = 0; addBatteryTipFromDetector(tips, new LowBatteryDetector(policy, batteryInfo)); + addBatteryTipFromDetector(tips, + new HighUsageDetector(getContext(), policy, mBatteryStatsHelper)); // Add summary detector at last since it need other detectors to update the mVisibleTips addBatteryTipFromDetector(tips, new SummaryDetector(policy, mVisibleTips)); diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java index f6114052cf6..9aa70c55264 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batterytip; 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 @@ import java.util.Map; * 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 mBatteryTips; private Map mBatteryTipMap; @@ -41,16 +45,18 @@ public class BatteryTipPreferenceController extends BasePreferenceController { 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 @@ public class BatteryTipPreferenceController extends BasePreferenceController { 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 00000000000..8b743946648 --- /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 { + private final Context mContext; + private final IconDrawableFactory mIconDrawableFactory; + private final PackageManager mPackageManager; + private final List 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 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 00000000000..f75ecf0e322 --- /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, 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 new file mode 100644 index 00000000000..237f430c575 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java @@ -0,0 +1,84 @@ +/* + * 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.detectors; + +import android.content.Context; +import android.os.BatteryStats; +import android.support.annotation.VisibleForTesting; +import android.text.format.DateUtils; + +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; +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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Detector whether to show summary tip. This detector should be executed as the last + * {@link BatteryTipDetector} since it need the most up-to-date {@code visibleTips} + */ +public class HighUsageDetector implements BatteryTipDetector { + private BatteryTipPolicy mPolicy; + private BatteryStatsHelper mBatteryStatsHelper; + private List mHighUsageAppList; + private Context mContext; + @VisibleForTesting + BatteryUtils mBatteryUtils; + + public HighUsageDetector(Context context, BatteryTipPolicy policy, + BatteryStatsHelper batteryStatsHelper) { + mContext = context; + mPolicy = policy; + mBatteryStatsHelper = batteryStatsHelper; + mHighUsageAppList = new ArrayList<>(); + mBatteryUtils = BatteryUtils.getInstance(context); + } + + @Override + public BatteryTip detect() { + final long screenUsageTimeMs = mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper); + //TODO(b/70570352): Change it to detect whether battery drops 25% in last 2 hours + if (mPolicy.highUsageEnabled && screenUsageTimeMs > DateUtils.HOUR_IN_MILLIS) { + final List batterySippers = mBatteryStatsHelper.getUsageList(); + for (int i = 0, size = batterySippers.size(); i < size; i++) { + final BatterySipper batterySipper = batterySippers.get(i); + if (!mBatteryUtils.shouldHideSipper(batterySipper)) { + final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs( + BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj, + BatteryStats.STATS_SINCE_CHARGED); + mHighUsageAppList.add(new HighUsageApp( + mBatteryUtils.getPackageName(batterySipper.getUid()), + foregroundTimeMs)); + } + } + + mHighUsageAppList = mHighUsageAppList.subList(0, + Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size())); + Collections.sort(mHighUsageAppList, Collections.reverseOrder()); + } + + 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 17e395ef185..eadd0e1a811 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 @@ import java.lang.annotation.RetentionPolicy; * 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 { +public abstract class BatteryTip implements Comparable, Parcelable { @Retention(RetentionPolicy.SOURCE) @IntDef({StateType.NEW, StateType.HANDLED, @@ -62,12 +63,34 @@ public abstract class BatteryTip implements Comparable { 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 @@ public abstract class BatteryTip implements Comparable { /** * 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 class BatteryTip implements Comparable { */ 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 @@ public abstract class BatteryTip implements Comparable { 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 new file mode 100644 index 00000000000..001a48eedc3 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/tips/HighUsageTip.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.tips; + +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; + +/** + * Tip to show general summary about battery life + */ +public class HighUsageTip extends BatteryTip { + + private final long mScreenTimeMs; + @VisibleForTesting + final List mHighUsageAppList; + + public HighUsageTip(long screenTimeMs, List appList) { + super(TipType.HIGH_DEVICE_USAGE, appList.isEmpty() ? StateType.INVISIBLE : StateType.NEW, + true /* showDialog */); + mScreenTimeMs = screenTimeMs; + mHighUsageAppList = appList; + } + + @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 + public CharSequence getTitle(Context context) { + return context.getString(R.string.battery_tip_high_usage_title); + } + + @Override + public CharSequence getSummary(Context context) { + return context.getString(R.string.battery_tip_high_usage_summary, + Utils.formatElapsedTime(context, mScreenTimeMs, false)); + } + + @Override + public int getIconId() { + return R.drawable.ic_perm_device_information_red_24dp; + } + + @Override + public void updateState(BatteryTip tip) { + mState = tip.mState; + } + + @Override + public void action() { + // do nothing + } + + public long getScreenTimeMs() { + return mScreenTimeMs; + } + + public List 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 8605fbb6319..4a207e0b126 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 @@ import com.android.settings.R; 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 @@ public class LowBatteryTip extends BatteryTip { // 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 2a2deabfa8f..51019a8d35d 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 @@ import com.android.settings.R; 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 @@ public class SummaryTip extends BatteryTip { // 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 f8134578117..fb571bb5cf3 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 com.google.common.truth.Truth.assertThat; 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.os.storage.VolumeInfo; 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 @@ import java.util.List; @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 @@ public class UtilsTest { 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 @@ public class UtilsTest { 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/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java index 1393d5718b0..844aca4c4a5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java @@ -20,7 +20,9 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND; import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE; import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING; + import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; @@ -141,6 +143,7 @@ public class BatteryUtilsTest { private BatteryUtils mBatteryUtils; private FakeFeatureFactory mFeatureFactory; private PowerUsageFeatureProvider mProvider; + private List mUsageList; @Before public void setUp() { @@ -194,6 +197,12 @@ public class BatteryUtilsTest { mBatteryUtils.mPowerUsageFeatureProvider = mProvider; doReturn(0L).when(mBatteryUtils).getForegroundServiceTotalTimeUs( any(BatteryStats.Uid.class), anyLong()); + + mUsageList = new ArrayList<>(); + mUsageList.add(mNormalBatterySipper); + mUsageList.add(mScreenBatterySipper); + mUsageList.add(mCellBatterySipper); + doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); } @Test @@ -468,4 +477,28 @@ public class BatteryUtilsTest { verify(mBatteryStatsHelper).refreshStats(BatteryStats.STATS_SINCE_CHARGED, mUserManager.getUserProfiles()); } + + @Test + public void testFindBatterySipperByType_findTypeScreen() { + BatterySipper sipper = mBatteryUtils.findBatterySipperByType(mUsageList, + BatterySipper.DrainType.SCREEN); + + assertThat(sipper).isSameAs(mScreenBatterySipper); + } + + @Test + public void testFindBatterySipperByType_findTypeApp() { + BatterySipper sipper = mBatteryUtils.findBatterySipperByType(mUsageList, + BatterySipper.DrainType.APP); + + assertThat(sipper).isSameAs(mNormalBatterySipper); + } + + @Test + public void testCalculateScreenUsageTime_returnCorrectTime() { + mScreenBatterySipper.usageTimeMs = TIME_EXPECTED_FOREGROUND; + + assertThat(mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper)).isEqualTo( + TIME_EXPECTED_FOREGROUND); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetailsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetailsTest.java index c992d0aef63..8aa0659671d 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 @@ public class PowerUsageAnomalyDetailsTest { 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 @@ public class PowerUsageAnomalyDetailsTest { 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/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index 272890939aa..6fecf3ce928 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -247,34 +247,6 @@ public class PowerUsageSummaryTest { assertThat(mFragment.mShowAllApps).isEqualTo(!isShowApps); } - @Test - public void testFindBatterySipperByType_findTypeScreen() { - BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList, - BatterySipper.DrainType.SCREEN); - - assertThat(sipper).isSameAs(mScreenBatterySipper); - } - - @Test - public void testFindBatterySipperByType_findTypeApp() { - BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList, - BatterySipper.DrainType.APP); - - assertThat(sipper).isSameAs(mNormalBatterySipper); - } - - @Test - public void testUpdateScreenPreference_showCorrectSummary() { - doReturn(mScreenBatterySipper).when(mFragment).findBatterySipperByType(any(), any()); - doReturn(mRealContext).when(mFragment).getContext(); - final CharSequence expectedSummary = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS, - false); - - mFragment.updateScreenPreference(); - - assertThat(mScreenUsagePref.getSubtitle()).isEqualTo(expectedSummary); - } - @Test public void testUpdateLastFullChargePreference_showCorrectSummary() { doReturn(mRealContext).when(mFragment).getContext(); @@ -284,16 +256,6 @@ public class PowerUsageSummaryTest { assertThat(mLastFullChargePref.getSubtitle()).isEqualTo("2 hr. ago"); } - @Test - public void testUpdatePreference_usageListEmpty_shouldNotCrash() { - when(mBatteryHelper.getUsageList()).thenReturn(new ArrayList()); - doReturn(STUB_STRING).when(mFragment).getString(anyInt(), any()); - doReturn(mRealContext).when(mFragment).getContext(); - - // Should not crash when update - mFragment.updateScreenPreference(); - } - @Test public void testNonIndexableKeys_MatchPreferenceKeys() { final Context context = RuntimeEnvironment.application; 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 944587fb21e..9f0c61fc9c8 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 @@ public class BatteryTipPreferenceControllerTest { 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/detectors/HighUsageDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java new file mode 100644 index 00000000000..2a719916fb0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java @@ -0,0 +1,94 @@ +/* + * 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.fuelgauge.batterytip.detectors; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.os.BatteryStats; +import android.text.format.DateUtils; + +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; +import com.android.settings.TestConfig; +import com.android.settings.fuelgauge.BatteryInfo; +import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class HighUsageDetectorTest { + private Context mContext; + @Mock + private BatteryStatsHelper mBatteryStatsHelper; + @Mock + private BatteryUtils mBatteryUtils; + @Mock + private BatterySipper mBatterySipper; + + private BatteryTipPolicy mPolicy; + private HighUsageDetector mHighUsageDetector; + private List mUsageList; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mPolicy = spy(new BatteryTipPolicy(mContext)); + mHighUsageDetector = new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper); + mHighUsageDetector.mBatteryUtils = mBatteryUtils; + + mUsageList = new ArrayList<>(); + mUsageList.add(mBatterySipper); + } + + @Test + public void testDetect_disabledByPolicy_tipInvisible() { + ReflectionHelpers.setField(mPolicy, "highUsageEnabled", false); + + assertThat(mHighUsageDetector.detect().isVisible()).isFalse(); + } + + @Test + public void testDetect_containsHighUsageApp_tipVisible() { + doReturn(2 * DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).calculateScreenUsageTime( + mBatteryStatsHelper); + doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); + doReturn(DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).getProcessTimeMs( + BatteryUtils.StatusType.FOREGROUND, mBatterySipper.uidObj, + BatteryStats.STATS_SINCE_CHARGED); + + assertThat(mHighUsageDetector.detect().isVisible()).isTrue(); + } +} 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 eec8e760914..1c6c8683c04 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 static com.google.common.truth.Truth.assertThat; 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 @@ public class BatteryTipTest { 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 @@ public class BatteryTipTest { // 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 00000000000..e2f8a26cc7e --- /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 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); + } +}