一、启动流程
StatusBar的启动流程是一个View创建,状态设置,添加到WindowManager的过程:
SystemServer通过下面的代码启动SystemUIService:
- static final void startSystemUi(Context context) {
- Intent intent = new Intent();
- intent.setComponent(new ComponentName("com.android.systemui",
- "com.android.systemui.SystemUIService"));
- //Slog.d(TAG, "Starting service: " + intent);
- context.startServiceAsUser(intent, UserHandle.OWNER);
- }
- private final Class<?>[] SERVICES = new Class[] {
- com.android.systemui.recent.Recents.class,
- com.android.systemui.statusbar.SystemBars.class,
- com.android.systemui.usb.StorageNotification.class,
- com.android.systemui.power.PowerUI.class,
- com.android.systemui.media.RingtonePlayer.class,
- com.android.systemui.settings.SettingsUI.class,
- };
那SystemBars是怎么启动了PhoneStatusBar的呢?分析如下的代码:
- private void createStatusBarFromConfig() {
- if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
- final String clsName = mContext.getString(R.string.config_statusBarComponent);
- if (clsName == null || clsName.length() == 0) {
- throw andLog("No status bar component configured", null);
- }
- Class<?> cls = null;
- try {
- cls = mContext.getClassLoader().loadClass(clsName);
- } catch (Throwable t) {
- throw andLog("Error loading status bar component: " + clsName, t);
- }
- try {
- mStatusBar = (BaseStatusBar) cls.newInstance();
- } catch (Throwable t) {
- throw andLog("Error creating status bar component: " + clsName, t);
- }
- mStatusBar.mContext = mContext;
- mStatusBar.mComponents = mComponents;
- mStatusBar.start();
- if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
- }
- <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
在Android4.2中的statusBar包括:TabletStatusBar,PhoneStatusBar, TvStatusBar,而在4.4上Phone与Tablet合并为PhoneStatusBar。
我们再看一下PhoneStatusBar的代码:
- public void start() {
- mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay();
- updateDisplaySize();//<span style="font-family:宋体;">JPH:</span>获取屏幕大小
- super.start(); // calls createAndAddWindows()
- addNavigationBar();
- // Lastly, call to the icon policy to install/update all the icons.
- mIconPolicy = new PhoneStatusBarPolicy(mContext);
- <span style="font-family:宋体;">//JPH:在Status Bar上显示Notification</span>
- mHeadsUpObserver.onChange(true); // set up
- if (ENABLE_HEADS_UP) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP), true,
- mHeadsUpObserver);
- }
- }
我们看一下BaseStatusBar的start方法:
- public void start() {
- mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
- mDisplay = mWindowManager.getDefaultDisplay();
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mProvisioningObserver.onChange(false); // set up
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
- mProvisioningObserver);
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mRecents = getComponent(RecentsComponent.class);
- mLocale = mContext.getResources().getConfiguration().locale;
- mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
- // Connect in to the status bar manager service
- StatusBarIconList iconList = new StatusBarIconList();
- ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
- ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
- mCommandQueue = new CommandQueue(this, iconList);
- int[] switches = new int[7];
- ArrayList<IBinder> binders = new ArrayList<IBinder>();
- try {
- mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
- switches, binders);
- } catch (RemoteException ex) {
- // If the system process isn't there we're doomed anyway.
- }
- createAndAddWindows();
- disable(switches[0]);
- setSystemUiVisibility(switches[1], 0xffffffff);
- topAppWindowChanged(switches[2] != 0);
- // StatusBarManagerService has a back up of IME token and it's restored here.
- setImeWindowStatus(binders.get(0), switches[3], switches[4]);
- setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
- // Set up the initial icon state
- int N = iconList.size();
- int viewIndex = 0;
- for (int i=0; i<N; i++) {
- StatusBarIcon icon = iconList.getIcon(i);
- if (icon != null) {
- addIcon(iconList.getSlot(i), i, viewIndex, icon);
- viewIndex++;
- }
- }
- // Set up the initial notification state
- N = notificationKeys.size();
- if (N == notifications.size()) {
- for (int i=0; i<N; i++) {
- addNotification(notificationKeys.get(i), notifications.get(i));
- }
- } else {
- Log.wtf(TAG, "Notification list length mismatch: keys=" + N
- + " notifications=" + notifications.size());
- }
- if (DEBUG) {
- Log.d(TAG, String.format(
- "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
- iconList.size(),
- switches[0],
- switches[1],
- switches[2],
- switches[3]
- ));
- }
- mCurrentUserId = ActivityManager.getCurrentUser();
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
在这个方法中调用了registerStatusBar到StatusManagerService,用来获取status Icon,status Bar的部分功能关闭(clock,Expand panel, notification icon,还有Navigation bar上的返回键,Home键等等),systemUI 显示配置,topAppWindowChange,这些都是StatusManager对其它程序提供的操作接口,在以下章节我们再来分析status bar icon的加载与显示。
现在看一下PhoneStatusBar下的makeStatusBarView。
- protected PhoneStatusBarView makeStatusBarView() {
- final Context context = mContext;
- Resources res = context.getResources();
- updateDisplaySize(); // populates mDisplayMetrics
- loadDimens();
- mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
- if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
- mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
- R.layout.msim_super_status_bar, null);
- } else {
- mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
- R.layout.super_status_bar, null);
- }
- mStatusBarWindow.mService = this;
- mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- checkUserAutohide(v, event);
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- if (mExpandedVisible) {
- animateCollapsePanels();
- }
- }
- return mStatusBarWindow.onTouchEvent(event);
- }});
- if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
- mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(
- R.id.msim_status_bar);
- } else {
- mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
- }
- mStatusBarView.setBar(this);
- PanelHolder holder;
- if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
- holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.msim_panel_holder);
- } else {
- holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
- }
- mStatusBarView.setPanelHolder(holder);
- mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
- mNotificationPanel.setStatusBar(this);
- mNotificationPanelIsFullScreenWidth =
- (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
- // make the header non-responsive to clicks
- mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
- new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return true; // e eats everything
- }
- });
- if (!ActivityManager.isHighEndGfx()) {
- mStatusBarWindow.setBackground(null);
- mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
- R.color.notification_panel_solid_background)));
- }
- if (ENABLE_HEADS_UP) {
- mHeadsUpNotificationView =
- (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
- mHeadsUpNotificationView.setVisibility(View.GONE);
- mHeadsUpNotificationView.setBar(this);
- }
- if (MULTIUSER_DEBUG) {
- mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
- mNotificationPanelDebugText.setVisibility(View.VISIBLE);
- }
- updateShowSearchHoldoff();
- try {
- boolean showNav = mWindowManagerService.hasNavigationBar();
- if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
- if (showNav) {
- mNavigationBarView =
- (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
- mNavigationBarView.setDisabledFlags(mDisabled);
- mNavigationBarView.setBar(this);
- mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- checkUserAutohide(v, event);
- return false;
- }});
- }
- } catch (RemoteException ex) {
- // no window manager? good luck with that
- }
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.OPAQUE;
- mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
- mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
- mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
- mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
- mNotificationIcons.setOverflowIndicator(mMoreIcon);
- mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
- mTickerView = mStatusBarView.findViewById(R.id.ticker);
- mPile = (NotificationRowLayout)mStatusBarWindow.findViewById(R.id.latestItems);
- mPile.setLayoutTransitionsEnabled(false);
- mPile.setLongPressListener(getNotificationLongClicker());
- mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
- mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
- mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
- mClearButton.setAlpha(0f);
- mClearButton.setVisibility(View.INVISIBLE);
- mClearButton.setEnabled(false);
- mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
- mHasSettingsPanel = res.getBoolean(R.bool.config_hasSettingsPanel);
- mHasFlipSettings = res.getBoolean(R.bool.config_hasFlipSettingsPanel);
- mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
- if (mDateTimeView != null) {
- mDateTimeView.setOnClickListener(mClockClickListener);
- mDateTimeView.setEnabled(true);
- }
- mSettingsButton = (ImageView) mStatusBarWindow.findViewById(R.id.settings_button);
- if (mSettingsButton != null) {
- mSettingsButton.setOnClickListener(mSettingsButtonListener);
- if (mHasSettingsPanel) {
- if (mStatusBarView.hasFullWidthNotifications()) {
- // the settings panel is hiding behind this button
- mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
- mSettingsButton.setVisibility(View.VISIBLE);
- } else {
- // there is a settings panel, but it's on the other side of the (large) screen
- final View buttonHolder = mStatusBarWindow.findViewById(
- R.id.settings_button_holder);
- if (buttonHolder != null) {
- buttonHolder.setVisibility(View.GONE);
- }
- }
- } else {
- // no settings panel, go straight to settings
- mSettingsButton.setVisibility(View.VISIBLE);
- mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
- }
- }
- if (mHasFlipSettings) {
- mNotificationButton = (ImageView) mStatusBarWindow.findViewById(R.id.notification_button);
- if (mNotificationButton != null) {
- mNotificationButton.setOnClickListener(mNotificationButtonListener);
- }
- }
- mScrollView = (ScrollView)mStatusBarWindow.findViewById(R.id.scroll);
- mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns
- if (!mNotificationPanelIsFullScreenWidth) {
- mScrollView.setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER |
- View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
- View.STATUS_BAR_DISABLE_CLOCK);
- }
- mTicker = new MyTicker(context, mStatusBarView);
- TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
- tickerView.mTicker = mTicker;
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
- // set the inital view visibility
- setAreThereNotifications();
- // Other icons
- mLocationController = new LocationController(mContext); // will post a notification
- mBatteryController = new BatteryController(mContext);
- mBluetoothController = new BluetoothController(mContext);
- mRotationLockController = new RotationLockController(mContext);
- if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
- mMSimNetworkController = new MSimNetworkController(mContext);
- MSimSignalClusterView mSimSignalCluster = (MSimSignalClusterView)
- mStatusBarView.findViewById(R.id.msim_signal_cluster);
- for (int i=0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) {
- mMSimNetworkController.addSignalCluster(mSimSignalCluster, i);
- }
- mSimSignalCluster.setNetworkController(mMSimNetworkController);
- mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById(
- R.id.emergency_calls_only);
- if (mEmergencyCallLabel != null) {
- mMSimNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
- mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) { }});
- mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- updateCarrierLabelVisibility(false);
- }});
- }
- mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
- mSubsLabel = (TextView)mStatusBarWindow.findViewById(R.id.subs_label);
- mShowCarrierInPanel = (mCarrierLabel != null);
- if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" +
- mShowCarrierInPanel + "operator label=" + mSubsLabel);
- if (mShowCarrierInPanel) {
- mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
- // for mobile devices, we always show mobile connection info here (SPN/PLMN)
- // for other devices, we show whatever network is connected
- if (mMSimNetworkController.hasMobileDataFeature()) {
- mMSimNetworkController.addMobileLabelView(mCarrierLabel);
- } else {
- mMSimNetworkController.addCombinedLabelView(mCarrierLabel);
- }
- mSubsLabel.setVisibility(View.VISIBLE);
- mMSimNetworkController.addSubsLabelView(mSubsLabel);
- // set up the dynamic hide/show of the label
- mPile.setOnSizeChangedListener(new OnSizeChangedListener() {
- @Override
- public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
- updateCarrierLabelVisibility(false);
- }
- });
- }
- } else {
- mNetworkController = new NetworkController(mContext);
- final SignalClusterView signalCluster =
- (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
- mNetworkController.addSignalCluster(signalCluster);
- signalCluster.setNetworkController(mNetworkController);
- final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
- if (isAPhone) {
- mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById(
- R.id.emergency_calls_only);
- if (mEmergencyCallLabel != null) {
- mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
- mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) { }});
- mEmergencyCallLabel.addOnLayoutChangeListener(
- new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- updateCarrierLabelVisibility(false);
- }});
- }
- }
- mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
- mShowCarrierInPanel = (mCarrierLabel != null);
- if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" +
- mShowCarrierInPanel);
- if (mShowCarrierInPanel) {
- mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
- // for mobile devices, we always show mobile connection info here (SPN/PLMN)
- // for other devices, we show whatever network is connected
- if (mNetworkController.hasMobileDataFeature()) {
- mNetworkController.addMobileLabelView(mCarrierLabel);
- } else {
- mNetworkController.addCombinedLabelView(mCarrierLabel);
- }
- // set up the dynamic hide/show of the label
- mPile.setOnSizeChangedListener(new OnSizeChangedListener() {
- @Override
- public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
- updateCarrierLabelVisibility(false);
- }
- });
- }
- }
- // Quick Settings (where available, some restrictions apply)
- if (mHasSettingsPanel) {
- // first, figure out where quick settings should be inflated
- final View settings_stub;
- if (mHasFlipSettings) {
- // a version of quick settings that flips around behind the notifications
- settings_stub = mStatusBarWindow.findViewById(R.id.flip_settings_stub);
- if (settings_stub != null) {
- mFlipSettingsView = ((ViewStub)settings_stub).inflate();
- mFlipSettingsView.setVisibility(View.GONE);
- mFlipSettingsView.setVerticalScrollBarEnabled(false);
- }
- } else {
- // full quick settings panel
- settings_stub = mStatusBarWindow.findViewById(R.id.quick_settings_stub);
- if (settings_stub != null) {
- mSettingsPanel = (SettingsPanelView) ((ViewStub)settings_stub).inflate();
- } else {
- mSettingsPanel = (SettingsPanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
- }
- if (mSettingsPanel != null) {
- if (!ActivityManager.isHighEndGfx()) {
- mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
- R.color.notification_panel_solid_background)));
- }
- }
- }
- // wherever you find it, Quick Settings needs a container to survive
- mSettingsContainer = (QuickSettingsContainerView)
- mStatusBarWindow.findViewById(R.id.quick_settings_container);
- if (mSettingsContainer != null) {
- mQS = new QuickSettings(mContext, mSettingsContainer);
- if (!mNotificationPanelIsFullScreenWidth) {
- mSettingsContainer.setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
- | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
- }
- if (mSettingsPanel != null) {
- mSettingsPanel.setQuickSettings(mQS);
- }
- mQS.setService(this);
- mQS.setBar(mStatusBarView);
- if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
- mQS.setup(mMSimNetworkController, mBluetoothController, mBatteryController,
- mLocationController, mRotationLockController);
- } else {
- mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
- mLocationController, mRotationLockController);
- }
- } else {
- mQS = null; // fly away, be free
- }
- }
- PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mBroadcastReceiver.onReceive(mContext,
- new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
- // receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(ACTION_DEMO);
- context.registerReceiver(mBroadcastReceiver, filter);
- // listen for USER_SETUP_COMPLETE setting (per-user)
- resetUserSetupObserver();
- return mStatusBarView;
- }
需要特别说明的是整个StatusBar为StatusBarView 和 Panel两部分组成,当触发显示Panel时statusBar隐藏,当收起Panel时StatusBar显示。
而Panel由Header和NotificationPanel/FlipSetting 组成,FlipSetting是一些快捷设置的入口,如:Wifi, Bluetooth,Backlight等等,而上图的SettingsPanelView很多项目中都没有使用。
整个status bar 的启动和创建的流程还是比较简单的。
二、Status Bar 构成
从第一节最后的类可以看出,整个Status Bar由Status bar和notificationPanelView两部分组成,这一节我们分析PhoneStatusBarView,它的布局如下所示:
但收到Notification时,它会显示对应的tick,以下的时序图上有显示流程。
2.1 Notification
- <LinearLayout
- android:id="@+id/notification_icon_area"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal"
- >
- <com.android.systemui.statusbar.StatusBarIconView android:id="@+id/moreIcon"
- android:layout_width="@dimen/status_bar_icon_size"
- android:layout_height="match_parent"
- android:src="@drawable/stat_notify_more"
- android:visibility="gone"
- />
- <com.android.systemui.statusbar.phone.IconMerger android:id="@+id/notificationIcons"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentStart="true"
- android:gravity="center_vertical"
- android:orientation="horizontal"/>
- </LinearLayout>
下图说明了整个流程:
以上时序图流程是NotificationManagerService收到Notification时创健StatusBarNotification,并传给StatusManagerService,而StatusManagerService最后会调到PhoneStatusBar,它的父类BastStatusBar调用InflateViews把Notification添加到NotificationPanelView了,这个对象怎么来,这些可视化的对象都在第一节中的makeStatusBarView中创建的,它们的根View是mStatusBarWindow。完成这一部操作后,它会返回PhoneStatusBar发送添加tick,触发tickStarting(),接下来再调用updateNotificationIcons来添加NotificationIcon到IconMerger中,Notification的整个显示流程就是这样。
2.2 Status Icon
整个Status Icon的初始化,已及创建流程如下图所示:
StatusBarManagerService在调用StatusIconList的defines()传递了status icon 的配置文件config_statusBarIcons,内容如下:
- <string-array name="config_statusBarIcons">
- <item><xliff:g id="id">ime</xliff:g></item>
- <item><xliff:g id="id">sync_failing</xliff:g></item>
- <item><xliff:g id="id">sync_active</xliff:g></item>
- <item><xliff:g id="id">location</xliff:g></item>
- <item><xliff:g id="id">bluetooth</xliff:g></item>
- <item><xliff:g id="id">nfc</xliff:g></item>
- <item><xliff:g id="id">tty</xliff:g></item>
- <item><xliff:g id="id">speakerphone</xliff:g></item>
- <item><xliff:g id="id">mute</xliff:g></item>
- <item><xliff:g id="id">volume</xliff:g></item>
- <item><xliff:g id="id">wifi</xliff:g></item>
- <item><xliff:g id="id">cdma_eri</xliff:g></item>
- <item><xliff:g id="id">data_connection</xliff:g></item>
- <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
- <item><xliff:g id="id">phone_signal</xliff:g></item>
- <item><xliff:g id="id">battery</xliff:g></item>
- <item><xliff:g id="id">alarm_clock</xliff:g></item>
- <item><xliff:g id="id">secure</xliff:g></item>
- <item><xliff:g id="id">clock</xliff:g></item>
- </string-array>
- public PhoneStatusBarPolicy(Context context) {
- mContext = context;
- mService = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
- // listen for broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_ALARM_CHANGED);
- filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
- mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
- int numPhones = MSimTelephonyManager.getDefault().getPhoneCount();
- //---省略部分代码
- // bluetooth status
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- int bluetoothIcon = R.drawable.stat_sys_data_bluetooth;
- if (adapter != null) {
- mBluetoothEnabled = (adapter.getState() == BluetoothAdapter.STATE_ON);
- if (adapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED) {
- bluetoothIcon = R.drawable.stat_sys_data_bluetooth_connected;
- }
- }
- mService.setIcon("bluetooth", bluetoothIcon, 0, null);
- mService.setIconVisibility("bluetooth", mBluetoothEnabled);
- //---省略部分代码
- }
- public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
- if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
- + " icon=" + icon);
- StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
- view.set(icon);
- mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
- }
初始化创建的流程是走完了,也很简单。
2.3 信号相关Icon
这部分的Icon包含了Wifi, 手机信号,数据流量,流量类型,和没有插入sim card,最后还有一个飞行模式图标,那这几个Icon是怎么添加到StatusBar上的呢,它们又是怎么保持和系统同步的呢?这儿有几个新类需要认识了,NetworkController它用来接收系统状态的变化,如wifi的连接断开,手机信号的变化,sim状态变化,飞行模式变化,而SignalClusterView用来组织这些Icon,并提供接口给NetworkController来更新状态。
NetworkController是在PhoneStatusBar的makeStatusBarView方法中创建的,并且在自己的构造函数中注册了这些广播,我们可以看下,它监听哪些广播:
- IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mWimaxSupported = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wimaxEnabled);
- if(mWimaxSupported) {
- filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION);
- filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION);
- }
- context.registerReceiver(this, filter)
在status_bar.xml里配置了以下的xml:
- <com.android.systemui.BatteryMeterView
- android:id="@+id/battery"
- android:layout_height="16dp"
- android:layout_width="10.5dp"
- android:layout_marginBottom="0.33dp"
- android:layout_marginStart="4dip"
- />
这儿你如果还把电池图标理解成是图片,你就错了,不是图片难道是画出来的吗,答案就在BatteryMetterView中,它自己监听ACTION_BATTERY_CHANGED事件,并重写了draw方法电池图标就是这儿画出来的,节省篇副代码就不贴了。
2.5 Clock
这个就比较简单了,它就是继承了TextView,然后根据系统消息设置文字:
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
总结:对于Status Bar的整个创建构成以及状态同步都做了一个详细的说明,如果要在这部分做一些定制开发我相信是没有问题的了。