你真的瞭解你手機的狀態欄嗎?

相關類

frameworks\base\services\java\com\android\server\SystemServer.java
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIService.java
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIApplication.java
frameworks\base\packages\SystemUI\res\values\config.xml
systemui\app\src\main\java\com\android\systemui\statusbar\phone\StatusBar.java
app\src\main\java\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment
app\src\main\java\com\android\systemui\statusbar\phone\StatusBarWindowManager
app\src\main\java\com\android\systemui\statusbar\phone\PhoneStatusBarPolicy.java
settingslib\src\main\java\com\android\settingslib\graph\BatteryMeterDrawableBase

1.systemUI的啓動

frameworks\base\services\java\com\android\server\SystemServer.java

static final void startSystemUi(Context context, WindowManagerService windowManager) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui",
                    "com.android.systemui.SystemUIService"));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        //Slog.d(TAG, "Starting service: " + intent);
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        windowManager.onSystemUiStarted();
    }
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIService.java


    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
	// 在這裏啓動
        // For debugging RescueParty
        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
            throw new RuntimeException();
        }

        if (Build.IS_DEBUGGABLE) {
            // b/71353150 - looking for leaked binder proxies
            BinderInternal.nSetBinderProxyCountEnabled(true);
            BinderInternal.nSetBinderProxyCountWatermarks(1000,900);
            BinderInternal.setBinderProxyCountCallback(
                    new BinderInternal.BinderProxyLimitListener() {
                        @Override
                        public void onLimitReached(int uid) {
                            Slog.w(SystemUIApplication.TAG,
                                    "uid " + uid + " sent too many Binder proxies to uid "
                                    + Process.myUid());
                        }
                    }, Dependency.get(Dependency.MAIN_HANDLER));
        }
    }
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIApplication.java

    public void startServicesIfNeeded() {
        String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
        startServicesIfNeeded(names);
    }
	//這裏可以看到有一個array的數組需要啓動    

    private void startServicesIfNeeded(String[] services) {
        if (mServicesStarted) {
            return;
        }
        mServices = new SystemUI[services.length];

        if (!mBootCompleted) {
            // check to see if maybe it was already completed long before we began
            // see ActivityManagerService.finishBooting()
            if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
                mBootCompleted = true;
                if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
            }
        }

        Log.v(TAG, "Starting SystemUI services for user " +
                Process.myUserHandle().getIdentifier() + ".");
        TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
                Trace.TRACE_TAG_APP);
        log.traceBegin("StartServices");
        final int N = services.length;
        for (int i = 0; i < N; i++) {
            String clsName = services[i];
            if (DEBUG) Log.d(TAG, "loading: " + clsName);
            log.traceBegin("StartServices" + clsName);
            long ti = System.currentTimeMillis();
            Class cls;
            try {
                cls = Class.forName(clsName);
                mServices[i] = (SystemUI) cls.newInstance();
            } catch(ClassNotFoundException ex){
                throw new RuntimeException(ex);
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InstantiationException ex) {
                throw new RuntimeException(ex);
            }

            mServices[i].mContext = this;
            mServices[i].mComponents = mComponents;
            if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
            mServices[i].start();   // 每個都從這裏啓動
            log.traceEnd();

            // Warn if initialization of component takes too long
            ti = System.currentTimeMillis() - ti;
            if (ti > 1000) {
                Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
            }
            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }

這裏其實大家不要被名稱搞混了,這不是服務,而是繼承systemUI的類。通過start方法去加載啓動

frameworks\base\packages\SystemUI\res\values\config.xml
這裏可以看到有這麼多類需要啓動加載。

  <!-- SystemUI Services: The classes of the stuff to start. -->
    <string-array name="config_systemUIServiceComponents" translatable="false">
        <item>com.android.systemui.Dependency</item>
        <item>com.android.systemui.util.NotificationChannels</item>
        <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
        <item>com.android.systemui.recents.Recents</item>
        <item>com.android.systemui.volume.VolumeUI</item>
        <item>com.android.systemui.stackdivider.Divider</item>
        <item>com.android.systemui.SystemBars</item>
        <item>com.android.systemui.usb.StorageNotification</item>
        <item>com.android.systemui.power.PowerUI</item>
        <item>com.android.systemui.media.RingtonePlayer</item>
        <item>com.android.systemui.keyboard.KeyboardUI</item>
        <item>com.android.systemui.pip.PipUI</item>
        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
        <item>@string/config_systemUIVendorServiceComponent</item>
        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
        <item>com.android.systemui.LatencyTester</item>
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
        <item>com.android.systemui.ScreenDecorations</item>
        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
    </string-array>

