SystemUI system status bar status bar analysis (Android o)

System status bar status bar analysis (Android o)

1 Overview

In the system UI, the status bar provides users with some quick information display and function operation. The user can display more information on the screen in a reasonable way through the drop-down bar. On the 848 platform, there are two configurable status bars: StatusBar and TvStatusBar. The default configuration uses the StatusBar suitable for small devices, but the platform does not use the status bar. Here we mainly analyze the process of creating and starting the StatusBar status bar of common small screen devices (mobile phones).

2 status bar analysis

2.1 status bar startup

SystemBars is responsible for starting the status bar component service, which is located in the list of SERVICES array constant SERVICES maintained in the SystemUIApplication class. The status bar is mainly through calling SystemBars Start () method.

Before the status bar starts, it will get config Create an instance of StatusBar from the XML configuration file, and then call StatusBar The start () method is used to create the status bar. Here you can modify config Configure the custom StatusBar in the XML file.

// frameworks\base\packages\SystemUI\res\values\config.xml
<!-- Customizable StatusBar´╝îAlternative configurations are also available, such as com.android.systemui.statusbar.tv.TvStatusBar -->
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
// frameworks\base\packages\SystemUI\src\com\android\systemui\SystemBars.java
public class SystemBars extends SystemUI {
    private SystemUI mStatusBar;
    @Override
    public void start() {
        createStatusBarFromConfig();
    }
    private void createStatusBarFromConfig() {
        // Get the above config XML configuration and create a StatusBar instance according to the full class name
        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;
        cls = mContext.getClassLoader().loadClass(clsName);
        mStatusBar = (SystemUI) cls.newInstance();
        
        mStatusBar.mContext = mContext;
        mStatusBar.mComponents = mComponents;
        mStatusBar.start();     // Call the start() method of StatusBar 
    }
    ...
}

StatusBar inherits from the base class SystemUI and implements many interfaces. The key interface here is CommandQueue Callbacks and CommandQueue inherit from istatusbar Stub, which internally maintains an array variable of callback type in StatusBar Start() will add the instance of StatusBar to this array to facilitate subsequent proxy calls to StatusBar.

In statusbar Start () is not only responsible for the creation of the status bar window, but also involves other related processes such as navigation bar and notification bar. Here, only the related processes of the status bar are analyzed for the time being.

StatusBar. The start () method mainly does several things:

  1. Initialize mWindowManager and mWindowManagerService variables
  2. Get the client agent instance of the StatusBarManagerService service. It is responsible for receiving the system service's request for the status bar and forwarding the request to the StatusBar. At the same time, it saves a copy of the relevant information that needs to be displayed in the status bar. This is to prevent information loss when the system UI crashes unexpectedly.
  3. Get the CommandQueue instance, add the StatusBar instance to the Callbacks array, and register the CommandQueue instance with the StatusBarManagerService, which is equivalent to maintaining a CommandQueue in the StatusBarManagerService instance, and then the CommandQueue inherits from istatusbar Stub, so the operation on the status bar will be handed over to CommandQueue for processing.
  4. Create a window for the status bar and navigation bar
  5. Handle the copied information of the status bar
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
public class StatusBar extends SystemUI implements CommandQueue.Callbacks´╝î... {
    public void start() {
        ...  
        // The status bar and navigation bar windows do not belong to any Activity, but are created by WindowManager
        mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        mDisplay = mWindowManager.getDefaultDisplay();
        updateDisplaySize();
        // Get WMS
        mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
        // Get IStatusBarService instance
        mBarService = IStatusBarService.Stub.asInterface(
            ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        //CommandQueue inherits from istatiusbar Stub is the bridge between IStatusBarService and StatusBar
        mCommandQueue = getComponent(CommandQueue.class); // At commandqueuestart start() -> putComponent()
        mCommandQueue.addCallbacks(this); // Add the StatusBar instance to the Callbacks array of CommandQueue

        
        int[] switches = new int[9];
        ArrayList<IBinder> binders = new ArrayList<IBinder>();
        ArrayList<String> iconSlots = new ArrayList<>();
        ArrayList<StatusBarIcon> icons = new ArrayList<>();
        Rect fullscreenStackBounds = new Rect();
        Rect dockedStackBounds = new Rect();
        // Register the StatusBar into the IStatusBarService system service, and take out the copy of relevant information that needs to be displayed and processed in the status bar
        // mCommandQueue is injected into IStatusBarService as a parameter
        mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
                    fullscreenStackBounds, dockedStackBounds);

        // Create a window for the status bar and navigation bar, which will be analyzed later
        createAndAddWindows();

        // Handle the copied information of the status bar
        int N = iconSlots.size();
        int viewIndex = 0;
        for (int i=0; i < N; i++) {
            mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
        }
	    ...
    }
}
// frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
                              List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
                              Rect fullscreenStackBounds, Rect dockedStackBounds) {
    enforceStatusBarService();
    // Save bar(CommandQueue) in mBar, and mBar will transfer some operation requests to the status bar to mBar later
    mBar = bar;
    ...

    // Populates the relevant copy information back into the parameters 
    synchronized (mIcons) {
        for (String slot : mIcons.keySet()) {
            iconSlots.add(slot);
            iconList.add(mIcons.get(slot));
        }
    }
    synchronized (mLock) {
        switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
        switches[1] = mSystemUiVisibility;
        switches[2] = mMenuVisible ? 1 : 0;
        ...
    }
}

