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