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