Android Framework Audio Subsystem (15) Volume Adjustment Setting Interface Volume Adjustment Process

This series of articles Master link: Thematic sub-directory Android Framework Class Audio Subsystem


Summary and description of key points in this chapter:

This chapter focuses on ➕ The process of adjusting the volume in the Setting interface in the process analysis in the upper left volume adjustment process of the mind map above is sufficient. It mainly explains how the volume value of the slider is transferred layer by layer.


1 Find the program entry from notification_settings.xml

In the Setting application, define the volume slider via the file (packages / apps / Settings / res / xml / notification_settings.xml):

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
        android:title="@string/notification_settings"
        android:key="notification_settings"
        settings:keywords="@string/keywords_sounds_and_notifications">

    <PreferenceCategory
        android:key="sound"
        android:title="@string/sound_settings" >

        <!-- Media volume -->
        <com.android.settings.notification.VolumeSeekBarPreference
                android:key="media_volume"
                android:icon="@*android:drawable/ic_audio_vol"
                android:title="@string/media_volume_option_title" />

        <!-- Alarm volume -->
        <com.android.settings.notification.VolumeSeekBarPreference
                android:key="alarm_volume"
                android:icon="@*android:drawable/ic_audio_alarm"
                android:title="@string/alarm_volume_option_title" />
		...
    </PreferenceCategory>
</PreferenceScreen>

The file defines multiple VolumeSeekBarPreference. Each VolumeSeekBarPreference here is bound to a SeekBar. In the binding function onBindView of VolumeSeekBarPreference, the corresponding SeekBar SeekBarChangeListener (a SeekBarVolumizer object) is set. The code is as follows:

@Override
protected void onBindView(View view) {
    super.onBindView(view);
    if (mStream == 0) {
        Log.w(TAG, "No stream found, not binding volumizer");
        return;
    }
    mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
    mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
    mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
    init();
}

2 From the analysis of the init function to sending a message MSG_SET_DEVICE_VOLUME to AudioService

The init function is finally called here, and the code is implemented as follows:

private void init() {
    if (mSeekBar == null) return;
    getPreferenceManager().registerOnActivityStopListener(this);
    final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() {
        @Override
        public void onSampleStarting(SeekBarVolumizer sbv) {
            if (mCallback != null) {
                mCallback.onSampleStarting(sbv);
            }
        }
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
            if (mCallback != null) {
                mCallback.onStreamValueChanged(mStream, progress);
            }
        }
        @Override
        public void onMuted(boolean muted) {
            if (mMuted == muted) return;
            mMuted = muted;
            updateIconView();
        }
    };
    final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
    if (mVolumizer == null) {
        //创建SeekBarVolumizer
        mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc);
    }
    mVolumizer.start();
    //设置mSeekBar
    mVolumizer.setSeekBar(mSeekBar);
    updateIconView();
    mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());
    updateSuppressionText();
}

When SeekBar is slid, onProgressRefresh of SeekBar object is called, the code is as follows:

public class SeekBar extends AbsSeekBar {
    //...
    @Override
    void onProgressRefresh(float scale, boolean fromUser) {
        super.onProgressRefresh(scale, fromUser);

        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
        }
    }
    //...
}

This function will call mOnSeekBarChangeListener.onProgressChanged to set the volume. This mOnSeekBarChangeListener is set in the setSeekBarfan method in SeekBarVolumizer. The code is as follows:

public void setSeekBar(SeekBar seekBar) {
    if (mSeekBar != null) {
        mSeekBar.setOnSeekBarChangeListener(null);
    }
    mSeekBar = seekBar;
    mSeekBar.setOnSeekBarChangeListener(null);
    mSeekBar.setMax(mMaxStreamVolume);
    updateSeekBar();
    mSeekBar.setOnSeekBarChangeListener(this);
}

The setOnSeekBarChangeListener here will be set to SeekBar's mOnSeekBarChangeListener in SeekBarVolumizer itself, so here is concerned about the onProgressChanged implementation of SeekBarVolumizer, the code is as follows:

public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
    if (fromTouch) {
        postSetVolume(progress);
    }
    if (mCallback != null) {
        mCallback.onProgressChanged(seekBar, progress, fromTouch);
    }
}

Continue to analyze postSetVolume here, the code implementation is as follows:

private void postSetVolume(int progress) {
    if (mHandler == null) return;
    // Do the volume changing separately to give responsive UI
    mLastProgress = progress;
    mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
    mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
}

Continue to analyze the message processing flow here, the handleMessage code corresponding to the handler is implemented as follows:

@Override
public boolean handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_SET_STREAM_VOLUME:
            mAudioManager.setStreamVolume(mStreamType, mLastProgress,
                    AudioManager.FLAG_SHOW_UI_WARNINGS);
            break;
        //..
        default:
            Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
    }
    return true;
}

Here begins to enter the audio subsystem, the setStreamVolume code of mAudioManager is implemented as follows:

public void setStreamVolume(int streamType, int index, int flags) {
    IAudioService service = getService();
    try {
        if (mUseMasterVolume) {
            service.setMasterVolume(index, flags, mContext.getOpPackageName());
        } else {
            service.setStreamVolume(streamType, index, flags, mContext.getOpPackageName());
        }
    } catch (RemoteException e) {
        Log.e(TAG, "Dead object in setStreamVolume", e);
    }
}

The setStreamVolume in AUdioService is called here, and the code is implemented as follows:

public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
    setStreamVolume(streamType, index, flags, callingPackage, Binder.getCallingUid());
}

Continue to analyze setStreamVolume, the code implementation is as follows:

private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
        int uid) {
    //...
    synchronized (mSafeMediaVolumeState) {
        // reset any pending volume command
        //...
        if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
            mVolumeController.postDisplaySafeVolumeWarning(flags);
            mPendingVolumeCommand = new StreamVolumeCommand(
                                                streamType, index, flags, device);
        } else {
            //checkSafeMediaVolume为真时
            onSetStreamVolume(streamType, index, flags, device);
            index = mStreamStates[streamType].getIndex(device);
        }
    }
    sendVolumeUpdate(streamType, oldIndex, index, flags);
}

Continue to analyze onSetStreamVolume here, the code implementation is as follows:

private void onSetStreamVolume(int streamType, int index, int flags, int device) {
    setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false);
    //...
}

Continue to analyze setStreamVolumeInt, the code implementation is as follows:

private void setStreamVolumeInt(int streamType,
                                int index,
                                int device,
                                boolean force) {
    VolumeStreamState streamState = mStreamStates[streamType];
    if (streamState.setIndex(index, device) || force) {
        //最终都会调用AudioService.java的代码发出MSG
        sendMsg(mAudioHandler,
                MSG_SET_DEVICE_VOLUME,
                SENDMSG_QUEUE,
                device,
                0,
                streamState,
                0);
    }
}

Finally, MSG_SET_DEVICE_VOLUME was sent to mAudioHandler for processing. The processing flow was  analyzed in Part 3 of the volume key processing flow for volume adjustment of the Android Framework audio subsystem (14) .

Note: Each SeekBar corresponds to a stream. When you slide SeekBar, the volume of the stream is set, and the volume of other streams that belong to the same alias (the same group) is also set. 

Published 291 original articles · praised 47 · 30,000+ views

Guess you like

Origin blog.csdn.net/vviccc/article/details/105457922