blob: 36d14650ddee60c646347d856271c6fcb2b83070 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package com.android.server.status;
18
19import com.android.internal.R;
20import com.android.internal.location.GpsLocationProvider;
21import com.android.internal.telephony.SimCard;
22import com.android.internal.telephony.TelephonyIntents;
23
24import android.app.AlertDialog;
25import android.bluetooth.BluetoothA2dp;
26import android.bluetooth.BluetoothDevice;
The Android Open Source Project10592532009-03-18 17:39:46 -070027import android.bluetooth.BluetoothError;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.bluetooth.BluetoothHeadset;
29import android.bluetooth.BluetoothIntent;
30import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.DialogInterface;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.res.TypedArray;
36import android.graphics.PixelFormat;
37import android.graphics.drawable.Drawable;
38import android.media.AudioManager;
39import android.net.NetworkInfo;
40import android.net.wifi.WifiManager;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.Message;
44import android.provider.Settings;
45import android.telephony.PhoneStateListener;
46import android.telephony.ServiceState;
47import android.telephony.TelephonyManager;
48import android.text.format.DateFormat;
49import android.util.Log;
50import android.view.View;
51import android.view.ViewGroup;
52import android.view.WindowManager;
53import android.view.WindowManagerImpl;
54import android.widget.ImageView;
55import android.widget.LinearLayout;
56import android.widget.TextView;
57
58import java.util.Calendar;
59import java.util.TimeZone;
60
61/**
62 * This class contains all of the policy about which icons are installed in the status
63 * bar at boot time. In reality, it should go into the android.policy package, but
64 * putting it here is the first step from extracting it.
65 */
66public class StatusBarPolicy {
67 private static final String TAG = "StatusBarPolicy";
68
69 private static StatusBarPolicy sInstance;
70
71 // message codes for the handler
72 private static final int EVENT_DATA_CONN_STATE_CHANGED = 2;
73 private static final int EVENT_DATA_ACTIVITY = 3;
74 private static final int EVENT_BATTERY_CLOSE = 4;
75
76 // indices into mBatteryThresholds
77 private static final int BATTERY_THRESHOLD_CLOSE_WARNING = 0;
78 private static final int BATTERY_THRESHOLD_WARNING = 1;
79 private static final int BATTERY_THRESHOLD_EMPTY = 2;
80
81 private Context mContext;
82 private StatusBarService mService;
83 private Handler mHandler = new StatusBarHandler();
84
85 // clock
86 private Calendar mCalendar;
87 private IBinder mClockIcon;
88 private IconData mClockData;
89
90 // battery
91 private IBinder mBatteryIcon;
92 private IconData mBatteryData;
93 private boolean mBatteryFirst = true;
94 private boolean mBatteryPlugged;
95 private int mBatteryLevel;
96 private int mBatteryThreshold = 0; // index into mBatteryThresholds
97 private int[] mBatteryThresholds = new int[] { 20, 15, -1 };
98 private AlertDialog mLowBatteryDialog;
99 private TextView mBatteryLevelTextView;
100 private View mBatteryView;
101 private int mBatteryViewSequence;
102 private boolean mBatteryShowLowOnEndCall = false;
103 private static final boolean SHOW_LOW_BATTERY_WARNING = true;
104
105 // phone
106 private TelephonyManager mPhone;
107 private IBinder mPhoneIcon;
108 private IconData mPhoneData;
109 private static final int[] sSignalImages = new int[] {
110 com.android.internal.R.drawable.stat_sys_signal_0,
111 com.android.internal.R.drawable.stat_sys_signal_1,
112 com.android.internal.R.drawable.stat_sys_signal_2,
113 com.android.internal.R.drawable.stat_sys_signal_3,
114 com.android.internal.R.drawable.stat_sys_signal_4
115 };
116 private static final int[] sSignalImages_r = new int[] {
117 com.android.internal.R.drawable.stat_sys_r_signal_0,
118 com.android.internal.R.drawable.stat_sys_r_signal_1,
119 com.android.internal.R.drawable.stat_sys_r_signal_2,
120 com.android.internal.R.drawable.stat_sys_r_signal_3,
121 com.android.internal.R.drawable.stat_sys_r_signal_4
122 };
123 private int[] mDataIconList = sDataNetType_g;
124 private static final int[] sDataNetType_g = new int[] {
125 com.android.internal.R.drawable.stat_sys_data_connected_g,
126 com.android.internal.R.drawable.stat_sys_data_in_g,
127 com.android.internal.R.drawable.stat_sys_data_out_g,
128 com.android.internal.R.drawable.stat_sys_data_inandout_g,
129 };
130 private static final int[] sDataNetType_3g = new int[] {
131 com.android.internal.R.drawable.stat_sys_data_connected_3g,
132 com.android.internal.R.drawable.stat_sys_data_in_3g,
133 com.android.internal.R.drawable.stat_sys_data_out_3g,
134 com.android.internal.R.drawable.stat_sys_data_inandout_3g,
135 };
136 private static final int[] sDataNetType_e = new int[] {
137 com.android.internal.R.drawable.stat_sys_data_connected_e,
138 com.android.internal.R.drawable.stat_sys_data_in_e,
139 com.android.internal.R.drawable.stat_sys_data_out_e,
140 com.android.internal.R.drawable.stat_sys_data_inandout_e,
141 };
142 // Assume it's all good unless we hear otherwise. We don't always seem
143 // to get broadcasts that it *is* there.
144 SimCard.State mSimState = SimCard.State.READY;
145 int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
146 int mDataState = TelephonyManager.DATA_DISCONNECTED;
147 int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
148 ServiceState mServiceState;
149 int mSignalAsu = -1;
150
151 // data connection
152 private IBinder mDataIcon;
153 private IconData mDataData;
154 private boolean mDataIconVisible;
155
156 // ringer volume
157 private IBinder mVolumeIcon;
158 private IconData mVolumeData;
159 private boolean mVolumeVisible;
160
161 // bluetooth device status
162 private IBinder mBluetoothIcon;
163 private IconData mBluetoothData;
164 private int mBluetoothHeadsetState;
165 private int mBluetoothA2dpState;
166 private boolean mBluetoothEnabled;
167
168 // wifi
169 private static final int[] sWifiSignalImages = new int[] {
170 com.android.internal.R.drawable.stat_sys_wifi_signal_1,
171 com.android.internal.R.drawable.stat_sys_wifi_signal_2,
172 com.android.internal.R.drawable.stat_sys_wifi_signal_3,
173 com.android.internal.R.drawable.stat_sys_wifi_signal_4,
174 };
175 private static final int sWifiTemporarilyNotConnectedImage =
176 com.android.internal.R.drawable.stat_sys_wifi_signal_0;
177
178 private int mLastWifiSignalLevel = -1;
179 private boolean mIsWifiConnected = false;
180 private IBinder mWifiIcon;
181 private IconData mWifiData;
182
183 // gps
184 private IBinder mGpsIcon;
185 private IconData mGpsEnabledIconData;
186 private IconData mGpsFixIconData;
187
188 // alarm clock
189 // Icon lit when clock is set
190 private IBinder mAlarmClockIcon;
191 private IconData mAlarmClockIconData;
192
193 // sync state
194 // If sync is active the SyncActive icon is displayed. If sync is not active but
195 // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
196 private IBinder mSyncActiveIcon;
197 private IBinder mSyncFailingIcon;
198
199 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
200 @Override
201 public void onReceive(Context context, Intent intent) {
202 String action = intent.getAction();
203 if (action.equals(Intent.ACTION_TIME_TICK)) {
204 updateClock();
205 }
206 else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
207 updateClock();
208 }
209 else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
210 updateClock();
211 }
212 else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
213 String tz = intent.getStringExtra("time-zone");
214 mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
215 updateClock();
216 }
217 else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
218 updateAlarm(intent);
219 }
220 else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
221 updateSyncState(intent);
222 }
223 else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
224 updateBattery(intent);
225 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700226 else if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION) ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION) ||
228 action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
229 updateBluetooth(intent);
230 }
231 else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
232 action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
233 action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
234 updateWifi(intent);
235 }
236 else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) ||
237 action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION)) {
238 updateGps(intent);
239 }
240 else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
241 action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) {
242 updateVolume();
243 }
244 else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
245 updateSimState(intent);
246 }
247 }
248 };
249
250 private StatusBarPolicy(Context context, StatusBarService service) {
251 mContext = context;
252 mService = service;
253
254 // clock
255 mCalendar = Calendar.getInstance(TimeZone.getDefault());
256 mClockData = IconData.makeText("clock", "");
257 mClockIcon = service.addIcon(mClockData, null);
258 updateClock();
259
260 // battery
261 mBatteryData = IconData.makeIcon("battery",
262 null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0);
263 mBatteryIcon = service.addIcon(mBatteryData, null);
264
265 // phone_signal
266 mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
267 mPhoneData = IconData.makeIcon("phone_signal",
268 null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
269 mPhoneIcon = service.addIcon(mPhoneData, null);
270 // register for phone state notifications.
271 ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
272 .listen(mPhoneStateListener,
273 PhoneStateListener.LISTEN_SERVICE_STATE
274 | PhoneStateListener.LISTEN_SIGNAL_STRENGTH
275 | PhoneStateListener.LISTEN_CALL_STATE
276 | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
277 | PhoneStateListener.LISTEN_DATA_ACTIVITY);
278
279 // data_connection
280 mDataData = IconData.makeIcon("data_connection",
281 null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
282 mDataIcon = service.addIcon(mDataData, null);
283 service.setIconVisibility(mDataIcon, false);
284
285 // wifi
286 mWifiData = IconData.makeIcon("wifi", null, sWifiSignalImages[0], 0, 0);
287 mWifiIcon = service.addIcon(mWifiData, null);
288 service.setIconVisibility(mWifiIcon, false);
289 // wifi will get updated by the sticky intents
290
291 // bluetooth status
292 mBluetoothData = IconData.makeIcon("bluetooth",
293 null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
294 mBluetoothIcon = service.addIcon(mBluetoothData, null);
295 BluetoothDevice bluetooth =
296 (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
297 if (bluetooth != null) {
298 mBluetoothEnabled = bluetooth.isEnabled();
299 } else {
300 mBluetoothEnabled = false;
301 }
302 mBluetoothA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
303 mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
304 mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
305
306 // Gps status
307 mGpsEnabledIconData = IconData.makeIcon("gps",
308 null, com.android.internal.R.drawable.stat_sys_gps_acquiring_anim, 0, 0);
309 mGpsFixIconData = IconData.makeIcon("gps",
310 null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0);
311 mGpsIcon = service.addIcon(mGpsEnabledIconData, null);
312 service.setIconVisibility(mGpsIcon, false);
313
314 // Alarm clock
315 mAlarmClockIconData = IconData.makeIcon(
316 "alarm_clock",
317 null, com.android.internal.R.drawable.stat_notify_alarm, 0, 0);
318 mAlarmClockIcon = service.addIcon(mAlarmClockIconData, null);
319 service.setIconVisibility(mAlarmClockIcon, false);
320
321 // Sync state
322 mSyncActiveIcon = service.addIcon(IconData.makeIcon("sync_active",
323 null, R.drawable.stat_notify_sync_anim0, 0, 0), null);
324 mSyncFailingIcon = service.addIcon(IconData.makeIcon("sync_failing",
325 null, R.drawable.stat_notify_sync_error, 0, 0), null);
326 service.setIconVisibility(mSyncActiveIcon, false);
327 service.setIconVisibility(mSyncFailingIcon, false);
328
329 // volume
330 mVolumeData = IconData.makeIcon("volume",
331 null, com.android.internal.R.drawable.stat_sys_ringer_silent, 0, 0);
332 mVolumeIcon = service.addIcon(mVolumeData, null);
333 service.setIconVisibility(mVolumeIcon, false);
334 updateVolume();
335
336 IntentFilter filter = new IntentFilter();
337
338 // Register for Intent broadcasts for...
339 filter.addAction(Intent.ACTION_TIME_TICK);
340 filter.addAction(Intent.ACTION_TIME_CHANGED);
341 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
342 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
343 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
344 filter.addAction(Intent.ACTION_ALARM_CHANGED);
345 filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
346 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
347 filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
The Android Open Source Project10592532009-03-18 17:39:46 -0700348 filter.addAction(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 filter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
350 filter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
351 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
352 filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
353 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
354 filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
355 filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
356 filter.addAction(GpsLocationProvider.GPS_FIX_CHANGE_ACTION);
357 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
358 mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
359 }
360
361 public static void installIcons(Context context, StatusBarService service) {
362 sInstance = new StatusBarPolicy(context, service);
363 }
364
365 private final void updateClock() {
366 mCalendar.setTimeInMillis(System.currentTimeMillis());
367 mClockData.text = DateFormat.getTimeFormat(mContext)
368 .format(mCalendar.getTime());
369 mService.updateIcon(mClockIcon, mClockData, null);
370 }
371
372 private final void updateAlarm(Intent intent) {
373 boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
374 mService.setIconVisibility(mAlarmClockIcon, alarmSet);
375 }
376
377 private final void updateSyncState(Intent intent) {
378 boolean isActive = intent.getBooleanExtra("active", false);
379 boolean isFailing = intent.getBooleanExtra("failing", false);
380 mService.setIconVisibility(mSyncActiveIcon, isActive);
381 // Don't display sync failing icon: BUG 1297963 Set sync error timeout to "never"
382 //mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
383 }
384
385 private void pickNextBatteryLevel(int level) {
386 final int N = mBatteryThresholds.length;
387 for (int i=0; i<N; i++) {
388 if (level >= mBatteryThresholds[i]) {
389 mBatteryThreshold = i;
390 break;
391 }
392 }
393 if (mBatteryThreshold >= N) {
394 mBatteryThreshold = N-1;
395 }
396 }
397
398 private final void updateBattery(Intent intent) {
399 mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
400 mBatteryData.iconLevel = intent.getIntExtra("level", 0);
401 mService.updateIcon(mBatteryIcon, mBatteryData, null);
402
403 boolean plugged = intent.getIntExtra("plugged", 0) != 0;
404 int level = intent.getIntExtra("level", -1);
405 if (false) {
406 Log.d(TAG, "updateBattery level=" + level
407 + " plugged=" + plugged
408 + " mBatteryPlugged=" + mBatteryPlugged
409 + " mBatteryLevel=" + mBatteryLevel
410 + " mBatteryThreshold=" + mBatteryThreshold
411 + " mBatteryFirst=" + mBatteryFirst);
412 }
413
414 boolean oldPlugged = mBatteryPlugged;
415 int oldThreshold = mBatteryThreshold;
416 pickNextBatteryLevel(level);
417
418 mBatteryPlugged = plugged;
419 mBatteryLevel = level;
420
421 if (mBatteryFirst) {
422 mBatteryFirst = false;
423 }
424 /*
425 * No longer showing the battery view because it draws attention away
426 * from the USB storage notification. We could still show it when
427 * connected to a brick, but that could lead to the user into thinking
428 * the device does not charge when plugged into USB (since he/she would
429 * not see the same battery screen on USB as he sees on brick).
430 */
431 /* else {
432 if (plugged && !oldPlugged) {
433 showBatteryView();
434 }
435 }
436 */
437 if (false) {
438 Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
439 + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
440 }
441 if (!plugged
442 && ((oldPlugged && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
443 || (mBatteryThreshold > oldThreshold
444 && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) {
445 // Broadcast the low battery warning
446 mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
447
448 if (SHOW_LOW_BATTERY_WARNING) {
449 if (false) {
450 Log.d(TAG, "mPhoneState=" + mPhoneState
451 + " mLowBatteryDialog=" + mLowBatteryDialog
452 + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
453 }
454
455 if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
456 showLowBatteryWarning();
457 } else {
458 mBatteryShowLowOnEndCall = true;
459 }
460 }
461 } else if (mBatteryThreshold == BATTERY_THRESHOLD_CLOSE_WARNING) {
462 if (SHOW_LOW_BATTERY_WARNING) {
463 if (mLowBatteryDialog != null) {
464 mLowBatteryDialog.dismiss();
465 mBatteryShowLowOnEndCall = false;
466 }
467 }
468 }
469 }
470
471 private void showBatteryView() {
472 closeLastBatteryView();
473 if (mLowBatteryDialog != null) {
474 mLowBatteryDialog.dismiss();
475 }
476
477 int level = mBatteryLevel;
478
479 View v = View.inflate(mContext, com.android.internal.R.layout.battery_status, null);
480 mBatteryView = v;
481 int pixelFormat = PixelFormat.TRANSLUCENT;
482 Drawable bg = v.getBackground();
483 if (bg != null) {
484 pixelFormat = bg.getOpacity();
485 }
486
487 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
488 ViewGroup.LayoutParams.WRAP_CONTENT,
489 ViewGroup.LayoutParams.WRAP_CONTENT,
490 WindowManager.LayoutParams.TYPE_TOAST,
491 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
492 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
493 | WindowManager.LayoutParams.FLAG_BLUR_BEHIND
494 | WindowManager.LayoutParams.FLAG_DIM_BEHIND,
495 pixelFormat);
496
497 // Get the dim amount from the theme
498 TypedArray a = mContext.obtainStyledAttributes(
499 com.android.internal.R.styleable.Theme);
500 lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
501 a.recycle();
502
503 lp.setTitle("Battery");
504
505 TextView levelTextView = (TextView)v.findViewById(com.android.internal.R.id.level_percent);
506 levelTextView.setText(mContext.getString(
507 com.android.internal.R.string.battery_status_text_percent_format, level));
508
509 setBatteryLevel(v, com.android.internal.R.id.spacer, 100-level, 0, 0);
510 setBatteryLevel(v, com.android.internal.R.id.level, level,
511 com.android.internal.R.drawable.battery_charge_fill, level);
512
513 WindowManagerImpl.getDefault().addView(v, lp);
514
515 scheduleCloseBatteryView();
516 }
517
518 private void setBatteryLevel(View parent, int id, int height, int background, int level) {
519 ImageView v = (ImageView)parent.findViewById(id);
520 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
521 lp.weight = height;
522 if (background != 0) {
523 v.setBackgroundResource(background);
524 Drawable bkg = v.getBackground();
525 bkg.setLevel(level);
526 }
527 }
528
529 private void showLowBatteryWarning() {
530 closeLastBatteryView();
531
532 int level = mBatteryThresholds[mBatteryThreshold > 1 ? mBatteryThreshold - 1 : 0];
533 CharSequence levelText = mContext.getString(
534 com.android.internal.R.string.battery_low_percent_format, level);
535
536 if (mBatteryLevelTextView != null) {
537 mBatteryLevelTextView.setText(levelText);
538 } else {
539 View v = View.inflate(mContext, com.android.internal.R.layout.battery_low, null);
540 mBatteryLevelTextView=(TextView)v.findViewById(com.android.internal.R.id.level_percent);
541
542 mBatteryLevelTextView.setText(levelText);
543
544 AlertDialog.Builder b = new AlertDialog.Builder(mContext);
545 b.setCancelable(true);
546 b.setTitle(com.android.internal.R.string.battery_low_title);
547 b.setView(v);
548 b.setIcon(android.R.drawable.ic_dialog_alert);
549 b.setPositiveButton(android.R.string.ok, null);
550
551 AlertDialog d = b.create();
552 d.setOnDismissListener(mLowBatteryListener);
553 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
554 d.show();
555 mLowBatteryDialog = d;
556 }
557 }
558
559 private final void updateCallState(int state) {
560 mPhoneState = state;
561 if (false) {
562 Log.d(TAG, "mPhoneState=" + mPhoneState
563 + " mLowBatteryDialog=" + mLowBatteryDialog
564 + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
565 }
566 if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
567 if (mBatteryShowLowOnEndCall) {
568 if (!mBatteryPlugged) {
569 showLowBatteryWarning();
570 }
571 mBatteryShowLowOnEndCall = false;
572 }
573 } else {
574 if (mLowBatteryDialog != null) {
575 mLowBatteryDialog.dismiss();
576 mBatteryShowLowOnEndCall = true;
577 }
578 }
579 }
580
581 private DialogInterface.OnDismissListener mLowBatteryListener
582 = new DialogInterface.OnDismissListener() {
583 public void onDismiss(DialogInterface dialog) {
584 mLowBatteryDialog = null;
585 mBatteryLevelTextView = null;
586 }
587 };
588
589 private void scheduleCloseBatteryView() {
590 Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
591 m.arg1 = (++mBatteryViewSequence);
592 mHandler.sendMessageDelayed(m, 3000);
593 }
594
595 private void closeLastBatteryView() {
596 if (mBatteryView != null) {
597 //mBatteryView.debug();
598 WindowManagerImpl.getDefault().removeView(mBatteryView);
599 mBatteryView = null;
600 }
601 }
602
603 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
604 @Override
605 public void onSignalStrengthChanged(int asu) {
606 mSignalAsu = asu;
607 updateSignalStrength();
608 }
609
610 @Override
611 public void onServiceStateChanged(ServiceState state) {
612 mServiceState = state;
613 updateSignalStrength();
614 updateDataIcon();
615 }
616
617 @Override
618 public void onCallStateChanged(int state, String incomingNumber) {
619 updateCallState(state);
620 }
621
622 @Override
623 public void onDataConnectionStateChanged(int state) {
624 mDataState = state;
625 updateDataNetType();
626 updateDataIcon();
627 }
628
629 @Override
630 public void onDataActivity(int direction) {
631 mDataActivity = direction;
632 updateDataIcon();
633 }
634 };
635
636
637 private final void updateSimState(Intent intent) {
638 String stateExtra = intent.getStringExtra(SimCard.INTENT_KEY_SIM_STATE);
639 if (SimCard.INTENT_VALUE_SIM_ABSENT.equals(stateExtra)) {
640 mSimState = SimCard.State.ABSENT;
641 }
642 else if (SimCard.INTENT_VALUE_SIM_READY.equals(stateExtra)) {
643 mSimState = SimCard.State.READY;
644 }
645 else if (SimCard.INTENT_VALUE_SIM_LOCKED.equals(stateExtra)) {
646 final String lockedReason = intent.getStringExtra(SimCard.INTENT_KEY_LOCKED_REASON);
647 if (SimCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
648 mSimState = SimCard.State.PIN_REQUIRED;
649 }
650 else if (SimCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
651 mSimState = SimCard.State.PUK_REQUIRED;
652 }
653 else {
654 mSimState = SimCard.State.NETWORK_LOCKED;
655 }
656 } else {
657 mSimState = SimCard.State.UNKNOWN;
658 }
659 updateDataIcon();
660 }
661
662 private final void updateSignalStrength() {
663 int asu = mSignalAsu;
664 ServiceState ss = mServiceState;
665
666 boolean hasService = true;
667
668 if (ss != null) {
669 int state = ss.getState();
670 switch (state) {
671 case ServiceState.STATE_OUT_OF_SERVICE:
672 case ServiceState.STATE_POWER_OFF:
673 hasService = false;
674 break;
675 }
676 } else {
677 hasService = false;
678 }
679
680 if (!hasService) {
681 //Log.d(TAG, "updateSignalStrength: no service");
682 if (Settings.System.getInt(mContext.getContentResolver(),
683 Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
684 mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_flightmode;
685 } else {
686 mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
687 }
688 mService.updateIcon(mPhoneIcon, mPhoneData, null);
689 return;
690 }
691
692 // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
693 // asu = 0 (-113dB or less) is very weak
694 // signal, its better to show 0 bars to the user in such cases.
695 // asu = 99 is a special case, where the signal strength is unknown.
696 if (asu <= 0 || asu == 99) asu = 0;
697 else if (asu >= 16) asu = 4;
698 else if (asu >= 8) asu = 3;
699 else if (asu >= 4) asu = 2;
700 else asu = 1;
701
702 int[] iconList;
703 if (mPhone.isNetworkRoaming()) {
704 iconList = sSignalImages_r;
705 } else {
706 iconList = sSignalImages;
707 }
708
709 mPhoneData.iconId = iconList[asu];
710 mService.updateIcon(mPhoneIcon, mPhoneData, null);
711 }
712
713 private final void updateDataNetType() {
714 int net = mPhone.getNetworkType();
715 switch (net) {
716 case TelephonyManager.NETWORK_TYPE_EDGE:
717 mDataIconList = sDataNetType_e;
718 break;
719 case TelephonyManager.NETWORK_TYPE_UMTS:
720 mDataIconList = sDataNetType_3g;
721 break;
722 default:
723 mDataIconList = sDataNetType_g;
724 break;
725 }
726 }
727
728 private final void updateDataIcon() {
729 int iconId;
730 boolean visible = true;
731
732 if (mSimState == SimCard.State.READY || mSimState == SimCard.State.UNKNOWN) {
733 int data = mDataState;
734
735 int[] list = mDataIconList;
736
737 ServiceState ss = mServiceState;
738
739 boolean hasService = false;
740
741 if (ss != null) {
742 hasService = (ss.getState() == ServiceState.STATE_IN_SERVICE);
743 }
744
745 if (hasService && data == TelephonyManager.DATA_CONNECTED) {
746 switch (mDataActivity) {
747 case TelephonyManager.DATA_ACTIVITY_IN:
748 iconId = list[1];
749 break;
750 case TelephonyManager.DATA_ACTIVITY_OUT:
751 iconId = list[2];
752 break;
753 case TelephonyManager.DATA_ACTIVITY_INOUT:
754 iconId = list[3];
755 break;
756 default:
757 iconId = list[0];
758 break;
759 }
760 mDataData.iconId = iconId;
761 mService.updateIcon(mDataIcon, mDataData, null);
762 } else {
763 visible = false;
764 }
765 } else {
766 mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
767 mService.updateIcon(mDataIcon, mDataData, null);
768 }
769 if (mDataIconVisible != visible) {
770 mService.setIconVisibility(mDataIcon, visible);
771 mDataIconVisible = visible;
772 }
773 }
774
775 private final void updateVolume() {
776 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
777 final int ringerMode = audioManager.getRingerMode();
778 final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
779 ringerMode == AudioManager.RINGER_MODE_VIBRATE;
780 final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)
781 ? com.android.internal.R.drawable.stat_sys_ringer_vibrate
782 : com.android.internal.R.drawable.stat_sys_ringer_silent;
783
784 if (visible) {
785 mVolumeData.iconId = iconId;
786 mService.updateIcon(mVolumeIcon, mVolumeData, null);
787 }
788 if (visible != mVolumeVisible) {
789 mService.setIconVisibility(mVolumeIcon, visible);
790 mVolumeVisible = visible;
791 }
792 }
793
794 private final void updateBluetooth(Intent intent) {
795 int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
796
797 String action = intent.getAction();
The Android Open Source Project10592532009-03-18 17:39:46 -0700798 if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)) {
799 int state = intent.getIntExtra(BluetoothIntent.BLUETOOTH_STATE,
800 BluetoothError.ERROR);
801 mBluetoothEnabled = state == BluetoothDevice.BLUETOOTH_STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
803 mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
804 BluetoothHeadset.STATE_ERROR);
805 } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
806 mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.SINK_STATE,
807 BluetoothA2dp.STATE_DISCONNECTED);
808 } else {
809 return;
810 }
811
812 if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED ||
813 mBluetoothA2dpState == BluetoothA2dp.STATE_CONNECTED ||
814 mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING) {
815 iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
816 }
817
818 mBluetoothData.iconId = iconId;
819 mService.updateIcon(mBluetoothIcon, mBluetoothData, null);
820 mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
821 }
822
823 private final void updateWifi(Intent intent) {
824 final String action = intent.getAction();
825 if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
826
827 final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
828 WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
829
830 if (!enabled) {
831 // If disabled, hide the icon. (We show icon when connected.)
832 mService.setIconVisibility(mWifiIcon, false);
833 }
834
835 } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
836 final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
837 false);
838 if (!enabled) {
839 mService.setIconVisibility(mWifiIcon, false);
840 }
841 } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
842
843 final NetworkInfo networkInfo = (NetworkInfo)
844 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
845
846 int iconId;
847 if (networkInfo != null && networkInfo.isConnected()) {
848 mIsWifiConnected = true;
849 if (mLastWifiSignalLevel == -1) {
850 iconId = sWifiSignalImages[0];
851 } else {
852 iconId = sWifiSignalImages[mLastWifiSignalLevel];
853 }
854
855 // Show the icon since wi-fi is connected
856 mService.setIconVisibility(mWifiIcon, true);
857
858 } else {
859 mLastWifiSignalLevel = -1;
860 mIsWifiConnected = false;
861 iconId = sWifiSignalImages[0];
862
863 // Hide the icon since we're not connected
864 mService.setIconVisibility(mWifiIcon, false);
865 }
866
867 mWifiData.iconId = iconId;
868 mService.updateIcon(mWifiIcon, mWifiData, null);
869 } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
870 final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
871 int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
872 sWifiSignalImages.length);
873 if (newSignalLevel != mLastWifiSignalLevel) {
874 mLastWifiSignalLevel = newSignalLevel;
875 if (mIsWifiConnected) {
876 mWifiData.iconId = sWifiSignalImages[newSignalLevel];
877 } else {
878 mWifiData.iconId = sWifiTemporarilyNotConnectedImage;
879 }
880 mService.updateIcon(mWifiIcon, mWifiData, null);
881 }
882 }
883 }
884
885 private final void updateGps(Intent intent) {
886 final String action = intent.getAction();
887 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false);
888
889 if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) {
890 // GPS is getting fixes
891 mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
892 mService.setIconVisibility(mGpsIcon, true);
893 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
894 // GPS is off
895 mService.setIconVisibility(mGpsIcon, false);
896 } else {
897 // GPS is on, but not receiving fixes
898 mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null);
899 mService.setIconVisibility(mGpsIcon, true);
900 }
901 }
902
903 private class StatusBarHandler extends Handler {
904 @Override
905 public void handleMessage(Message msg) {
906 switch (msg.what) {
907 case EVENT_BATTERY_CLOSE:
908 if (msg.arg1 == mBatteryViewSequence) {
909 closeLastBatteryView();
910 }
911 break;
912 }
913 }
914 }
915}