As a system service, StatusBarManagerService is in systemserver Run() - > startotherservices() this phase creates a start.

// frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices() {
    ...
    if (!disableSystemUI) { // config.disable_systemui
        try {
            statusBar = new StatusBarManagerService(context, wm);
            ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
        } catch (Throwable e) {
            reportWtf("starting StatusBarManagerService", e);
        }
    }
    ...
}

So how does StatusBar establish a communication connection with StatusBarManagerService? It has also been analyzed above. Generally speaking, it is to maintain a CommandQueue instance in IStatusBarService, which is inherited from istatusbar Stub, so many requests for the status bar will actually be forwarded to the CommandQueue for processing after being forwarded to the StatusBarManagerService. That is to say, the CommandQueue is equivalent to the communication bridge between IStatusBarService and StatusBar. What's more, the requests received and processed by CommandQueue will be transferred to the internally maintained array variable mCallbacks of Callbacks type for related calls, and finally the related methods in the StatusBar instance will be called.

Here, take the call of collapsePanels() method provided by StatusBarManagerService as an example.

// frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java
private volatile IStatusBar mBar;
@Override
public void collapsePanels() {
    enforceExpandStatusBar();
    if (mBar != null) {
        try {
            mBar.animateCollapsePanels(); // 
        } catch (RemoteException ex) {
        }
    }
}

CommandQueue class analysis

// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\CommandQueue.java
public class CommandQueue extends IStatusBar.Stub {
    
    private ArrayList<Callbacks> mCallbacks = new ArrayList<>();
    public interface Callbacks {
        ...
        default void animateCollapsePanels(int flags) { } // 
        ...
        default void toggleRecentApps() { }
        default void toggleSplitScreen() { }
    }
    
    public void animateCollapsePanels() {
        synchronized (mLock) {
            mHandler.removeMessages(MSG_COLLAPSE_PANELS);
            mHandler.obtainMessage(MSG_COLLAPSE_PANELS, 0, 0).sendToTarget();
        }
    }
    
    private final class H extends Handler {
        public void handleMessage(Message msg) {
            final int what = msg.what & MSG_MASK;
            switch (what) {
                case MSG_COLLAPSE_PANELS:
                    for (int i = 0; i < mCallbacks.size(); i++) {
                        mCallbacks.get(i).animateCollapsePanels(msg.arg1); 
                    }
                    break;   
                    ...
            }
        }
    }
}

Finally, it is called to statusbar Animatecollapsepanels() instance method

// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
public void animateCollapsePanels(int flags) {
    animateCollapsePanels(flags, false /* force */, false /* delayed */, 1.0f /* speedUpFactor */);
}

Take a brief look at the schematic diagram

2.2 status bar window creation

Then analyze how the status bar window is created and displayed

// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
public void createAndAddWindows() {
    addStatusBarWindow();
}

private void addStatusBarWindow() {
    makeStatusBarView(); // 1 create status bar control tree
    // Get the StatusBarWindowManager, which encapsulates the relevant logic of status bar window status management
    mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class); 
    mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); // 2 add status bar window
}

2.2.1 loading status bar control tree

To load the status bar control tree, you need to load super_status_bar.xml layout, and then analyze the status bar control tree from this layout file.