這裏只分析com.android.systemui.SystemBars的流程

systemui\app\src\main\java\com\android\systemui\SystemBars

    @Override
    public void start() {
        if (DEBUG) Log.d(TAG, "start");
        createStatusBrFromConfig();
    }


 private void createStatusBarFromConfig() {
        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
        final String clsName = mContext.getString(R.string.config_statusBarComponent);
        //    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
        // 這裏這個採用反射
        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 = (SystemUI) 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());
    }

systemui\app\src\main\java\com\android\systemui\statusbar\phone\StatusBar.java

   @Override
    public void start() {
    ...  省略部分代碼,只保留文章相關的
     createAndAddWindows() ;
    ...
        
    }
        
private void addStatusBarWindow() {
        makeStatusBarView();// 調用此處方法
        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
        mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,
                new RemoteInputController.Delegate() {
                    public void setRemoteInputActive(NotificationData.Entry entry,
                            boolean remoteInputActive) {
                        mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
                        entry.row.notifyHeightChanged(true /* needsAnimation */);
                        updateFooter();
                    }
                    public void lockScrollTo(NotificationData.Entry entry) {
                        mStackScroller.lockScrollTo(entry.row);
                    }
                    public void requestDisallowLongPressAndDismiss() {
                        mStackScroller.requestDisallowLongPress();
                        mStackScroller.requestDisallowDismiss();
                    }
                });
        mRemoteInputManager.getController().addCallback(mStatusBarWindowManager);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); // 這個方法裏面其實就可以看出是個window
    }
    


protected void makeStatusBarView() {
     inflateStatusBarWindow(context);  // 加載佈局,下面分析
        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, new FragmentHostManager.FragmentListener() {
                    @Override
                    public void onFragmentViewCreated(String tag, Fragment fragment) {
                        CollapsedStatusBarFragment statusBarFragment =
                                (CollapsedStatusBarFragment) fragment;
                 //這裏這個fragment 纔是我們的狀態欄的fragment statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                        mStatusBarView = (PhoneStatusBarView) fragment.getView();
                        mStatusBarView.setBar(StatusBar.this);
                        mStatusBarView.setPanel(mNotificationPanel);
                        mStatusBarView.setScrimController(mScrimController);
                        mStatusBarView.setBouncerShowing(mBouncerShowing);
                        if (mHeadsUpAppearanceController != null) {
                            // This view is being recreated, let's destroy the old one
                            mHeadsUpAppearanceController.destroy();
                        }
                        mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                                mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
                        StatusBar.this.setAreThereNotifications();
                        StatusBar.this.checkBarModes();
                        /// M: add for plmn display feature @{
                        StatusBar.this.attachPlmnPlugin();
                        ///@}
                    }
                }).getFragmentManager()
                .beginTransaction()
                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                        CollapsedStatusBarFragment.TAG)  // 這可以看到這裏也是在加載這個fragment 狀態欄
                .commit();
        mIconController = Dependency.get(StatusBarIconController.class);
}

    // 加載佈局
    protected void inflateStatusBarWindow(Context context) {
        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                R.layout.super_status_bar, null);
    }

app\src\main\java\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.status_bar, container, false);
        // 這個佈局文件就是我們手機的狀態欄
    }
app\src\main\java\com\android\systemui\statusbar\phone\StatusBarWindowManager

    public void add(View statusBarView, int barHeight) {

        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
        // hardware-accelerated.
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                barHeight,
                WindowManager.LayoutParams.TYPE_STATUS_BAR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.token = new Binder();
        mLp.gravity = Gravity.TOP;
        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        mStatusBarView = statusBarView;
        mBarHeight = barHeight;
        mWindowManager.addView(mStatusBarView, mLp);  // 可以看到這個其實也是一個window
        mLpChanged = new WindowManager.LayoutParams();
        mLpChanged.copyFrom(mLp);
    }

