This article to share the Android SystemUI drop-down notification panel when you see the switch panel (i.e. QS panel) implementation principle, including its overall architecture,UI construction process and event processing process, interested students can have a look

Analysis of QS panel elements

The QS panel actually has a variety of states, including:

  • Quick Quick Settings (QQS) : The primary expansion panel is a simple VERSION of the QS panel shown in a drop-down panel, with a small number of switches, as shown on the left below
  • Quick Settings (QS) : Complete QS panel, is the second drop-down panel to see the completed QS panel, which contains more switches, as shown below on the right
  • In addition, there are switch edit panel and switch details page, which will not be described in this article

Note: A single switch in the notification drop-down panel switch area is called a Tile in SystemUI

First take a look at the main categories in the QS panel:

The main functions of each class cluster are described below

1. QSTile class cluster. The main classes of this class cluster are as follows and the class diagram is as follows

packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/XxxTile.java
Copy the code

The “back end” of the tiles is responsible for the logical processing of individual tiles, where:

  • QSTile: Interface, which mainly defines allTileGeneral behavior, such asRegister listening, click event handling,TileView Icon element (QSIconView) build, refreshTilestate(state), etc
  • QSTileImpl: ImplementedQSTileDefines common behavior, and provides a set of abstract interfaces (see class diagram) that allow different types of subclasses to implement differently. All subsequent switches need to be inherited fromQSTileImplGraphics.WifiTile, by inheritanceQSTileImplTo enjoy the universal method provided by the switch, it is not necessary for each switch to do the implementation, at the same time, the use of differentiated interface can realize the switch’s own unique logic, such asWifiTilethehandleClickYou can turn on the Wifi switch, andBluetoothTilethehandleClickYou can turn on bluetooth

2.QSTileView class cluster The main classes of this class cluster are as follows, and the class diagram is as follows

packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
Copy the code

This type of cluster forms the view layer of the tiles, which is responsible for handling the interface display of individual tiles, where:

  • Qs /QSTileView: Abstract class, definedTileView-related actions, such as clicking event Settings, view refreshonStateChanged(State state)Etc.
  • QSTileBaseView: ImplementedQSTileViewThe abstract interface defined in theTileView construction, including background processing, click effect processing (such as Ripple), click event processing, etc.Used in the QQS panel
  • QSTileView: Note that this is different from the abstract class with the same name above, where the name is confusingQSTileViewinheritedQSTileBaseView, which extends the view it builds onlabelRelated things,labelIs the text description view in the switch, such as the corresponding bluetooth switchlabelIs bluetooth.Used in QS panel

3.QSHost class cluster. The main classes of this class cluster are as follows and the class diagram is as follows

packages/SystemUI/src/com/android/systemui/qs/QSHost.java
packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
Copy the code

This class of cluster mainly completes the construction of a single Tile, where:

  • QSHost: indicates an interface that provides access to the outside worldQSTileCollection interface (getTiles()) and asTileA bridge for external communication. For example, after a switch is clicked, the operation of folding the panel is triggered, and the switch passesQSHostTo trigger the folding of the panel
  • QSTileHost: ImplementedQSHostThe interface defined in, while extending creationTileThe backend objectQSTileAnd the creation ofTileView objectQSTileViewCreated using factory mode, created byQSFactoryImplClass implements

The QSTileHost serves as the entry point for creating tiles from the outside world. During the object construction process, the Tile backend object QSTile set will be created first, which will be used in the subsequent creation of the full Tile object. What create tiles is by getting configuration in config. The XML in the field to determine the specific process can see QSTileHost. OnTuningChanged (String key, String newValue) method

4.QSPanel class cluster. The main classes of this class cluster are as follows and the class diagram is as follows

packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
packages/SystemUI/src/com/android/systemui/qs/QSPanel#QSTileLayout
packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
Copy the code

