TextView summary1 has default height 2 line. If the content is longer
than 2 line, the TextView increases itself which causes the animation.
By increasing minLines to 3, it can avoid the animation. And remove
summary2 because it is only for debug purpose, the debug information
can be merged to summary1.
Fixes: 139554919
Test: visual, make RunSettingsRoboTests
Change-Id: I167ac87c9bd83035e00d4991961599e76f4f69e1
(cherry picked from commit 648ada031d
)
Merged-In: I167ac87c9bd83035e00d4991961599e76f4f69e1
430 lines
16 KiB
Java
430 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2009 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;
|
|
|
|
import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType;
|
|
|
|
import android.app.settings.SettingsEnums;
|
|
import android.content.Context;
|
|
import android.database.ContentObserver;
|
|
import android.net.Uri;
|
|
import android.os.BatteryStats;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.provider.SearchIndexableResource;
|
|
import android.provider.Settings.Global;
|
|
import android.text.format.Formatter;
|
|
import android.view.Menu;
|
|
import android.view.MenuInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.View.OnLongClickListener;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.loader.app.LoaderManager;
|
|
import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
|
import androidx.loader.content.Loader;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.SettingsActivity;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.core.SubSettingLauncher;
|
|
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
|
|
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
|
|
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
|
|
import com.android.settings.overlay.FeatureFactory;
|
|
import com.android.settings.search.BaseSearchIndexProvider;
|
|
import com.android.settingslib.fuelgauge.EstimateKt;
|
|
import com.android.settingslib.search.SearchIndexable;
|
|
import com.android.settingslib.utils.PowerUtil;
|
|
import com.android.settingslib.utils.StringUtil;
|
|
import com.android.settingslib.widget.LayoutPreference;
|
|
|
|
import java.util.Collections;
|
|
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.
|
|
*/
|
|
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
|
public class PowerUsageSummary extends PowerUsageBase implements OnLongClickListener,
|
|
BatteryTipPreferenceController.BatteryTipListener {
|
|
|
|
static final String TAG = "PowerUsageSummary";
|
|
|
|
private static final boolean DEBUG = false;
|
|
private static final String KEY_BATTERY_HEADER = "battery_header";
|
|
|
|
private static final String KEY_SCREEN_USAGE = "screen_usage";
|
|
private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge";
|
|
private static final String KEY_BATTERY_SAVER_SUMMARY = "battery_saver_summary";
|
|
|
|
@VisibleForTesting
|
|
static final int BATTERY_INFO_LOADER = 1;
|
|
@VisibleForTesting
|
|
static final int BATTERY_TIP_LOADER = 2;
|
|
@VisibleForTesting
|
|
static final int MENU_STATS_TYPE = Menu.FIRST;
|
|
@VisibleForTesting
|
|
static final int MENU_ADVANCED_BATTERY = Menu.FIRST + 1;
|
|
public static final int DEBUG_INFO_LOADER = 3;
|
|
|
|
@VisibleForTesting
|
|
PowerGaugePreference mScreenUsagePref;
|
|
@VisibleForTesting
|
|
PowerGaugePreference mLastFullChargePref;
|
|
@VisibleForTesting
|
|
PowerUsageFeatureProvider mPowerFeatureProvider;
|
|
@VisibleForTesting
|
|
BatteryUtils mBatteryUtils;
|
|
@VisibleForTesting
|
|
LayoutPreference mBatteryLayoutPref;
|
|
@VisibleForTesting
|
|
BatteryInfo mBatteryInfo;
|
|
|
|
@VisibleForTesting
|
|
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
|
|
@VisibleForTesting
|
|
boolean mNeedUpdateBatteryTip;
|
|
@VisibleForTesting
|
|
BatteryTipPreferenceController mBatteryTipPreferenceController;
|
|
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
|
|
|
|
@VisibleForTesting
|
|
final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
|
|
@Override
|
|
public void onChange(boolean selfChange, Uri uri) {
|
|
restartBatteryInfoLoader();
|
|
}
|
|
};
|
|
|
|
@VisibleForTesting
|
|
LoaderManager.LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks =
|
|
new LoaderManager.LoaderCallbacks<BatteryInfo>() {
|
|
|
|
@Override
|
|
public Loader<BatteryInfo> onCreateLoader(int i, Bundle bundle) {
|
|
return new BatteryInfoLoader(getContext(), mStatsHelper);
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<BatteryInfo> loader, BatteryInfo batteryInfo) {
|
|
mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo);
|
|
mBatteryInfo = batteryInfo;
|
|
updateLastFullChargePreference();
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<BatteryInfo> loader) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
LoaderManager.LoaderCallbacks<List<BatteryInfo>> mBatteryInfoDebugLoaderCallbacks =
|
|
new LoaderCallbacks<List<BatteryInfo>>() {
|
|
@Override
|
|
public Loader<List<BatteryInfo>> onCreateLoader(int i, Bundle bundle) {
|
|
return new DebugEstimatesLoader(getContext(), mStatsHelper);
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<List<BatteryInfo>> loader,
|
|
List<BatteryInfo> batteryInfos) {
|
|
updateViews(batteryInfos);
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<List<BatteryInfo>> loader) {
|
|
}
|
|
};
|
|
|
|
protected void updateViews(List<BatteryInfo> batteryInfos) {
|
|
final BatteryMeterView batteryView = mBatteryLayoutPref
|
|
.findViewById(R.id.battery_header_icon);
|
|
final TextView percentRemaining =
|
|
mBatteryLayoutPref.findViewById(R.id.battery_percent);
|
|
final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
|
|
BatteryInfo oldInfo = batteryInfos.get(0);
|
|
BatteryInfo newInfo = batteryInfos.get(1);
|
|
percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel));
|
|
|
|
// set the text to the old estimate (copied from battery info). Note that this
|
|
// can sometimes say 0 time remaining because battery stats requires the phone
|
|
// be unplugged for a period of time before being willing ot make an estimate.
|
|
final String OldEstimateString = mPowerFeatureProvider.getOldEstimateDebugString(
|
|
Formatter.formatShortElapsedTime(getContext(),
|
|
PowerUtil.convertUsToMs(oldInfo.remainingTimeUs)));
|
|
final String NewEstimateString = mPowerFeatureProvider.getEnhancedEstimateDebugString(
|
|
Formatter.formatShortElapsedTime(getContext(),
|
|
PowerUtil.convertUsToMs(newInfo.remainingTimeUs)));
|
|
summary1.setText(OldEstimateString + "\n" + NewEstimateString);
|
|
|
|
batteryView.setBatteryLevel(oldInfo.batteryLevel);
|
|
batteryView.setCharging(!oldInfo.discharging);
|
|
}
|
|
|
|
private LoaderManager.LoaderCallbacks<List<BatteryTip>> mBatteryTipsCallbacks =
|
|
new LoaderManager.LoaderCallbacks<List<BatteryTip>>() {
|
|
|
|
@Override
|
|
public Loader<List<BatteryTip>> onCreateLoader(int id, Bundle args) {
|
|
return new BatteryTipLoader(getContext(), mStatsHelper);
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<List<BatteryTip>> loader,
|
|
List<BatteryTip> data) {
|
|
mBatteryTipPreferenceController.updateBatteryTips(data);
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<List<BatteryTip>> loader) {
|
|
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public void onAttach(Context context) {
|
|
super.onAttach(context);
|
|
final SettingsActivity activity = (SettingsActivity) getActivity();
|
|
|
|
mBatteryHeaderPreferenceController = use(BatteryHeaderPreferenceController.class);
|
|
mBatteryHeaderPreferenceController.setActivity(activity);
|
|
mBatteryHeaderPreferenceController.setFragment(this);
|
|
mBatteryHeaderPreferenceController.setLifecycle(getSettingsLifecycle());
|
|
|
|
mBatteryTipPreferenceController = use(BatteryTipPreferenceController.class);
|
|
mBatteryTipPreferenceController.setActivity(activity);
|
|
mBatteryTipPreferenceController.setFragment(this);
|
|
mBatteryTipPreferenceController.setBatteryTipListener(this::onBatteryTipHandled);
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle icicle) {
|
|
super.onCreate(icicle);
|
|
setAnimationAllowed(true);
|
|
|
|
initFeatureProvider();
|
|
mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER);
|
|
|
|
mScreenUsagePref = (PowerGaugePreference) findPreference(KEY_SCREEN_USAGE);
|
|
mLastFullChargePref = (PowerGaugePreference) findPreference(
|
|
KEY_TIME_SINCE_LAST_FULL_CHARGE);
|
|
mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary);
|
|
mBatteryUtils = BatteryUtils.getInstance(getContext());
|
|
|
|
restartBatteryInfoLoader();
|
|
mBatteryTipPreferenceController.restoreInstanceState(icicle);
|
|
updateBatteryTipFlag(icicle);
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
getContentResolver().registerContentObserver(
|
|
Global.getUriFor(Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
|
|
false,
|
|
mSettingsObserver);
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
getContentResolver().unregisterContentObserver(mSettingsObserver);
|
|
super.onPause();
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return SettingsEnums.FUELGAUGE_POWER_USAGE_SUMMARY_V2;
|
|
}
|
|
|
|
@Override
|
|
protected String getLogTag() {
|
|
return TAG;
|
|
}
|
|
|
|
@Override
|
|
protected int getPreferenceScreenResId() {
|
|
return R.xml.power_usage_summary;
|
|
}
|
|
|
|
@Override
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
if (DEBUG) {
|
|
menu.add(Menu.NONE, MENU_STATS_TYPE, Menu.NONE, R.string.menu_stats_total)
|
|
.setIcon(com.android.internal.R.drawable.ic_menu_info_details)
|
|
.setAlphabeticShortcut('t');
|
|
}
|
|
|
|
menu.add(Menu.NONE, MENU_ADVANCED_BATTERY, Menu.NONE, R.string.advanced_battery_title);
|
|
|
|
super.onCreateOptionsMenu(menu, inflater);
|
|
}
|
|
|
|
@Override
|
|
public int getHelpResource() {
|
|
return R.string.help_url_battery;
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case MENU_STATS_TYPE:
|
|
if (mStatsType == BatteryStats.STATS_SINCE_CHARGED) {
|
|
mStatsType = BatteryStats.STATS_SINCE_UNPLUGGED;
|
|
} else {
|
|
mStatsType = BatteryStats.STATS_SINCE_CHARGED;
|
|
}
|
|
refreshUi(BatteryUpdateType.MANUAL);
|
|
return true;
|
|
case MENU_ADVANCED_BATTERY:
|
|
new SubSettingLauncher(getContext())
|
|
.setDestination(PowerUsageAdvanced.class.getName())
|
|
.setSourceMetricsCategory(getMetricsCategory())
|
|
.setTitleRes(R.string.advanced_battery_title)
|
|
.launch();
|
|
return true;
|
|
default:
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
}
|
|
|
|
protected void refreshUi(@BatteryUpdateType int refreshType) {
|
|
final Context context = getContext();
|
|
if (context == null) {
|
|
return;
|
|
}
|
|
|
|
// Skip BatteryTipLoader if device is rotated or only battery level change
|
|
if (mNeedUpdateBatteryTip
|
|
&& refreshType != BatteryUpdateType.BATTERY_LEVEL) {
|
|
restartBatteryTipLoader();
|
|
} else {
|
|
mNeedUpdateBatteryTip = true;
|
|
}
|
|
|
|
// reload BatteryInfo and updateUI
|
|
restartBatteryInfoLoader();
|
|
updateLastFullChargePreference();
|
|
mScreenUsagePref.setSubtitle(StringUtil.formatElapsedTime(getContext(),
|
|
mBatteryUtils.calculateScreenUsageTime(mStatsHelper), false));
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void restartBatteryTipLoader() {
|
|
getLoaderManager().restartLoader(BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
|
|
mBatteryLayoutPref = layoutPreference;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void updateLastFullChargePreference() {
|
|
if (mBatteryInfo != null && mBatteryInfo.averageTimeToDischarge
|
|
!= EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN) {
|
|
mLastFullChargePref.setTitle(R.string.battery_full_charge_last);
|
|
mLastFullChargePref.setSubtitle(
|
|
StringUtil.formatElapsedTime(getContext(), mBatteryInfo.averageTimeToDischarge,
|
|
false /* withSeconds */));
|
|
} else {
|
|
final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper,
|
|
System.currentTimeMillis());
|
|
mLastFullChargePref.setTitle(R.string.battery_last_full_charge);
|
|
mLastFullChargePref.setSubtitle(
|
|
StringUtil.formatRelativeTime(getContext(), lastFullChargeTime,
|
|
false /* withSeconds */));
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void showBothEstimates() {
|
|
final Context context = getContext();
|
|
if (context == null
|
|
|| !mPowerFeatureProvider.isEnhancedBatteryPredictionEnabled(context)) {
|
|
return;
|
|
}
|
|
getLoaderManager().restartLoader(DEBUG_INFO_LOADER, Bundle.EMPTY,
|
|
mBatteryInfoDebugLoaderCallbacks);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void initFeatureProvider() {
|
|
final Context context = getContext();
|
|
mPowerFeatureProvider = FeatureFactory.getFactory(context)
|
|
.getPowerUsageFeatureProvider(context);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void restartBatteryInfoLoader() {
|
|
if (getContext() == null) {
|
|
return;
|
|
}
|
|
getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
|
|
mBatteryInfoLoaderCallbacks);
|
|
if (mPowerFeatureProvider.isEstimateDebugEnabled()) {
|
|
// Set long click action for summary to show debug info
|
|
View header = mBatteryLayoutPref.findViewById(R.id.summary1);
|
|
header.setOnLongClickListener(this);
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void updateBatteryTipFlag(Bundle icicle) {
|
|
mNeedUpdateBatteryTip = icicle == null || mBatteryTipPreferenceController.needUpdate();
|
|
}
|
|
|
|
@Override
|
|
public boolean onLongClick(View view) {
|
|
showBothEstimates();
|
|
view.setOnLongClickListener(null);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void restartBatteryStatsLoader(@BatteryUpdateType int refreshType) {
|
|
super.restartBatteryStatsLoader(refreshType);
|
|
mBatteryHeaderPreferenceController.quickUpdateHeaderPreference();
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
super.onSaveInstanceState(outState);
|
|
mBatteryTipPreferenceController.saveInstanceState(outState);
|
|
}
|
|
|
|
@Override
|
|
public void onBatteryTipHandled(BatteryTip batteryTip) {
|
|
restartBatteryTipLoader();
|
|
}
|
|
|
|
|
|
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
|
new BaseSearchIndexProvider() {
|
|
@Override
|
|
public List<SearchIndexableResource> getXmlResourcesToIndex(
|
|
Context context, boolean enabled) {
|
|
final SearchIndexableResource sir = new SearchIndexableResource(context);
|
|
sir.xmlResId = R.xml.power_usage_summary;
|
|
return Collections.singletonList(sir);
|
|
}
|
|
};
|
|
}
|