Android SystemUI 音量条VolumeDialogImpl更新

1.AudioService::adjustStreamVolume

    private void adjustStreamVolume(int streamType, int direction, int flags,
            String callingPackage, String caller, int uid) {
......
//finally update notify systemui to update
        int index = mStreamStates[streamType].getIndex(device);
        sendVolumeUpdate(streamType, oldIndex, index, flags);

2.flags一定要是奇数才能显示UI,对应Audiomanager 中public static final int FLAG_SHOW_UI = 1 << 0;

    // UI update and Broadcast Intent
    protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
        streamType = mStreamVolumeAlias[streamType];

        if (streamType == AudioSystem.STREAM_MUSIC) {
            flags = updateFlagsForSystemAudio(flags);
        }
        mVolumeController.postVolumeChanged(streamType, flags);
    }

3.mVolumeController是AudioService的内部类VolumeController,封装了VolumeDialogControllerImpl,也就是mController,这是SystemUI启动的时候通过AudioManager的setVolumeController设置的。

        public void postVolumeChanged(int streamType, int flags) {
            if (mController == null)
                return;
            try {
                mController.volumeChanged(streamType, flags);
            } catch (RemoteException e) {
                Log.w(TAG, "Error calling volumeChanged", e);
            }
        }

4.SystemUI VolumeDialogControllerImpl内部类的VC

    private final class VC extends IVolumeController.Stub {
...
        @Override
        public void volumeChanged(int streamType, int flags) throws RemoteException {
            if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
                    + " " + Util.audioManagerFlagsToString(flags));
            if (mDestroyed) return;
            mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
        }

5.业务处理也是VolumeDialogControllerImpl内部类Handler继承类W做的

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;

6.showUI是音量条开关,值是1,fromKey就是key事件启动的,值是4096。updateActiveStreamw是更新mState中的active stream,lastAudibleStreamVolume是从AudioService去取对应stream的音量值,updateStreamLevelW是将这个值保存下来。关键是mCallbacks.onStateChanged和mCallbacks.onShowRequested。对于盒子而言要关注的Callback是VolumeDialogImpl,onStateChanged会更新音量条要显示那些stream的条及对应大小值,onShowRequested则会请求显示音量条或者更新dismiss的时间。

    boolean onVolumeChangedW(int stream, int flags) {
        final boolean showUI = shouldShowUI(flags);
        final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
        final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
        final boolean showSilentHint = (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();
        }
        if (changed && fromKey) {
            Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
        }
        return changed;
    }
7.VolumeDialogImpl,主要是对各个stream进行UI的更新。
    private void onStateChangedH(State state) {
        final boolean animating = mMotion.isAnimating();
        if (D.BUG) Log.d(TAG, "onStateChangedH animating=" + animating);
        mState = state;
        if (animating) {
            mPendingStateChanged = true;
            return;
        }
        mDynamic.clear();
        // add any new dynamic rows
        for (int i = 0; i < state.states.size(); i++) {
            final int stream = state.states.keyAt(i);
            final StreamState ss = state.states.valueAt(i);
            if (!ss.dynamic) continue;
            mDynamic.put(stream, true);
            if (findRow(stream) == null) {
                addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true,
                        true);
            }
        }

        if (mActiveStream != state.activeStream) {
            mPrevActiveStream = mActiveStream;
            mActiveStream = state.activeStream;
            updateRowsH(getActiveRow());
            rescheduleTimeoutH();
        }
        for (VolumeRow row : mRows) {
            updateVolumeRowH(row);
        }
        updateFooterH();
    }
8.Volume dialog显示这块逻辑的日志和events打印很足,非常方便debug,写的真好。每次都是先移除之前的SHOW和DISMISS message,然后定时dismiss。真要显示会调用mMotion.startShow,还会通知AudioService。
    private void showH(int reason) {
        if (D.BUG) Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
        mHandler.removeMessages(H.SHOW);
        mHandler.removeMessages(H.DISMISS);
        rescheduleTimeoutH();
        if (mShowing) return;
        mShowing = true;
        mMotion.startShow();
        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
        mController.notifyVisible(true);
    }

9.定时dismiss,mController.userActivity最终会调到PMS的userActivity来更新下一次休眠时间。

    protected void rescheduleTimeoutH() {
        mHandler.removeMessages(H.DISMISS);
        final int timeout = computeTimeoutH();
        mHandler.sendMessageDelayed(mHandler
                .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
        if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
        mController.userActivity();
    }
10.VolumeDialogMotion的show和dismiss,主要是涉及到animation的处理,要考虑到正在显示showing或者dismissing animation的情况。startShow如果正在mDismissing的状态会取消掉dismissing anmiation,但这里有个bug,大家一看便知,会导致dismiss最终还是会发生,并且是在dialog show之后,引起音量条不显示。
    public void startShow() {
        if (D.BUG) Log.d(TAG, "startShow");
        if (mShowing) return;
        setShowing(true);
        if (mDismissing) {
            mDialogView.animate().cancel();
            setDismissing(false);
            startShowAnimation();
            return;
        }
        if (D.BUG) Log.d(TAG, "mDialog.show()");
        mDialog.show();
    }
    public void startDismiss(final Runnable onComplete) {
        if (D.BUG) Log.d(TAG, "startDismiss");
        if (mDismissing) return;
        setDismissing(true);
        if (mShowing) {
            mDialogView.animate().cancel();
            if (mContentsPositionAnimator != null) {
                mContentsPositionAnimator.cancel();
            }
            mContents.animate().cancel();
            if (mChevronPositionAnimator != null) {
                mChevronPositionAnimator.cancel();
            }
            mChevron.animate().cancel();
            setShowing(false);
        }
        mDialogView.animate()
                .translationY(-mDialogView.getHeight())
                .setDuration(scaledDuration(250))
                .setInterpolator(new LogAccelerateInterpolator())
                .setUpdateListener(new AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        mContents.setTranslationY(-mDialogView.getTranslationY());
                        final int posY = chevronPosY();
                        mChevron.setTranslationY(posY + -mDialogView.getTranslationY());
                    }
                })
                .setListener(new AnimatorListenerAdapter() {
                    private boolean mCancelled;
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        if (mCancelled) return;
                        if (D.BUG) Log.d(TAG, "dismiss.onAnimationEnd");
                                                mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
                                mDialog.dismiss();
                                onComplete.run();
                                setDismissing(false);
                            }
                        }, PRE_DISMISS_DELAY);
                    }
                    @Override
                    public void onAnimationCancel(Animator animation) {
                        if (D.BUG) Log.d(TAG, "dismiss.onAnimationCancel");
                        mCancelled = true;
                    }
                }).start();
    }


你可能感兴趣的:(Android SystemUI 音量条VolumeDialogImpl更新)