This kind of cluster mainly completes the dynamic addition of QS panel elements, where:

  • QSPanel: Corresponding to the QS panel we mentioned earlier, the secondary unfold panel, is the top container of the page, which is nested in oneScrollViewResponsible for dynamically adding corresponding elements, including [brightness bar/dynamically adding switch container QSTileLayout/Footer] according to the screen direction. It also provides a series of operation interfaces for these elements, such as
    • Create a specific switch object for the switch container (via QSHost) and add a refresh listener to it. When the switch back end receives a switch state refresh request, the refresh request is sent to the corresponding switch view layer.
    • Switch creation and refresh of Details
    • Handle the changes of QS panel expansion state (setExpanded(boolean expanded))
  • QuickQSPanel: Corresponds to the QQS panel we talked about earlier, inherited fromQSPanelAnd on displayTileThe number is limited (throughsetMaxTiles(int)), at the same time, the method of adding child elements provided by the parent class is copied, and the elements of QQS panel are added as required. Because QQS panel is a simplified version of QS panel, many child elements are not added
  • QSTileLayout: Switch container interface, mainly defines the switch container drawing related interface
  • TileLayout: QQS panel switch container class, responsible for simplifying the drawing of QS panel, inherited from Viewgroup
  • PagedTileLayout: The QS panel switch container class, responsible for drawing the QS panel, inherited from ViewPager

5.QS class cluster, which includes the main classes as follows and the class diagram is as follows

packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
Copy the code

This kind of cluster is mainly used as the top-level container of the whole QS panel, and mainly deals with the expansion/collapse logic of QS panel, where:

  • QS: defines the interfaces for expanding and unpacking the QS panel
  • QSFragment: inherited fromFragment, realizing the QS interface, is mainly responsible for receiving the changes of the EXPANSION/collapse state of THE QS panel, and synchronizing the latest state to the View element in the Fragment, such as QSContainerImpl
  • QSContainerImpl: Add toQSFragmentCustom View, inherited fromFrameLayoutThe corresponding layoutqs_panel.xml, mainly by receiving fromQSFragmentChange and refresh the panel expansion/collapse state

At this point, the key class of the whole QS panel is introduced, the following will be through several main lines to understand the specific implementation of the whole QS panel:

  • QS Panel switch set construction process
  • TileHow does the back end relateTileThe view layer generates connections
  • TileWhat is the flow behind a click event

Two QS panel internal carding

2.1 Construction process of QS Panel switch set

QsPanel not only creates each switch View, but also creates brightness bar,Footer and other elements. This article focuses on the construction of the switch, so it will not analyze the creation process of other elements. In addition, elements such as QsFragment/QSHost are built by injection or reflection in the SystemUI startup process, and their early build process skips analysis

Let’s look at the overall flow chart

The process is relatively simple, read the source code. To put it simply, the QSTileHost object uses the QSFactoryImpl tool object to create the back-end object QSTile for each switch in the initial construction stage, and then QSPanel uses QSTileHost again to build the view object QSTileView for each switch during the initialization process A complete switch is then built and finally added to the switch container PagedTileLayout. If you’re not sure what each of these classes does, refer back to the previous description of each class cluster

2.2 How does the Tile back end relate to the Tile view layer

QSTile has multiple inner classes, including Callback and State.

The Tile view is connected to the back end with the help of these two inner classes and the QSPanel mediation, so let’s look at the code.

In the previous section 2.1, when combing the construction process of QS panel switch set, we can see that the addTile function is used to build the Tile switch object in Step 10, and the code details are as follows