到此,狀態欄就啓動了。接下來說說狀態欄的加載以及佈局

2.狀態欄佈局

super_status_bar.xml

<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <com.android.systemui.statusbar.BackDropView
        android:id="@+id/backdrop"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        sysui:ignoreRightInset="true">

        <ImageView
            android:id="@+id/backdrop_back"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop" />

        <ImageView
            android:id="@+id/backdrop_front"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:visibility="invisible" />
    </com.android.systemui.statusbar.BackDropView>

    <com.android.systemui.statusbar.ScrimView
        android:id="@+id/scrim_behind"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true" />

    <FrameLayout
        android:id="@+id/status_bar_container"  // 這個fragment就是我們之前說的那個狀態欄的fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ViewStub
        android:id="@+id/fullscreen_user_switcher_stub"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/car_fullscreen_user_switcher" />

    <include
        layout="@layout/status_bar_expanded"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible" />

    <include layout="@layout/brightness_mirror" />

    <com.android.systemui.statusbar.ScrimView
        android:id="@+id/scrim_in_front"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true" />

</com.android.systemui.statusbar.phone.StatusBarWindowView>

// 真正狀態欄的顯示

status_bar.xml

<com.android.systemui.statusbar.phone.PhoneStatusBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
    android:layout_width="match_parent"
    android:layout_height="@dimen/status_bar_height"
    android:id="@+id/status_bar"
    android:background="@drawable/system_bar_background"
    android:orientation="vertical"
    android:focusable="false"
    android:descendantFocusability="afterDescendants"
    android:accessibilityPaneTitle="@string/status_bar"
    >

    <ImageView
        android:id="@+id/notification_lights_out"
        android:layout_width="@dimen/status_bar_icon_size"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingBottom="2dip"
        android:src="@drawable/ic_sysbar_lights_out_dot_small"
        android:scaleType="center"
        android:visibility="gone"
        />

    <LinearLayout android:id="@+id/status_bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingEnd="@dimen/status_bar_padding_end"
        android:orientation="horizontal"
        >
        <ViewStub
            android:id="@+id/operator_name"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout="@layout/operator_name" />
        <FrameLayout
            android:layout_height="match_parent"
            android:layout_width="0dp"
            android:layout_weight="1">

            <include layout="@layout/heads_up_status_bar_layout" />

            <!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
             individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
             DISABLE_NOTIFICATION_ICONS, respectively -->
            <LinearLayout
                android:id="@+id/status_bar_left_side"
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:clipChildren="false"
            >
            // 時間顯示
                <com.android.systemui.statusbar.policy.Clock
                    android:id="@+id/clock"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:textAppearance="@style/TextAppearance.StatusBar.Clock"
                    android:singleLine="true"
                    android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
                    android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
                    android:gravity="center_vertical|start"
                />
			 // 通知顯示區域
                <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                    android:id="@+id/notification_icon_area"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:orientation="horizontal"
                    android:clipChildren="false"/>

            </LinearLayout>
        </FrameLayout>

        <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
        <android.widget.Space
            android:id="@+id/cutout_space_view"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:gravity="center_horizontal|center_vertical"
        />
		// 系統icon
        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:gravity="center_vertical|end"
            >

            <include layout="@layout/system_icons" />
        </com.android.keyguard.AlphaOptimizedLinearLayout>
    </LinearLayout>
	
    <ViewStub
        android:id="@+id/emergency_cryptkeeper_text"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout="@layout/emergency_cryptkeeper_text"
    />

</com.android.systemui.statusbar.phone.PhoneStatusBarView>
system_icons.xml
這裏主要就是顯示一些系統的圖標,包括鬧鐘,wifi。藍牙等,系統中這些圖片都是矢量圖
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/system_icons"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical">

    <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:paddingEnd="@dimen/signal_cluster_battery_padding"
        android:gravity="center_vertical"
        android:orientation="horizontal"/>
	// 電池
    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:clipToPadding="false"
        android:clipChildren="false" />


</LinearLayout>

