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