[packages/SystemUI/src/com/android/systemui/qs/QSPanel.java] public static final class TileRecord extends Record { public QSTile tile; public com.android.systemui.plugins.qs.QSTileView tileView; public boolean scanState; public QSTile.Callback callback; } protected TileRecord addTile(final QSTile tile, boolean collapsedView) { final TileRecord r = new TileRecord(); r.tile = tile; r.tileView = createTileView(tile, collapsedView); Final qstile.callback Callback = new qstile.callback () {@override public void onStateChanged(qstile.state state) { drawTile(r, state); }... }; r.tile.addCallback(callback); R.callback = callback; r.tileView.init(r.tile); // Initialize the click event r.tile.refreshState(); // Refresh mrecords.add (r) for the first time; mCachedSpecs = getTilesSpecs(); if (mTileLayout ! = null) { mTileLayout.addTile(r); // add to switch container} return r; } protected void drawTile(TileRecord r, QSTile.State state) { r.tileView.onStateChanged(state); // Pass the changes to the switch view layer}Copy the code

As you can see, QSPanel registers a callback with R. File, the back end, and passes the switch State to the R.File view, the switch view layer, to refresh the view when the callback occurs.

As for where the State is refreshed, we will analyze this in section 2.3.

What is the flow behind a click on a 2.3 Tile

R.tierview.init (r.tier.init); This completes the transfer of view layer click events to the back end

[packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java]

    @Override
    public void init(QSTile tile) {
        init(v -> tile.click(), v -> tile.secondaryClick(), view -> {
            tile.longClick();
            return true;
        });
    }

    public void init(OnClickListener click, OnClickListener secondaryClick,
            OnLongClickListener longClick) {
        setOnClickListener(click);
        setOnLongClickListener(longClick);
    }
Copy the code

The click events received by QSTileView are handed to the corresponding click function of QSTile for processing

[packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java]

    public void click() {
        mHandler.sendEmptyMessage(H.CLICK);
    }

public void handleMessage(Message msg) {
    if (msg.what == CLICK) {
                    if (mState.disabledByPolicy) {
                        ......
                    } else {
                        handleClick();
                    }
                }
}

abstract protected void handleClick();
Copy the code

The handleClick() function in QSTileImpl is an abstract method implemented by the corresponding switch subclasses, such as the Wifi switch

[packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java] @Override protected void handleClick() { ...... refreshState(wifiEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING); . }Copy the code

Back to QSTileImpl

[packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java]

    protected final void refreshState(Object arg) {
        mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
    }

public void handleMessage(Message msg) {
  if (msg.what == REFRESH_STATE) {
                    handleRefreshState(msg.obj);
                }
}

    protected void handleRefreshState(Object arg) {
        handleUpdateState(mTmpState, arg);

        final boolean changed = mTmpState.copyTo(mState);
        if (changed) {
            handleStateChanged();
        }
        ...
    }

    abstract protected void handleUpdateState(TState state, Object arg);
Copy the code

As you can see, QSTileImpl uses a temporary variable of type State to collect the latest State of the current switch in the handleUpdateState function. This function is an abstract method, and the implementation is still in each switch subclass. Let’s look at Wifi switches

[packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java]

    @Override
    protected void handleUpdateState(SignalState state, Object arg) {
            state.state = Tile.STATE_ACTIVE;
            state.dualTarget = true;
            state.value = transientEnabling || cb.enabled;
            state.activityIn = cb.enabled && cb.activityIn;
            state.activityOut = cb.enabled && cb.activityOut;
}
Copy the code

Here we extract some code snippet, and you can see that the switch back end assigns a value to state based on the current switch state, which will have an effect on subsequent switch view refreshes

After the handleUpdateState function collects the switch state, we go back

[packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java]
    protected void handleRefreshState(Object arg) {
        handleUpdateState(mTmpState, arg);

        final boolean changed = mTmpState.copyTo(mState);
        if (changed) {
            handleStateChanged();
        }
        ...
    }
Copy the code

You can see that handleStateChanged() is called if the switch state changes

[packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java] private void handleStateChanged() { if (mCallbacks.size() ! = 0) { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onStateChanged(mState); }}}Copy the code

This is related to section 2.2, where state is passed to the view layer

[packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java] public void onStateChanged(QSTile.State state) { mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget(); } private class H extends Handler { private static final int STATE_CHANGED = 1; public H() { super(Looper.getMainLooper()); } @Override public void handleMessage(Message msg) { if (msg.what == STATE_CHANGED) { handleStateChanged((QSTile.State) msg.obj); }}}Copy the code

The handleStateChanged function is left unexplored, this function is where the switch view display refresh is actually done, see the source code for details.

At this point, we will switch click behind the process of sorting out, I believe that after reading the three main lines of the code process, the overall implementation of the WHOLE QS panel is very clear

Design thinking

In front of the reading Section 2.3 of the code, we can see, both in Tile logic layer and view layer, its internal all through the Handler to organize the state of the switch refresh, because there are all kinds of the QS panel switch, the switch of the refresh time is uncertain, and through the message mechanism to be methodical all switch refresh orderly organized, this In Android reflects a very important feature order, which is worth learning and reference.