總結下來狀態欄如下所示
在這裏插入圖片描述

接下來看一下系統icon 欄是如何顯示的

app\src\main\java\com\android\systemui\statusbar\phone\PhoneStatusBarPolicy.java
系統icon 欄的初始化和更新都是在這個類裏面去實現的

 @VisibleForTesting
    public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {
        mContext = context;
        mIconController = iconController;
        mCast = Dependency.get(CastController.class);
        mHotspot = Dependency.get(HotspotController.class);
        mBluetooth = Dependency.get(BluetoothController.class);
        mNextAlarmController = Dependency.get(NextAlarmController.class);
        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        mUserInfoController = Dependency.get(UserInfoController.class);
        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        mRotationLockController = Dependency.get(RotationLockController.class);
        mDataSaver = Dependency.get(DataSaverController.class);
        mZenController = Dependency.get(ZenModeController.class);
        mProvisionedController = Dependency.get(DeviceProvisionedController.class);
        mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
        mLocationController = Dependency.get(LocationController.class);

        mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
        mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
        mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
        mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);
        mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);
        mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);
        mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);
        mSlotManagedProfile = context.getString(
                com.android.internal.R.string.status_bar_managed_profile);
        mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
        mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
        mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
        mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
       // systemicon 的初始化
        // listen for broadcasts
        IntentFilter filter = new IntentFilter();
        /// M: [Multi-User] Will register this action using special receiver.
        //filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
        filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
        filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
        /// M: [Multi-User] Add user switched action for updating possible alarm icon.
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
	 // 廣播的註冊
        /// M: Add icon for embms session.
        filter.addAction(TelephonyIntents.ACTION_EMBMS_SESSION_STATUS_CHANGED);
        filter.addAction(SMART_DEVICE_BROADCAST);
        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
        /// M: [Multi-User] Register Alarm intent by user
        registerAlarmClockChanged(UserHandle.USER_OWNER, false);

        // Alarm clock
        mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
        mIconController.setIconVisibility(mSlotAlarmClock, false);
	接受到廣播後去更新icon
    private void updateAlarm() {
        final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
        final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
        int zen = mZenController.getZen();
        final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
        mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim
                : R.drawable.stat_sys_alarm, buildAlarmContentDescription());
        mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);
    }

 // 廣播接收器
@VisibleForTesting
    /*private*/ BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            switch (action) {
                case AudioManager.RINGER_MODE_CHANGED_ACTION:
                case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:
                    updateVolumeZen();
                    break;
                case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                    // Avoid rebroadcast because SysUI is direct boot aware.
                    if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
                            false)) {
                        break;
                    }
                    updateSimState(intent);
                    break;
                case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
                    updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
                            TelecomManager.TTY_MODE_OFF));
                    break;
                case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_REMOVED:
                    updateManagedProfile();
                    break;
                case AudioManager.ACTION_HEADSET_PLUG:
                    updateHeadsetPlug(intent);
                    break;
                case Intent.ACTION_USER_SWITCHED:
                    updateAlarm();
                    int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                    registerAlarmClockChanged(newUserId, true);
                    break;
                case TelephonyIntents.ACTION_EMBMS_SESSION_STATUS_CHANGED:
                    updateEmbmsState(intent);
                    break;
                case SMART_DEVICE_BROADCAST:
                    updateSmartDevice(intent);
                    break;
         
            }
        }
    };

所以如果,需要在systemicon 欄客製化加入一個其他的icon,則可以在這個類裏面仿照做修改

4. 電池電量的更新

從上面的xml 中可以看到電池電量的UI是在BatteryMeterView.java 類中
而真正去繪製電視的UI顯示則是在settingslib的BatteryMeterDrawableBase.java 類中