protected void makeStatusBarView() {
    final Context context = mContext;
    ...
    // Load layout r.layout super_ status_ bar
    // Mstatusbarwindow = (statusbarwindowview) view will be executed inflate(context,R.layout.super_status_bar, null);
    inflateStatusBarWindow(context); 
    mStatusBarWindow.setService(this);
    // Set touch event listener
    mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
	...
    FragmentHostManager.get(mStatusBarWindow)
        .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
            CollapsedStatusBarFragment statusBarFragment =
                (CollapsedStatusBarFragment) fragment; // R.layout.status_bar
            statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
            mStatusBarView = (PhoneStatusBarView) fragment.getView();
            mStatusBarView.setBar(this);
            mStatusBarView.setPanel(mNotificationPanel);
            mStatusBarView.setScrimController(mScrimController);
            setAreThereNotifications();
            checkBarModes();
        }).getFragmentManager()
        .beginTransaction()
        .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(), 
                 CollapsedStatusBarFragment.TAG)
        .commit();
	...
    // QS settings panel
    View container = mStatusBarWindow.findViewById(R.id.qs_frame);
    if (container != null) {
        FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
        fragmentHostManager.getFragmentManager().beginTransaction()
            .replace(R.id.qs_frame, new QSFragment(), QS.TAG) //
            .commit();
        new PluginFragmentListener(container, QS.TAG, QSFragment.class, QS.class)
            .startListening();
        final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
                                                                              mIconController);
        mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
        fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
            QS qs = (QS) f;
            if (qs instanceof QSFragment) {
                ((QSFragment) qs).setHost(qsh);
                mQSPanel = ((QSFragment) qs).getQsPanel();
                mQSPanel.setBrightnessMirror(mBrightnessMirrorController); //
                mKeyguardStatusBar.setQSPanel(mQSPanel);
            }
        });
    }
}

2.2.2 create status bar window

Use StatusBarWindow as a control tree to create a status bar window. barHeight is the height of the status bar, which is obtained through getStatusBarHeight() method in frameworks \ base \ core \ res \ res \ values \ dimensions It can be set in the XML file. The default value is 25dp.

//frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBarWindowManager.java
public void add(View statusBarView, int barHeight) { 
    mLp = new WindowManager.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT, // width
        barHeight,  // Status bar height
        WindowManager.LayoutParams.TYPE_STATUS_BAR, // Window type FIRST_SYSTEM_WINDOW
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE // The status bar does not accept key events
        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING // Receive a touch event that causes the device to wake up
        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH  // The window will accept touch events beyond its boundaries and send them to other windows that also support split touch
        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, // Responsible for drawing the logo of the system bar background. If set, the system bar will be drawn with a transparent background
        PixelFormat.TRANSLUCENT);  // Support transparency
    mLp.token = new Binder();
    mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
    mLp.gravity = Gravity.TOP; 
    mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
    mLp.setTitle("StatusBar");
    mLp.packageName = mContext.getPackageName();
    mStatusBarView = statusBarView;
    mBarHeight = barHeight;
    mWindowManager.addView(mStatusBarView, mLp); //Via WindowManager Addview() creates a window for the status bar
    ...
}

2.3 status bar control tree analysis

2.3.1 super_status_bar.xml

StatusBarWindowView, as the root layout control, mainly contains status_bar.xml and status_bar_expanded.xml these two important layouts.

status_bar.xml is mainly used as the layout of the top icon bar, such as the display of system notification icon, system power, signal, time and other related icons.

status_bar_expanded.xml is the layout after the status bar is pulled down, such as the display of related layouts such as the top system icon, icon brightness bar, shortcut setting panel, bottom function editing bar, notification bar and so on.

// frameworks\base\packages\SystemUI\res\layout\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.ScrimView 
        android:id="@+id/scrim_behind"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true"
        sysui:scrimColor="@color/scrim_behind_color"/>
    <!-- Top icon bar  frameworks\base\packages\SystemUI\res\layout\status_bar.xml-->
    <FrameLayout
        android:id="@+id/status_bar_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <!-- Brightness bar frameworks\base\packages\SystemUI\res\layout\brightness_mirror.xml-->
    <include layout="@layout/brightness_mirror" />
    
    <!-- Drop down status bar frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml-->
    <include layout="@layout/status_bar_expanded"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible" />
    ...
</com.android.systemui.statusbar.phone.StatusBarWindowView>
status_bar.xml

The layout is mainly responsible for displaying several kinds of information: notification information, time information, power information, signal information and system status icon area:

// frameworks\base\packages\SystemUI\res\layout\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"
    ...>
    ...
    <LinearLayout android:id="@+id/status_bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        ...>
        <!-- Notification icon -->
        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/notification_icon_area"
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal" />
        <com.android.keyguard.AlphaOptimizedLinearLayout 
            android:id="@+id/system_icon_area"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal">
		   <!-- System Icon frameworks\base\packages\SystemUI\res\layout\system_icons.xml-->
            <include layout="@layout/system_icons" />
		   <!-- time -->
            <com.android.systemui.statusbar.policy.Clock
                android:id="@+id/clock"
                android:textAppearance="@style/TextAppearance.StatusBar.Clock"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:singleLine="true"
                android:paddingStart="@dimen/status_bar_clock_starting_padding"
                android:paddingEnd="@dimen/status_bar_clock_end_padding"
                android:gravity="center_vertical|start"
                />
        </com.android.keyguard.AlphaOptimizedLinearLayout>
    </LinearLayout>
    ...
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
system_icons.xml

Status bar system icon layout

// frameworks\base\packages\SystemUI\res\layout\system_icons.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/system_icons"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:gravity="center_vertical">
    <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="horizontal"/>
	<!-- Signal correlation frameworks\base\packages\SystemUI\res\layout\signal_cluster_view.xml-->
    <include layout="@layout/signal_cluster_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
	<!-- Electricity correlation -->
    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        />
</LinearLayout>

2.3.2 status_bar_expanded.xml

Status bar drop-down layout

// frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml
<com.android.systemui.statusbar.phone.NotificationPanelView
    android:id="@+id/notification_panel"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent" >
    ...
    <!-- Drop down shortcut settings panel layout -->
    <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="@integer/notification_panel_layout_gravity"
        android:id="@+id/notification_container_parent"
        android:clipToPadding="false"
        android:clipChildren="false">
		<!-- Quick settings panel frameworks\base\packages\SystemUI\res\layout\qs_panel.xml-->
        <FrameLayout
            android:id="@+id/qs_frame"
            android:layout="@layout/qs_panel"
            android:layout_width="@dimen/notification_panel_width"
            android:layout_height="match_parent"
            android:layout_gravity="@integer/notification_panel_layout_gravity"
            android:clipToPadding="false"
            android:clipChildren="false"
            systemui:viewType="com.android.systemui.plugins.qs.QS" />
        <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
            android:id="@+id/notification_stack_scroller"
            android:layout_width="@dimen/notification_panel_width"
            android:layout_height="match_parent"
            android:layout_gravity="@integer/notification_panel_layout_gravity"
            android:layout_marginBottom="@dimen/close_handle_underlap" />
        ...
    </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
    ...
</com.android.systemui.statusbar.phone.NotificationPanelView>
qs_panel.xml
<com.android.systemui.qs.QSContainerImpl
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/quick_settings_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    ...>
    ...
    <!-- QS panel -->
    <com.android.systemui.qs.QSPanel
        android:id="@+id/quick_settings_panel"
        android:layout_marginTop="28dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:elevation="4dp"
        android:background="#00000000"
        android:layout_marginBottom="48dp" />
	
    <!-- Top status bar icon frameworks\base\packages\SystemUI\res\layout\quick_status_bar_expanded_header.xml-->
    <include layout="@layout/quick_status_bar_expanded_header" />
	<!-- bottom toolbars  frameworks\base\packages\SystemUI\res\layout\qs_footer.xml-->
    <include android:id="@+id/qs_footer"
        layout="@layout/qs_footer" />
	...
    <!-- QS Custom panel frameworks\base\packages\SystemUI\res\layout\qs_customize_panel.xml-->
    <include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel"
        android:visibility="gone" />
</com.android.systemui.qs.QSContainerImpl>

2.4 status bar drop-down

2.4.1 pull down status

After understanding the layout of the status bar, let's take a look at the drop-down process of the status bar. The status bar drop-down can be divided into three states: folded state, extended state and QS panel extended state:

  1. Folded state. In this state, the gesture clicks the status bar to lift, the state changes to extended, and then automatically returns to the folded state. This process is called peeping animation.

  2. Extended status. In this state, drag down a certain distance to change the state to QS panel expansion state, and drag up to restore to folding state.

  3. QS panel extension status. In this state, drag up the specified distance to restore the extended state and folded state.

2.4.2 drop down event

Respond to the status bar drop-down by listening for gestures, and analyze the View call chain in response to gesture events using AS Android debugger:

The relevant processing of the status bar drop-down is mainly in PanelView In ontouchevent(), the gesture response here can be divided into two types: one is the automatic recovery after clicking the drop-down, also known as peek peeping process, and the other is dragging the drop-down / pull-up. There are many code logics here, which do not need in-depth analysis.

// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\PanelView.java
public boolean onTouchEvent(MotionEvent event) {
    ...
    // Capture the touch event and update the extended height according to the user's finger
    // If you just click, the drop-down status bar panel (PEEP) will be quickly displayed and closed
    int pointerIndex = event.findPointerIndex(mTrackingPointer);
    if (pointerIndex < 0) {
        pointerIndex = 0;
        mTrackingPointer = event.getPointerId(pointerIndex);
    }
    final float x = event.getX(pointerIndex);
    final float y = event.getY(pointerIndex);
    if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
        // Mgesturewaitfotouchslope will be used to mark whether the drop-down height of the drop-down status bar is 0 at each down event
        mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures();
    }

    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN: // Press
            // Initialize mInitialTouchX=x, InitialTouchY=y, mInitialOffsetOnTouch=mExpandedHeight=mExpandedHeight(0 when not pulled down)
            startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
            mJustPeeked = false; // Indicates whether only peek animation is executed
            mMinExpandHeight = 0.0f;
            // Indicates whether the down event occurs when the drop-down status bar is closed (mxpandedfraction < = 0.0F or mxpandedheight = = 0.0F)
            mPanelClosedOnDown = isFullyCollapsed();
            ...
            // Judge whether the drop-down height of the drop-down status bar is 0
            if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning) 
                				|| mPeekAnimator != null) {
                // mTouchSlopExceeded indicates whether the conservative value of finger movement distance is exceeded. Once it is exceeded, other operations can be done, such as expanding QS
                mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning) || mPeekAnimator != null;
                cancelHeightAnimator(); // Cancel drop-down animation
                cancelPeek(); // Unpick animation
                onTrackingStarted(); // Triggered when clicking, dragging and pulling (automatic recovery will not be triggered)
            }
            if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()) {
                startOpening(); // Triggered when you click the drop-down (perform peek animation, operate the drop-down height)
            }
            break;
            
        case MotionEvent.ACTION_MOVE: // drag 
            trackMovement(event);
            float h = y - mInitialTouchY;
            // Check whether the y component of the gesture exceeds the conservative value of the finger movement distance
            if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX) || mIgnoreXTouchSlop)) {
                mTouchSlopExceeded = true; 
                if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
                    if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
                        // If no folding or pull-down occurs
                        startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
                        h = 0;
                    }
                    cancelHeightAnimator(); // Cancel drop-down animation
                    onTrackingStarted();
                }
            }
            float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
            if (newHeight > mPeekHeight) { // Judge whether the drop-down height exceeds the drop-down height of peeping animation
                if (mPeekAnimator != null) {
                    mPeekAnimator.cancel();// Unpick animation
                }
                mJustPeeked = false; // Set snoop flag to false
            } else if (mPeekAnimator == null && mJustPeeked) { // Peeping has been completed (peeping height has been reached), but it has not been dragged far
                mInitialOffsetOnTouch = mExpandedHeight;
                mInitialTouchY = y;
                mMinExpandHeight = mExpandedHeight;
                mJustPeeked = false;
            }
            newHeight = Math.max(newHeight, mMinExpandHeight);  // Update height
            ...
            if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) {
                setExpandedHeightInternal(newHeight);  // Drop down to the height of the gesture drag
            }
            break;
            
        case MotionEvent.ACTION_CANCEL: 
            trackMovement(event);
            // Decide whether to execute the peeping animation according to the event of pressing and lifting. For example, pressing and lifting quickly will execute the peeping animation
            endMotionEvent(event, x, y, false /* forceCancel */);
            break;
    }
    return !mGestureWaitForTouchSlop || mTracking;
}

3 customization of status bar

3.1 pull down width adaptation

Change the width of the drop-down status bar to fill the entire screen

android:layout_width = "@ dimension / notification_panel_width" changed to match_parent

// frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml
<!--         android:layout_width="@dimen/notification_panel_width"-->
<FrameLayout
    android:id="@+id/qs_frame"
    android:layout="@layout/qs_panel"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="@integer/notification_panel_layout_gravity"
    ... />
