This article analyzes SystemUI VolumeUI module, this module is relatively simple, it uses MVP architecture to complete the design, as shown in the following figure

This article first explains how this architecture came to be and then looks at the process of pressing the Power key.

The creation of the MVP

VolumeUI#start() = VolumeUI#start()

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
    
    public void start(a) {
        // ...

        // Create a VolumeDialogComponent object
        mVolumeComponent = SystemUIFactory.getInstance()
                .createVolumeDialogComponent(this, mContext);
        mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
        
        // ...
        
        // Enable the VolumeUI function
        mVolumeComponent.register();
    }
Copy the code

When VolumeUI is launched, a VolumeDialogComponent object is created, which, as the name indicates, represents the VolumeUI component from which the entire MVP can be created.

Once the VolumeDialogComponent object is created, its register() method is called to launch the VolumeUI function. It is simply associating the Presenter layer with the Model layer.

Let’s start with the constructor of the VolumeDialogComponent

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
    
    public VolumeDialogComponent(SystemUI sysui, Context context) {
        // ...

        // After build(), createDefault() is called, followed by Callback
        Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                .withPlugin(VolumeDialog.class)
                .withDefault(this::createDefault)
                .withCallback(dialog -> {
                    if(mDialog ! =null) {
                        mDialog.destroy();
                    }
                    mDialog = dialog;
                    mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
                }).build();

        / /... Omitted the code for the Volume Policy function
    }
    
    protected VolumeDialog createDefault(a) {
        VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
        impl.setAutomute(true);
        impl.setSilentMode(false);
        return impl;
    }    
Copy the code

The VolumeDialogComponent creates the VolumeDialogImpl object with createDefault(), which represents the View layer, and then initializes it with init().

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
    
    public VolumeDialogImpl(Context context) {
        // VolumeDialogControllerImpl
        mController = Dependency.get(VolumeDialogController.class);
    }
    
    public void init(int windowType, Callback callback) {
        // Create Dialog and set parameters
        initDialog();
        // Set the callback
        mController.addCallback(mControllerCallbackH, mHandler);
    }  
Copy the code

In VolumeDialogImpl layer (View) in the constructor of, created VolumeDialogControllerImpl object, it represents the Presenter layer.

In the init (), to the VolumeDialogControllerImpl (Presenter layer) to register a callback, which is associated with the Presenter layer, View layer which can control the View through the Presenter layer layer.

Now that the View layer is associated with the Presenter layer, what about the Model layer? Remember the code that started the VolumeUI function earlier? It is called VolumeDialogComponent# register (), it is the Model layer associated with the Presenter, specific invocation is VolumeDialogControllerImpl# register ()

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java

    protected final VC mVolumeController = new VC();
    
    public void register(a) {
        try {
            // Register a Binder with the Audio Manager, which is essentially a callback
            mAudio.setVolumeController(mVolumeController);
        } catch (SecurityException e) {
            Log.w(TAG, "Unable to set the volume controller", e);
            return; }}Copy the code

To the Audio Audio Manager is the Model layer, VolumeDialogControllerImpl Manager to register a callback, in fact, the Presenter layer and Model layer.

Volume UI display

Now that the MVP framework is in place, let’s analyze how VolumeUI displays the UI when the Power key is pressed.

Due to Audio VolumeDialogControllerImpl Manager to register the callback, when press the volume button to adjust the volume, VolumeDialogControllerImpl will receive callback

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
    
    private final class VC extends IVolumeController.Stub {
        @Override
        public void volumeChanged(int streamType, int flags) throws RemoteException {
            / / call onVolumeChangedW ()mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget(); }}boolean onVolumeChangedW(int stream, int flags) {
        final boolean showUI = shouldShowUI(flags);
        final booleanfromKey = (flags & AudioManager.FLAG_FROM_KEY) ! =0;
        final booleanshowVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) ! =0;
        final booleanshowSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) ! =0;
        boolean changed = false;
        if (showUI) {
            changed |= updateActiveStreamW(stream);
        }
        int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
        changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
        changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
        if (changed) {
            mCallbacks.onStateChanged(mState);
        }
        if (showUI) {
            mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
        }
        if (showVibrateHint) {
            mCallbacks.onShowVibrateHint();
        }
        if (showSilentHint) {
            mCallbacks.onShowSilentHint();
        }
        return changed;
    }
Copy the code

Depending on flags which callback is executed, onShowRequested() is called if the UI is to be displayed, and this callback is of course implemented by the View layer.

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
    
    private final VolumeDialogController.Callbacks mControllerCallbackH
            = new VolumeDialogController.Callbacks() {
        @Override
        public void onShowRequested(int reason) { showH(reason); }}private void showH(int reason) {
        / / display the Dialog
        mDialog.show();
    }    
Copy the code

The View layer completes the display of a Dialog.

A little idea

VolumeUI does more than that, of course, but once you understand the design of this MVP, it’s easy to analyze other features.

VolumeDialogComponent also controls the Volume Policy function in addition to creating the View layer, but this function is controlled by SettingsProvider. The Presenter layer ultimately controls Volume Policy. According to the principle of single responsibility in the design pattern, the code for Volume Policy can be implemented entirely by Presenter, thus eliminating the VolumeDialogComponent, which is just my opinion.