1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.utils; 18 19 import android.content.Context; 20 import android.icu.text.DateFormat; 21 import android.icu.text.MeasureFormat; 22 import android.icu.text.MeasureFormat.FormatWidth; 23 import android.icu.util.Measure; 24 import android.icu.util.MeasureUnit; 25 import android.support.annotation.Nullable; 26 import android.text.TextUtils; 27 28 import com.android.settingslib.R; 29 30 import java.time.Instant; 31 import java.util.Date; 32 import java.util.Locale; 33 import java.util.concurrent.TimeUnit; 34 35 /** Utility class for keeping power related strings consistent**/ 36 public class PowerUtil { 37 38 private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7); 39 private static final long FIFTEEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(15); 40 private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1); 41 private static final long TWO_DAYS_MILLIS = TimeUnit.DAYS.toMillis(2); 42 private static final long ONE_HOUR_MILLIS = TimeUnit.HOURS.toMillis(1); 43 44 /** 45 * This method produces the text used in various places throughout the system to describe the 46 * remaining battery life of the phone in a consistent manner. 47 * 48 * @param context 49 * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds. 50 * @param percentageString An optional percentage of battery remaining string. 51 * @param basedOnUsage Whether this estimate is based on usage or simple extrapolation. 52 * @return a properly formatted and localized string describing how much time remains 53 * before the battery runs out. 54 */ getBatteryRemainingStringFormatted(Context context, long drainTimeMs, @Nullable String percentageString, boolean basedOnUsage)55 public static String getBatteryRemainingStringFormatted(Context context, long drainTimeMs, 56 @Nullable String percentageString, boolean basedOnUsage) { 57 if (drainTimeMs > 0) { 58 if (drainTimeMs <= SEVEN_MINUTES_MILLIS) { 59 // show a imminent shutdown warning if less than 7 minutes remain 60 return getShutdownImminentString(context, percentageString); 61 } else if (drainTimeMs <= FIFTEEN_MINUTES_MILLIS) { 62 // show a less than 15 min remaining warning if appropriate 63 CharSequence timeString = StringUtil.formatElapsedTime(context, 64 FIFTEEN_MINUTES_MILLIS, 65 false /* withSeconds */); 66 return getUnderFifteenString(context, timeString, percentageString); 67 } else if (drainTimeMs >= TWO_DAYS_MILLIS) { 68 // just say more than two day if over 48 hours 69 return getMoreThanTwoDaysString(context, percentageString); 70 } else if (drainTimeMs >= ONE_DAY_MILLIS) { 71 // show remaining days & hours if more than a day 72 return getMoreThanOneDayString(context, drainTimeMs, 73 percentageString, basedOnUsage); 74 } else { 75 // show the time of day we think you'll run out 76 return getRegularTimeRemainingString(context, drainTimeMs, 77 percentageString, basedOnUsage); 78 } 79 } 80 return null; 81 } 82 getShutdownImminentString(Context context, String percentageString)83 private static String getShutdownImminentString(Context context, String percentageString) { 84 return TextUtils.isEmpty(percentageString) 85 ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent) 86 : context.getString( 87 R.string.power_remaining_duration_shutdown_imminent, 88 percentageString); 89 } 90 getUnderFifteenString(Context context, CharSequence timeString, String percentageString)91 private static String getUnderFifteenString(Context context, CharSequence timeString, 92 String percentageString) { 93 return TextUtils.isEmpty(percentageString) 94 ? context.getString(R.string.power_remaining_less_than_duration_only, timeString) 95 : context.getString( 96 R.string.power_remaining_less_than_duration, 97 timeString, 98 percentageString); 99 100 } 101 getMoreThanOneDayString(Context context, long drainTimeMs, String percentageString, boolean basedOnUsage)102 private static String getMoreThanOneDayString(Context context, long drainTimeMs, 103 String percentageString, boolean basedOnUsage) { 104 final long roundedTimeMs = roundTimeToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS); 105 CharSequence timeString = StringUtil.formatElapsedTime(context, 106 roundedTimeMs, 107 false /* withSeconds */); 108 109 if (TextUtils.isEmpty(percentageString)) { 110 int id = basedOnUsage 111 ? R.string.power_remaining_duration_only_enhanced 112 : R.string.power_remaining_duration_only; 113 return context.getString(id, timeString); 114 } else { 115 int id = basedOnUsage 116 ? R.string.power_discharging_duration_enhanced 117 : R.string.power_discharging_duration; 118 return context.getString(id, timeString, percentageString); 119 } 120 } 121 getMoreThanTwoDaysString(Context context, String percentageString)122 private static String getMoreThanTwoDaysString(Context context, String percentageString) { 123 final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0); 124 final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT); 125 126 final Measure daysMeasure = new Measure(2, MeasureUnit.DAY); 127 128 return TextUtils.isEmpty(percentageString) 129 ? context.getString(R.string.power_remaining_only_more_than_subtext, 130 frmt.formatMeasures(daysMeasure)) 131 : context.getString( 132 R.string.power_remaining_more_than_subtext, 133 frmt.formatMeasures(daysMeasure), 134 percentageString); 135 } 136 getRegularTimeRemainingString(Context context, long drainTimeMs, String percentageString, boolean basedOnUsage)137 private static String getRegularTimeRemainingString(Context context, long drainTimeMs, 138 String percentageString, boolean basedOnUsage) { 139 // Get the time of day we think device will die rounded to the nearest 15 min. 140 final long roundedTimeOfDayMs = 141 roundTimeToNearestThreshold( 142 System.currentTimeMillis() + drainTimeMs, 143 FIFTEEN_MINUTES_MILLIS); 144 145 // convert the time to a properly formatted string. 146 String skeleton = android.text.format.DateFormat.getTimeFormatString(context); 147 DateFormat fmt = DateFormat.getInstanceForSkeleton(skeleton); 148 Date date = Date.from(Instant.ofEpochMilli(roundedTimeOfDayMs)); 149 CharSequence timeString = fmt.format(date); 150 151 if (TextUtils.isEmpty(percentageString)) { 152 int id = basedOnUsage 153 ? R.string.power_discharge_by_only_enhanced 154 : R.string.power_discharge_by_only; 155 return context.getString(id, timeString); 156 } else { 157 int id = basedOnUsage 158 ? R.string.power_discharge_by_enhanced 159 : R.string.power_discharge_by; 160 return context.getString(id, timeString, percentageString); 161 } 162 } 163 convertUsToMs(long timeUs)164 public static long convertUsToMs(long timeUs) { 165 return timeUs / 1000; 166 } 167 convertMsToUs(long timeMs)168 public static long convertMsToUs(long timeMs) { 169 return timeMs * 1000; 170 } 171 172 /** 173 * Rounds a time to the nearest multiple of the provided threshold. Note: This function takes 174 * the absolute value of the inputs since it is only meant to be used for times, not general 175 * purpose rounding. 176 * 177 * ex: roundTimeToNearestThreshold(41, 24) = 48 178 * @param drainTime The amount to round 179 * @param threshold The value to round to a multiple of 180 * @return The rounded value as a long 181 */ roundTimeToNearestThreshold(long drainTime, long threshold)182 public static long roundTimeToNearestThreshold(long drainTime, long threshold) { 183 long time = Math.abs(drainTime); 184 long multiple = Math.abs(threshold); 185 final long remainder = time % multiple; 186 if (remainder < multiple / 2) { 187 return time - remainder; 188 } else { 189 return time - remainder + multiple; 190 } 191 } 192 } 193