<!--         android:layout_width="@dimen/notification_panel_width" -->
<com.android.systemui.statusbar.stack.NotificationStackScrollLayout
    android:id="@+id/notification_stack_scroller"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    ... />

android:layout_width = "@ dimension / notification_panel_width" changed to match_parent

Brightness bar width adaptation

// frameworks\base\packages\SystemUI\res\layout\brightness_mirror.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/brightness_mirror"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="@integer/notification_panel_layout_gravity"
    android:visibility="invisible">
    ...
</FrameLayout>

3.2 volume bar addition

Add a volume bar below the brightness bar

// frameworks\base\packages\SystemUI\src\com\android\systemui\qs\QSPanel.java
protected final View mBrightnessView;
protected final View mVolumeView;
public QSPanel(Context context, AttributeSet attrs) { // Construction method
    super(context, attrs);
    mContext = context;
    setOrientation(VERTICAL);

    mBrightnessView = LayoutInflater.from(context).inflate(R.layout.quick_settings_brightness_dialog, this, false);
    addView(mBrightnessView);

    // Add volume bar control+
    mVolumeView = LayoutInflater.from(context).inflate( R.layout.quick_settings_volume_dialog, this, false);
    addView(mVolumeView);
    ...
    // Here, you can customize the volume bar control function according to your needs
}

Add volume bar layout file quick_settings_volume_dialog.xml, customized as needed

// frameworks\base\packages\SystemUI\res\layout\quick_settings_volume_dialog.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:layout_height="48dp"
    android:layout_width="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp">
    <ImageView
        android:id="@+id/volume_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="8dp"
        android:src="@drawable/ic_volume_remote"
        android:contentDescription="@null"
        android:visibility="gone" />
    <com.android.systemui.settings.ToggleSliderView
        android:id="@+id/volume_slider"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        ... />
</LinearLayout>

3.3 QS icon addition

Add a screenshot function icon in the QS panel list icon, click the full screen screenshot, and long press the area screenshot.

Methods related to creating icons:

// SystemUI\src\com\android\systemui\qs\QSTileHost.java
onTuningChanged(); 
	1. loadTileSpecs();  
		res.getString(R.string.quick_settings_tiles_default); 
  	2. createTile(); 
		return new ScreenshotTile(mHost);
  1. Add a screenshot string at the end of the relevant configuration. When creating an icon, it will be taken out for segmentation and created.
// frameworks\base\packages\SystemUI\res\values\config.xml
<string name="quick_settings_tiles_default" translatable="false">
    wifi,bt,cast,airplane,screenshot
</string>
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
    wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,screenshot
</string>
  1. Added ScreenshotTile class, which can modify relevant methods according to AirplaneModeTile.
// frameworks\base\packages\SystemUI\src\com\android\systemui\qs\tileimpl\QSFactoryImpl.java
public QSTile createTile(String tileSpec) {
    if (tileSpec.equals("wifi")) return new WifiTile(mHost);
    else if (tileSpec.equals("bt")) return new BluetoothTile(mHost);
    else if (tileSpec.equals("cell")) return new CellularTile(mHost);
    else if (tileSpec.equals("dnd")) return new DndTile(mHost);
    else if (tileSpec.equals("inversion")) return new ColorInversionTile(mHost);
    else if (tileSpec.equals("airplane")) return new AirplaneModeTile(mHost);
    ...
    else if (tileSpec.equals("screenshot")) return new ScreenshotTile(mHost); // Add ScreenshotTile class+
    else {
        Log.w(TAG, "Bad tile spec: " + tileSpec);
        return null;
    }
}

ScreenshotTile inherits from QSTileImpl and can modify icon related functions by rewriting methods, such as icon / text display, click / long press response, etc.

For the code calling the system screenshot function, please refer to frameworks \ base \ services \ core \ Java \ com \ Android \ server \ policy \ phonewindowmanager java .

// frameworks\base\packages\SystemUI\src\com\android\systemui\qs\tiles\ScreenshotTile.java
public class ScreenshotTile extends QSTileImpl<BooleanState> {
    @Override
    public void handleClick() { // Click action
        mHost.collapsePanels();
        mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); // Full screen shot
        mHandler.postDelayed(mScreenshotRunnable, SCREENSHOT_DELAY_MILLS);
    }

    @Override
    protected void handleLongClick() { // Long press operation
        mHost.collapsePanels();
        mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_SELECTED_REGION); //Area screenshot
        mHandler.postDelayed(mScreenshotRunnable, SCREENSHOT_DELAY_MILLS);
    }
    
    @Override
    public CharSequence getTileLabel() { // Icon text display
        return mContext.getString(R.string.quick_settings_screenshot_label);
    }
    
    @Override
    protected void handleUpdateState(BooleanState state, Object arg) { // Status refresh
        state.icon = mIcon; // Icon
        state.label = mContext.getString(R.string.quick_settings_screenshot_label); // written words
        state.contentDescription = mContext.getString(R.string.quick_settings_screenshot_label);
        ...
    }
    ...
}

