Loading res/xml/power_usage_summary.xml +3 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,9 @@ android:selectable="true" android:layout="@layout/battery_header"/> <Preference android:key="high_usage"/> <PreferenceCategory android:key="device_usage_list"> Loading src/com/android/settings/fuelgauge/PowerUsageSummary.java +41 −1 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package com.android.settings.fuelgauge; import android.app.Activity; import android.app.LoaderManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.Loader; import android.graphics.drawable.Drawable; import android.os.BatteryStats; import android.os.Build; Loading Loading @@ -59,6 +61,10 @@ import com.android.settings.dashboard.SummaryLoader; import com.android.settings.display.AutoBrightnessPreferenceController; import com.android.settings.display.BatteryPercentagePreferenceController; import com.android.settings.display.TimeoutPreferenceController; import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment; import com.android.settings.fuelgauge.anomaly.AnomalyLoader; import com.android.settings.fuelgauge.anomaly.AnomalyPreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.FooterPreferenceMixin; Loading @@ -74,7 +80,8 @@ import java.util.List; * Displays a list of apps and subsystems that consume power, ordered by how much power was * consumed since the last time it was unplugged. */ public class PowerUsageSummary extends PowerUsageBase { public class PowerUsageSummary extends PowerUsageBase implements AnomalyDialogFragment.AnomalyDialogListener { static final String TAG = "PowerUsageSummary"; Loading @@ -84,6 +91,7 @@ public class PowerUsageSummary extends PowerUsageBase { private static final String KEY_BATTERY_HEADER = "battery_header"; private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10; private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10; private static final int ANOMALY_LOADER = 1; private static final String KEY_SCREEN_USAGE = "screen_usage"; private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge"; Loading Loading @@ -117,8 +125,29 @@ public class PowerUsageSummary extends PowerUsageBase { private LayoutPreference mBatteryLayoutPref; private PreferenceGroup mAppListGroup; private AnomalyPreferenceController mAnomalyPreferenceController; private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; private LoaderManager.LoaderCallbacks<List<Anomaly>> mAnomalyLoaderCallbacks = new LoaderManager.LoaderCallbacks<List<Anomaly>>() { @Override public Loader<List<Anomaly>> onCreateLoader(int id, Bundle args) { return new AnomalyLoader(getContext(), mStatsHelper); } @Override public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) { // show high usage preference if possible mAnomalyPreferenceController.updateAnomalyPreference(data); } @Override public void onLoaderReset(Loader<List<Anomaly>> loader) { } }; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); Loading @@ -130,6 +159,7 @@ public class PowerUsageSummary extends PowerUsageBase { mLastFullChargePref = (PowerGaugePreference) findPreference( KEY_TIME_SINCE_LAST_FULL_CHARGE); mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary); mAnomalyPreferenceController = new AnomalyPreferenceController(this); mBatteryUtils = BatteryUtils.getInstance(getContext()); Loading Loading @@ -163,6 +193,9 @@ public class PowerUsageSummary extends PowerUsageBase { @Override public boolean onPreferenceTreeClick(Preference preference) { if (mAnomalyPreferenceController.onPreferenceTreeClick(preference)) { return true; } if (KEY_BATTERY_HEADER.equals(preference.getKey())) { performBatteryHeaderClick(); return true; Loading Loading @@ -403,6 +436,8 @@ public class PowerUsageSummary extends PowerUsageBase { return; } getLoaderManager().initLoader(ANOMALY_LOADER, null, mAnomalyLoaderCallbacks); cacheRemoveAllPrefs(mAppListGroup); mAppListGroup.setOrderingAsAdded(false); boolean addedSome = false; Loading Loading @@ -690,6 +725,11 @@ public class PowerUsageSummary extends PowerUsageBase { } }; @Override public void onAnomalyHandled(Anomaly anomaly) { mAnomalyPreferenceController.hideAnomalyPreference(); } private static class SummaryProvider implements SummaryLoader.SummaryProvider { private final Context mContext; private final SummaryLoader mLoader; Loading src/com/android/settings/fuelgauge/anomaly/Anomaly.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * 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.anomaly; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Data that represents an app has been detected as anomaly. It contains * * 1. Basic information of the app(i.e. uid, package name) * 2. Type of anomaly * 3. Data that has been detected as anomaly(i.e wakelock time) */ public class Anomaly implements Parcelable { @Retention(RetentionPolicy.SOURCE) @IntDef({AnomalyType.WAKE_LOCK}) public @interface AnomalyType { int WAKE_LOCK = 0; } @Retention(RetentionPolicy.SOURCE) @IntDef({AnomalyActionType.FORCE_STOP}) public @interface AnomalyActionType { int FORCE_STOP = 0; } /** * Type of this this anomaly */ public final int type; public final int uid; public final long wakelockTimeMs; /** * Display name of this anomaly, usually it is the app name */ public final String displayName; public final String packageName; private Anomaly(Builder builder) { type = builder.mType; uid = builder.mUid; displayName = builder.mDisplayName; packageName = builder.mPackageName; wakelockTimeMs = builder.mWakeLockTimeMs; } private Anomaly(Parcel in) { type = in.readInt(); uid = in.readInt(); displayName = in.readString(); packageName = in.readString(); wakelockTimeMs = in.readLong(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(type); dest.writeInt(uid); dest.writeString(displayName); dest.writeString(packageName); dest.writeLong(wakelockTimeMs); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Anomaly createFromParcel(Parcel in) { return new Anomaly(in); } public Anomaly[] newArray(int size) { return new Anomaly[size]; } }; public static final class Builder { @AnomalyType private int mType; private int mUid; private String mDisplayName; private String mPackageName; private long mWakeLockTimeMs; public Builder setType(@AnomalyType int type) { mType = type; return this; } public Builder setUid(int uid) { mUid = uid; return this; } public Builder setDisplayName(String displayName) { mDisplayName = displayName; return this; } public Builder setPackageName(String packageName) { mPackageName = packageName; return this; } public Builder setWakeLockTimeMs(long wakeLockTimeMs) { mWakeLockTimeMs = wakeLockTimeMs; return this; } public Anomaly build() { return new Anomaly(this); } } } src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * 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.anomaly; 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 com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; /** * Dialog Fragment to show action dialog for each anomaly */ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener { private static final String ARG_ANOMALY = "anomaly"; @VisibleForTesting Anomaly mAnomaly; /** * Listener to give the control back to target fragment */ public interface AnomalyDialogListener { /** * This method is invoked once anomaly is handled, then target fragment could do * extra work. One example is that fragment could remove the anomaly preference * since it has been handled * * @param anomaly that has been handled */ void onAnomalyHandled(Anomaly anomaly); } public static AnomalyDialogFragment newInstance(Anomaly anomaly) { AnomalyDialogFragment dialogFragment = new AnomalyDialogFragment(); Bundle args = new Bundle(1); args.putParcelable(ARG_ANOMALY, anomaly); dialogFragment.setArguments(args); return dialogFragment; } @Override public int getMetricsCategory() { // TODO(b/37681923): add anomaly metric id return 0; } @Override public void onClick(DialogInterface dialog, int which) { final AnomalyDialogListener lsn = (AnomalyDialogListener) getTargetFragment(); if (lsn == null) { return; } final AnomalyAction anomalyAction = AnomalyUtils.getAnomalyAction(mAnomaly.type); anomalyAction.handlePositiveAction(mAnomaly.packageName); lsn.onAnomalyHandled(mAnomaly); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Bundle bundle = getArguments(); mAnomaly = bundle.getParcelable(ARG_ANOMALY); final Context context = getContext(); final AnomalyAction anomalyAction = AnomalyUtils.getAnomalyAction(mAnomaly.type); switch (anomalyAction.getActionType()) { case Anomaly.AnomalyActionType.FORCE_STOP: return new AlertDialog.Builder(context) .setTitle(R.string.force_stop_dlg_title) .setMessage(R.string.force_stop_dlg_text) .setPositiveButton(R.string.dlg_ok, this) .setNegativeButton(R.string.dlg_cancel, null) .create(); default: throw new IllegalArgumentException("unknown type " + mAnomaly.type); } } } src/com/android/settings/fuelgauge/anomaly/AnomalyLoader.java 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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.anomaly; import android.content.Context; import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.utils.AsyncLoader; import java.util.ArrayList; import java.util.List; /** * Loader to compute which apps are anomaly and return a anomaly list. It will return * an empty list if there is no anomaly. */ //TODO(b/36924669): add test for this file, for now it seems there is nothing to test public class AnomalyLoader extends AsyncLoader<List<Anomaly>> { private BatteryStatsHelper mBatteryStatsHelper; public AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper) { super(context); mBatteryStatsHelper = batteryStatsHelper; } @Override protected void onDiscardResult(List<Anomaly> result) {} @Override public List<Anomaly> loadInBackground() { final List<Anomaly> anomalies = new ArrayList<>(); anomalies.addAll(new WakeLockAnomalyDetector().detectAnomalies(mBatteryStatsHelper)); return anomalies; } } Loading
res/xml/power_usage_summary.xml +3 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,9 @@ android:selectable="true" android:layout="@layout/battery_header"/> <Preference android:key="high_usage"/> <PreferenceCategory android:key="device_usage_list"> Loading
src/com/android/settings/fuelgauge/PowerUsageSummary.java +41 −1 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package com.android.settings.fuelgauge; import android.app.Activity; import android.app.LoaderManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.Loader; import android.graphics.drawable.Drawable; import android.os.BatteryStats; import android.os.Build; Loading Loading @@ -59,6 +61,10 @@ import com.android.settings.dashboard.SummaryLoader; import com.android.settings.display.AutoBrightnessPreferenceController; import com.android.settings.display.BatteryPercentagePreferenceController; import com.android.settings.display.TimeoutPreferenceController; import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment; import com.android.settings.fuelgauge.anomaly.AnomalyLoader; import com.android.settings.fuelgauge.anomaly.AnomalyPreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.FooterPreferenceMixin; Loading @@ -74,7 +80,8 @@ import java.util.List; * Displays a list of apps and subsystems that consume power, ordered by how much power was * consumed since the last time it was unplugged. */ public class PowerUsageSummary extends PowerUsageBase { public class PowerUsageSummary extends PowerUsageBase implements AnomalyDialogFragment.AnomalyDialogListener { static final String TAG = "PowerUsageSummary"; Loading @@ -84,6 +91,7 @@ public class PowerUsageSummary extends PowerUsageBase { private static final String KEY_BATTERY_HEADER = "battery_header"; private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10; private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10; private static final int ANOMALY_LOADER = 1; private static final String KEY_SCREEN_USAGE = "screen_usage"; private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge"; Loading Loading @@ -117,8 +125,29 @@ public class PowerUsageSummary extends PowerUsageBase { private LayoutPreference mBatteryLayoutPref; private PreferenceGroup mAppListGroup; private AnomalyPreferenceController mAnomalyPreferenceController; private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; private LoaderManager.LoaderCallbacks<List<Anomaly>> mAnomalyLoaderCallbacks = new LoaderManager.LoaderCallbacks<List<Anomaly>>() { @Override public Loader<List<Anomaly>> onCreateLoader(int id, Bundle args) { return new AnomalyLoader(getContext(), mStatsHelper); } @Override public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) { // show high usage preference if possible mAnomalyPreferenceController.updateAnomalyPreference(data); } @Override public void onLoaderReset(Loader<List<Anomaly>> loader) { } }; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); Loading @@ -130,6 +159,7 @@ public class PowerUsageSummary extends PowerUsageBase { mLastFullChargePref = (PowerGaugePreference) findPreference( KEY_TIME_SINCE_LAST_FULL_CHARGE); mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary); mAnomalyPreferenceController = new AnomalyPreferenceController(this); mBatteryUtils = BatteryUtils.getInstance(getContext()); Loading Loading @@ -163,6 +193,9 @@ public class PowerUsageSummary extends PowerUsageBase { @Override public boolean onPreferenceTreeClick(Preference preference) { if (mAnomalyPreferenceController.onPreferenceTreeClick(preference)) { return true; } if (KEY_BATTERY_HEADER.equals(preference.getKey())) { performBatteryHeaderClick(); return true; Loading Loading @@ -403,6 +436,8 @@ public class PowerUsageSummary extends PowerUsageBase { return; } getLoaderManager().initLoader(ANOMALY_LOADER, null, mAnomalyLoaderCallbacks); cacheRemoveAllPrefs(mAppListGroup); mAppListGroup.setOrderingAsAdded(false); boolean addedSome = false; Loading Loading @@ -690,6 +725,11 @@ public class PowerUsageSummary extends PowerUsageBase { } }; @Override public void onAnomalyHandled(Anomaly anomaly) { mAnomalyPreferenceController.hideAnomalyPreference(); } private static class SummaryProvider implements SummaryLoader.SummaryProvider { private final Context mContext; private final SummaryLoader mLoader; Loading
src/com/android/settings/fuelgauge/anomaly/Anomaly.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * 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.anomaly; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Data that represents an app has been detected as anomaly. It contains * * 1. Basic information of the app(i.e. uid, package name) * 2. Type of anomaly * 3. Data that has been detected as anomaly(i.e wakelock time) */ public class Anomaly implements Parcelable { @Retention(RetentionPolicy.SOURCE) @IntDef({AnomalyType.WAKE_LOCK}) public @interface AnomalyType { int WAKE_LOCK = 0; } @Retention(RetentionPolicy.SOURCE) @IntDef({AnomalyActionType.FORCE_STOP}) public @interface AnomalyActionType { int FORCE_STOP = 0; } /** * Type of this this anomaly */ public final int type; public final int uid; public final long wakelockTimeMs; /** * Display name of this anomaly, usually it is the app name */ public final String displayName; public final String packageName; private Anomaly(Builder builder) { type = builder.mType; uid = builder.mUid; displayName = builder.mDisplayName; packageName = builder.mPackageName; wakelockTimeMs = builder.mWakeLockTimeMs; } private Anomaly(Parcel in) { type = in.readInt(); uid = in.readInt(); displayName = in.readString(); packageName = in.readString(); wakelockTimeMs = in.readLong(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(type); dest.writeInt(uid); dest.writeString(displayName); dest.writeString(packageName); dest.writeLong(wakelockTimeMs); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Anomaly createFromParcel(Parcel in) { return new Anomaly(in); } public Anomaly[] newArray(int size) { return new Anomaly[size]; } }; public static final class Builder { @AnomalyType private int mType; private int mUid; private String mDisplayName; private String mPackageName; private long mWakeLockTimeMs; public Builder setType(@AnomalyType int type) { mType = type; return this; } public Builder setUid(int uid) { mUid = uid; return this; } public Builder setDisplayName(String displayName) { mDisplayName = displayName; return this; } public Builder setPackageName(String packageName) { mPackageName = packageName; return this; } public Builder setWakeLockTimeMs(long wakeLockTimeMs) { mWakeLockTimeMs = wakeLockTimeMs; return this; } public Anomaly build() { return new Anomaly(this); } } }
src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * 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.anomaly; 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 com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; /** * Dialog Fragment to show action dialog for each anomaly */ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener { private static final String ARG_ANOMALY = "anomaly"; @VisibleForTesting Anomaly mAnomaly; /** * Listener to give the control back to target fragment */ public interface AnomalyDialogListener { /** * This method is invoked once anomaly is handled, then target fragment could do * extra work. One example is that fragment could remove the anomaly preference * since it has been handled * * @param anomaly that has been handled */ void onAnomalyHandled(Anomaly anomaly); } public static AnomalyDialogFragment newInstance(Anomaly anomaly) { AnomalyDialogFragment dialogFragment = new AnomalyDialogFragment(); Bundle args = new Bundle(1); args.putParcelable(ARG_ANOMALY, anomaly); dialogFragment.setArguments(args); return dialogFragment; } @Override public int getMetricsCategory() { // TODO(b/37681923): add anomaly metric id return 0; } @Override public void onClick(DialogInterface dialog, int which) { final AnomalyDialogListener lsn = (AnomalyDialogListener) getTargetFragment(); if (lsn == null) { return; } final AnomalyAction anomalyAction = AnomalyUtils.getAnomalyAction(mAnomaly.type); anomalyAction.handlePositiveAction(mAnomaly.packageName); lsn.onAnomalyHandled(mAnomaly); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Bundle bundle = getArguments(); mAnomaly = bundle.getParcelable(ARG_ANOMALY); final Context context = getContext(); final AnomalyAction anomalyAction = AnomalyUtils.getAnomalyAction(mAnomaly.type); switch (anomalyAction.getActionType()) { case Anomaly.AnomalyActionType.FORCE_STOP: return new AlertDialog.Builder(context) .setTitle(R.string.force_stop_dlg_title) .setMessage(R.string.force_stop_dlg_text) .setPositiveButton(R.string.dlg_ok, this) .setNegativeButton(R.string.dlg_cancel, null) .create(); default: throw new IllegalArgumentException("unknown type " + mAnomaly.type); } } }
src/com/android/settings/fuelgauge/anomaly/AnomalyLoader.java 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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.anomaly; import android.content.Context; import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.utils.AsyncLoader; import java.util.ArrayList; import java.util.List; /** * Loader to compute which apps are anomaly and return a anomaly list. It will return * an empty list if there is no anomaly. */ //TODO(b/36924669): add test for this file, for now it seems there is nothing to test public class AnomalyLoader extends AsyncLoader<List<Anomaly>> { private BatteryStatsHelper mBatteryStatsHelper; public AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper) { super(context); mBatteryStatsHelper = batteryStatsHelper; } @Override protected void onDiscardResult(List<Anomaly> result) {} @Override public List<Anomaly> loadInBackground() { final List<Anomaly> anomalies = new ArrayList<>(); anomalies.addAll(new WakeLockAnomalyDetector().detectAnomalies(mBatteryStatsHelper)); return anomalies; } }