settingslib\src\main\java\com\android\settingslib\graph\BatteryMeterDrawableBase
 // 初始化畫筆
    public BatteryMeterDrawableBase(Context context, int frameColor) {
        mContext = context;
        final Resources res = context.getResources();
        TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
        TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);

        final int N = levels.length();
        mColors = new int[2 * N];
        for (int i = 0; i < N; i++) {
            mColors[2 * i] = levels.getInt(i, 0);
            if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) {
                mColors[2 * i + 1] = Utils.getColorAttr(context, colors.getThemeAttributeId(i, 0));
            } else {
                mColors[2 * i + 1] = colors.getColor(i, 0);
            }
        }
        levels.recycle();
        colors.recycle();

        mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
        mCriticalLevel = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
        mButtonHeightFraction = context.getResources().getFraction(
                R.fraction.battery_button_height_fraction, 1, 1);
        mSubpixelSmoothingLeft = context.getResources().getFraction(
                R.fraction.battery_subpixel_smoothing_left, 1, 1);
        mSubpixelSmoothingRight = context.getResources().getFraction(
                R.fraction.battery_subpixel_smoothing_right, 1, 1);

        mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mFramePaint.setColor(frameColor);
        mFramePaint.setDither(true);
        mFramePaint.setStrokeWidth(0);
        mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);

        mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBatteryPaint.setDither(true);
        mBatteryPaint.setStrokeWidth(0);
        mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
        mTextPaint.setTypeface(font);
        mTextPaint.setTextAlign(Paint.Align.CENTER);

        mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        font = Typeface.create("sans-serif", Typeface.BOLD);
        mWarningTextPaint.setTypeface(font);
        mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
        if (mColors.length > 1) {
            mWarningTextPaint.setColor(mColors[1]);
        }

        mChargeColor = Utils.getDefaultColor(mContext, android.R.color.holo_red_dark);

        mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBoltPaint.setColor(Utils.getDefaultColor(mContext,  android.R.color.holo_green_dark));
        mBoltPoints = loadPoints(res, R.array.batterymeter_bolt_points);

        mPlusPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPlusPaint.setColor(Utils.getDefaultColor(mContext, R.color.batterymeter_plus_color));
        mPlusPoints = loadPoints(res, R.array.batterymeter_plus_points);

        mPowersavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPowersavePaint.setColor(mPlusPaint.getColor());
        mPowersavePaint.setStyle(Style.STROKE);
        mPowersavePaint.setStrokeWidth(context.getResources()
                .getDimensionPixelSize(R.dimen.battery_powersave_outline_thickness));

        mIntrinsicWidth = context.getResources().getDimensionPixelSize(R.dimen.battery_width);
        mIntrinsicHeight = context.getResources().getDimensionPixelSize(R.dimen.battery_height);
    }