3.4 QS bottom toolbar icon hiding

// frameworks\base\packages\SystemUI\res\layout\qs_footer.xml
<com.android.systemui.qs.QSFooter
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/header"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    ...>
    <!-- Date display -->
    <include
        android:id="@+id/date_time_alarm_group"
        layout="@layout/status_bar_alarm_group"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="8dp"
        .../>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ...
        android:gravity="end">
		<!-- Hide multi-user toggle button -->
        <com.android.systemui.statusbar.phone.MultiUserSwitch
            android:id="@+id/multi_user_switch"
            android:layout_width="48dp"
            android:layout_height="48dp"
            ...>
            <ImageView
                android:id="@+id/multi_user_avatar"
                android:layout_width="@dimen/multi_user_avatar_expanded_size"
                android:layout_height="@dimen/multi_user_avatar_expanded_size"
                android:layout_gravity="center"
                android:scaleType="centerInside"/>
        </com.android.systemui.statusbar.phone.MultiUserSwitch>
		<!--Hide customization QS Icon entry button -->
        <com.android.systemui.statusbar.AlphaOptimizedImageView
            android:id="@android:id/edit"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background="?android:attr/selectableItemBackgroundBorderless"
            .../>
		<!-- Set button -->
        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/settings_button_container"
            android:layout_width="48dp"
            android:layout_height="48dp">
            <com.android.systemui.statusbar.phone.SettingsButton
                android:id="@+id/settings_button"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/ripple_drawable"
                .../>
            <com.android.systemui.statusbar.AlphaOptimizedImageView
                android:id="@+id/tuner_icon"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                ...
                android:visibility="invisible"/>

        </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
		<!-- Status bar extension Icon -->
        <com.android.systemui.statusbar.phone.ExpandableIndicator
            android:id="@+id/expand_indicator"
            android:layout_width="48dp"
            android:layout_height="48dp"
            ... />
    </LinearLayout>
	<!-- The split line will QS Separate from notice -->
    <include layout="@layout/qs_divider"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_gravity="bottom" />
</com.android.systemui.qs.QSFooter>

Setting android:visibility = "gone" in the layout file will not hide successfully. It needs to be hidden in relevant code as needed.

frameworks\base\packages\SystemUI\src\com\android\systemui\qs\QSFooter.java
protected void onFinishInflate() {
    super.onFinishInflate();
    Resources res = getResources();
   
    // Hide these controls as needed
    mEdit = findViewById(android.R.id.edit);
    mDateTimeGroup = findViewById(id.date_time_alarm_group);
    mDate = findViewById(R.id.date);
    mExpandIndicator = findViewById(R.id.expand_indicator);
    mExpandIndicator.setVisibility(
            res.getBoolean(R.bool.config_showQuickSettingsExpandIndicator)
                    ? VISIBLE : GONE);
    mSettingsButton = findViewById(R.id.settings_button);
    mSettingsContainer = findViewById(R.id.settings_button_container);
    mAlarmStatusCollapsed = findViewById(R.id.alarm_status_collapsed);
    mMultiUserSwitch = findViewById(R.id.multi_user_switch);
    mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
    ...
}
private void updateVisibilities() {
    updateAlarmVisibilities();
    mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
            TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
    final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);

    /*mMultiUserSwitch.setVisibility((mExpanded || mAlwaysShowMultiUserSwitch)
            && mMultiUserSwitch.hasMultipleUsers() && !isDemo
            ? View.VISIBLE : View.INVISIBLE);

    if (mShowEditIcon) {
        mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
    }*/
    // Hide multi-user toggle button
    mMultiUserSwitch.setVisibility(GONE);
    // Hide custom QS icon entry button
    mEdit.setVisibility(GONE);
    ...
}

Keywords: Java Android Design Pattern

Added by slick101 on Sun, 20 Feb 2022 14:16:23 +0200