1 /* 2 * Copyright (C) 2008 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.systemui.power; 18 19 import java.io.FileDescriptor; 20 import java.io.PrintWriter; 21 import java.util.Arrays; 22 23 import android.app.AlertDialog; 24 import android.content.BroadcastReceiver; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.content.DialogInterface; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.net.Uri; 31 import android.os.BatteryManager; 32 import android.os.Handler; 33 import android.os.UserHandle; 34 import android.media.AudioManager; 35 import android.media.Ringtone; 36 import android.media.RingtoneManager; 37 import android.provider.Settings; 38 import android.util.Slog; 39 import android.view.View; 40 import android.view.WindowManager; 41 import android.widget.TextView; 42 43 import com.android.systemui.R; 44 import com.android.systemui.SystemUI; 45 46 public class PowerUI extends SystemUI { 47 static final String TAG = "PowerUI"; 48 49 static final boolean DEBUG = false; 50 51 Handler mHandler = new Handler(); 52 53 int mBatteryLevel = 100; 54 int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; 55 int mPlugType = 0; 56 int mInvalidCharger = 0; 57 58 int mLowBatteryAlertCloseLevel; 59 int[] mLowBatteryReminderLevels = new int[2]; 60 61 AlertDialog mInvalidChargerDialog; 62 AlertDialog mLowBatteryDialog; 63 TextView mBatteryLevelTextView; 64 65 public void start() { 66 67 mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( 68 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel); 69 mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( 70 com.android.internal.R.integer.config_lowBatteryWarningLevel); 71 mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( 72 com.android.internal.R.integer.config_criticalBatteryWarningLevel); 73 74 // Register for Intent broadcasts for... 75 IntentFilter filter = new IntentFilter(); 76 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 77 filter.addAction(Intent.ACTION_POWER_CONNECTED); 78 mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); 79 } 80 81 /** 82 * Buckets the battery level. 83 * 84 * The code in this function is a little weird because I couldn't comprehend 85 * the bucket going up when the battery level was going down. --joeo 86 * 87 * 1 means that the battery is "ok" 88 * 0 means that the battery is between "ok" and what we should warn about. 89 * less than 0 means that the battery is low 90 */ 91 private int findBatteryLevelBucket(int level) { 92 if (level >= mLowBatteryAlertCloseLevel) { 93 return 1; 94 } 95 if (level >= mLowBatteryReminderLevels[0]) { 96 return 0; 97 } 98 final int N = mLowBatteryReminderLevels.length; 99 for (int i=N-1; i>=0; i--) { 100 if (level <= mLowBatteryReminderLevels[i]) { 101 return -1-i; 102 } 103 } 104 throw new RuntimeException("not possible!"); 105 } 106 107 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 108 @Override 109 public void onReceive(Context context, Intent intent) { 110 String action = intent.getAction(); 111 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 112 final int oldBatteryLevel = mBatteryLevel; 113 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100); 114 final int oldBatteryStatus = mBatteryStatus; 115 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS, 116 BatteryManager.BATTERY_STATUS_UNKNOWN); 117 final int oldPlugType = mPlugType; 118 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1); 119 final int oldInvalidCharger = mInvalidCharger; 120 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0); 121 122 final boolean plugged = mPlugType != 0; 123 final boolean oldPlugged = oldPlugType != 0; 124 125 int oldBucket = findBatteryLevelBucket(oldBatteryLevel); 126 int bucket = findBatteryLevelBucket(mBatteryLevel); 127 128 if (DEBUG) { 129 Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel 130 + " .. " + mLowBatteryReminderLevels[0] 131 + " .. " + mLowBatteryReminderLevels[1]); 132 Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel); 133 Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus); 134 Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType); 135 Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger); 136 Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket); 137 Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged); 138 } 139 140 if (oldInvalidCharger == 0 && mInvalidCharger != 0) { 141 Slog.d(TAG, "showing invalid charger warning"); 142 showInvalidChargerDialog(); 143 return; 144 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) { 145 dismissInvalidChargerDialog(); 146 } else if (mInvalidChargerDialog != null) { 147 // if invalid charger is showing, don't show low battery 148 return; 149 } 150 151 if (!plugged 152 && (bucket < oldBucket || oldPlugged) 153 && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN 154 && bucket < 0) { 155 showLowBatteryWarning(); 156 157 // only play SFX when the dialog comes up or the bucket changes 158 if (bucket != oldBucket || oldPlugged) { 159 playLowBatterySound(); 160 } 161 } else if (plugged || (bucket > oldBucket && bucket > 0)) { 162 dismissLowBatteryWarning(); 163 } else if (mBatteryLevelTextView != null) { 164 showLowBatteryWarning(); 165 } 166 } else { 167 Slog.w(TAG, "unknown intent: " + intent); 168 } 169 } 170 }; 171 172 void dismissLowBatteryWarning() { 173 if (mLowBatteryDialog != null) { 174 Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel); 175 mLowBatteryDialog.dismiss(); 176 } 177 } 178 179 void showLowBatteryWarning() { 180 Slog.i(TAG, 181 ((mBatteryLevelTextView == null) ? "showing" : "updating") 182 + " low battery warning: level=" + mBatteryLevel 183 + " [" + findBatteryLevelBucket(mBatteryLevel) + "]"); 184 185 CharSequence levelText = mContext.getString( 186 R.string.battery_low_percent_format, mBatteryLevel); 187 188 if (mBatteryLevelTextView != null) { 189 mBatteryLevelTextView.setText(levelText); 190 } else { 191 View v = View.inflate(mContext, R.layout.battery_low, null); 192 mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent); 193 194 mBatteryLevelTextView.setText(levelText); 195 196 AlertDialog.Builder b = new AlertDialog.Builder(mContext); 197 b.setCancelable(true); 198 b.setTitle(R.string.battery_low_title); 199 b.setView(v); 200 b.setIconAttribute(android.R.attr.alertDialogIcon); 201 b.setPositiveButton(android.R.string.ok, null); 202 203 final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); 204 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 205 | Intent.FLAG_ACTIVITY_MULTIPLE_TASK 206 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 207 | Intent.FLAG_ACTIVITY_NO_HISTORY); 208 if (intent.resolveActivity(mContext.getPackageManager()) != null) { 209 b.setNegativeButton(R.string.battery_low_why, 210 new DialogInterface.OnClickListener() { 211 @Override 212 public void onClick(DialogInterface dialog, int which) { 213 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 214 dismissLowBatteryWarning(); 215 } 216 }); 217 } 218 219 AlertDialog d = b.create(); 220 d.setOnDismissListener(new DialogInterface.OnDismissListener() { 221 @Override 222 public void onDismiss(DialogInterface dialog) { 223 mLowBatteryDialog = null; 224 mBatteryLevelTextView = null; 225 } 226 }); 227 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 228 d.getWindow().getAttributes().privateFlags |= 229 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; 230 d.show(); 231 mLowBatteryDialog = d; 232 } 233 } 234 235 void playLowBatterySound() { 236 if (DEBUG) { 237 Slog.i(TAG, "playing low battery sound. WOMP-WOMP!"); 238 } 239 240 final ContentResolver cr = mContext.getContentResolver(); 241 if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) { 242 final String soundPath = Settings.Global.getString(cr, 243 Settings.Global.LOW_BATTERY_SOUND); 244 if (soundPath != null) { 245 final Uri soundUri = Uri.parse("file://" + soundPath); 246 if (soundUri != null) { 247 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); 248 if (sfx != null) { 249 sfx.setStreamType(AudioManager.STREAM_SYSTEM); 250 sfx.play(); 251 } 252 } 253 } 254 } 255 } 256 257 void dismissInvalidChargerDialog() { 258 if (mInvalidChargerDialog != null) { 259 mInvalidChargerDialog.dismiss(); 260 } 261 } 262 263 void showInvalidChargerDialog() { 264 Slog.d(TAG, "showing invalid charger dialog"); 265 266 dismissLowBatteryWarning(); 267 268 AlertDialog.Builder b = new AlertDialog.Builder(mContext); 269 b.setCancelable(true); 270 b.setMessage(R.string.invalid_charger); 271 b.setIconAttribute(android.R.attr.alertDialogIcon); 272 b.setPositiveButton(android.R.string.ok, null); 273 274 AlertDialog d = b.create(); 275 d.setOnDismissListener(new DialogInterface.OnDismissListener() { 276 public void onDismiss(DialogInterface dialog) { 277 mInvalidChargerDialog = null; 278 mBatteryLevelTextView = null; 279 } 280 }); 281 282 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 283 d.show(); 284 mInvalidChargerDialog = d; 285 } 286 287 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 288 pw.print("mLowBatteryAlertCloseLevel="); 289 pw.println(mLowBatteryAlertCloseLevel); 290 pw.print("mLowBatteryReminderLevels="); 291 pw.println(Arrays.toString(mLowBatteryReminderLevels)); 292 pw.print("mInvalidChargerDialog="); 293 pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString()); 294 pw.print("mLowBatteryDialog="); 295 pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString()); 296 pw.print("mBatteryLevel="); 297 pw.println(Integer.toString(mBatteryLevel)); 298 pw.print("mBatteryStatus="); 299 pw.println(Integer.toString(mBatteryStatus)); 300 pw.print("mPlugType="); 301 pw.println(Integer.toString(mPlugType)); 302 pw.print("mInvalidCharger="); 303 pw.println(Integer.toString(mInvalidCharger)); 304 pw.print("bucket: "); 305 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel))); 306 } 307 } 308 309