// 畫電池
  @Override
    public void draw(Canvas c) {
        final int level = mLevel;
        final Rect bounds = getBounds();

        if (level == -1) return;

        float drawFrac = (float) level / 100f;
        final int height = mHeight;
        final int width = (int) (getAspectRatio() * mHeight);
        final int px = (mWidth - width) / 2;
        final int buttonHeight = Math.round(height * mButtonHeightFraction);
        final int left = mPadding.left + bounds.left;
        final int top = bounds.bottom - mPadding.bottom - height;

        mFrame.set(left, top, width + left, height + top);
        mFrame.offset(px, 0);

        // button-frame: area above the battery body
        mButtonFrame.set(
                mFrame.left + Math.round(width * 0.28f),
                mFrame.top,
                mFrame.right - Math.round(width * 0.28f),
                mFrame.top + buttonHeight);

        // frame: battery body area
        mFrame.top += buttonHeight;

        // set the battery charging color
        mBatteryPaint.setColor(batteryColorForLevel(level));

        if (level >= FULL) {
            drawFrac = 1f;
        } else if (level <= mCriticalLevel) {
            drawFrac = 0f;
        }

        final float levelTop = drawFrac == 1f ? mButtonFrame.top
                : (mFrame.top + (mFrame.height() * (1f - drawFrac)));

        // define the battery shape
        mShapePath.reset();
        mOutlinePath.reset();
        final float radius = getRadiusRatio() * (mFrame.height() + buttonHeight);
        mShapePath.setFillType(FillType.WINDING);
        mShapePath.addRoundRect(mFrame, radius, radius, Direction.CW);
        mShapePath.addRect(mButtonFrame, Direction.CW);
        mOutlinePath.addRoundRect(mFrame, radius, radius, Direction.CW);
        Path p = new Path();
        p.addRect(mButtonFrame, Direction.CW);
        mOutlinePath.op(p, Op.XOR);

        if (mCharging) {
            // define the bolt shape
            // Shift right by 1px for maximal bolt-goodness
            final float bl = mFrame.left + mFrame.width() / 4f + 1;
            final float bt = mFrame.top + mFrame.height() / 6f;
            final float br = mFrame.right - mFrame.width() / 4f + 1;
            final float bb = mFrame.bottom - mFrame.height() / 10f;
            if (mBoltFrame.left != bl || mBoltFrame.top != bt
                    || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
                mBoltFrame.set(bl, bt, br, bb);
                mBoltPath.reset();
                mBoltPath.moveTo(
                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
                for (int i = 2; i < mBoltPoints.length; i += 2) {
                    mBoltPath.lineTo(
                            mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
                            mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
                }
                mBoltPath.lineTo(
                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
            }

            float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);
            boltPct = Math.min(Math.max(boltPct, 0), 1);
            if (boltPct <= BOLT_LEVEL_THRESHOLD) {
                // draw the bolt if opaque
                c.drawPath(mBoltPath, mBoltPaint);
            } else {
                // otherwise cut the bolt out of the overall shape
                mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
            }
        } else if (mPowerSaveEnabled) {
            // define the plus shape
            final float pw = mFrame.width() * 2 / 3;
            final float pl = mFrame.left + (mFrame.width() - pw) / 2;
            final float pt = mFrame.top + (mFrame.height() - pw) / 2;
            final float pr = mFrame.right - (mFrame.width() - pw) / 2;
            final float pb = mFrame.bottom - (mFrame.height() - pw) / 2;
            if (mPlusFrame.left != pl || mPlusFrame.top != pt
                    || mPlusFrame.right != pr || mPlusFrame.bottom != pb) {
                mPlusFrame.set(pl, pt, pr, pb);
                mPlusPath.reset();
                mPlusPath.moveTo(
                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
                for (int i = 2; i < mPlusPoints.length; i += 2) {
                    mPlusPath.lineTo(
                            mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(),
                            mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height());
                }
                mPlusPath.lineTo(
                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
            }

            // Always cut out of the whole shape, and sometimes filled colorError
            mShapePath.op(mPlusPath, Path.Op.DIFFERENCE);
            if (mPowerSaveAsColorError) {
                c.drawPath(mPlusPath, mPlusPaint);
            }
        }

        // compute percentage text
        boolean pctOpaque = false;
        float pctX = 0, pctY = 0;
        String pctText = null;
        if (!mCharging && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) {
            mTextPaint.setColor(getColorForLevel(level));
            mTextPaint.setTextSize(height *
                    (SINGLE_DIGIT_PERCENT ? 0.75f
                            : (mLevel == 100 ? 0.38f : 0.5f)));
            mTextHeight = -mTextPaint.getFontMetrics().ascent;
            pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level);
            pctX = mWidth * 0.5f + left;
            pctY = (mHeight + mTextHeight) * 0.47f + top;
            pctOpaque = levelTop > pctY;
            if (!pctOpaque) {
                mTextPath.reset();
                mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);
                // cut the percentage text out of the overall shape
                mShapePath.op(mTextPath, Path.Op.DIFFERENCE);
            }
        }

        // draw the battery shape background
        c.drawPath(mShapePath, mFramePaint);

        // draw the battery shape, clipped to charging level
        mFrame.top = levelTop;
        c.save();
        c.clipRect(mFrame);
        c.drawPath(mShapePath, mBatteryPaint);
        c.restore();

        if (!mCharging && !mPowerSaveEnabled) {
            if (level <= mCriticalLevel) {
                // draw the warning text
                final float x = mWidth * 0.5f + left;
                final float y = (mHeight + mWarningTextHeight) * 0.48f + top;
                c.drawText(mWarningString, x, y, mWarningTextPaint);
            } else if (pctOpaque) {
                // draw the percentage text
                c.drawText(pctText, pctX, pctY, mTextPaint);
            }
        }

        // Draw the powersave outline last
        if (!mCharging && mPowerSaveEnabled && mPowerSaveAsColorError) {
            c.drawPath(mOutlinePath, mPowersavePaint);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章