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