FileDocCategorySizeDatePackage
VolumePanel.javaAPI DocAndroid 5.1 API65489Thu Mar 12 22:22:42 GMT 2015com.android.systemui.volume

VolumePanel

public class VolumePanel extends android.os.Handler implements com.android.systemui.DemoMode
Handles the user interface for the volume keys.
hide

Fields Summary
private static final String
TAG
private static boolean
LOGD
private static final int
PLAY_SOUND_DELAY
public static final int
VIBRATE_DELAY
The delay before vibrating. This small period exists so if the user is moving to silent mode, it will not emit a short vibrate (it normally would since vibrate is between normal mode and silent mode using hardware keys).
private static final int
VIBRATE_DURATION
private static final int
BEEP_DURATION
private static final int
MAX_VOLUME
private static final int
FREE_DELAY
private static final int
TIMEOUT_DELAY
private static final int
TIMEOUT_DELAY_SHORT
private static final int
TIMEOUT_DELAY_COLLAPSED
private static final int
TIMEOUT_DELAY_SAFETY_WARNING
private static final int
TIMEOUT_DELAY_EXPANDED
private static final int
MSG_VOLUME_CHANGED
private static final int
MSG_FREE_RESOURCES
private static final int
MSG_PLAY_SOUND
private static final int
MSG_STOP_SOUNDS
private static final int
MSG_VIBRATE
private static final int
MSG_TIMEOUT
private static final int
MSG_RINGER_MODE_CHANGED
private static final int
MSG_MUTE_CHANGED
private static final int
MSG_REMOTE_VOLUME_CHANGED
private static final int
MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN
private static final int
MSG_SLIDER_VISIBILITY_CHANGED
private static final int
MSG_DISPLAY_SAFE_VOLUME_WARNING
private static final int
MSG_LAYOUT_DIRECTION
private static final int
MSG_ZEN_MODE_AVAILABLE_CHANGED
private static final int
MSG_USER_ACTIVITY
private static final int
MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED
private static final int
MSG_INTERNAL_RINGER_MODE_CHANGED
private static final int
STREAM_MASTER
private static final int
STREAM_REMOTE_MUSIC
private static final android.media.AudioAttributes
VIBRATION_ATTRIBUTES
private static final int
IC_AUDIO_VOL
private static final int
IC_AUDIO_VOL_MUTE
private static final int
IC_AUDIO_BT
private static final int
IC_AUDIO_BT_MUTE
private final String
mTag
protected final android.content.Context
mContext
private final android.media.AudioManager
mAudioManager
private final com.android.systemui.statusbar.policy.ZenModeController
mZenController
private boolean
mRingIsSilent
private boolean
mVoiceCapable
private boolean
mZenModeAvailable
private boolean
mZenPanelExpanded
private int
mTimeoutDelay
private float
mDisabledAlpha
private int
mLastRingerMode
private int
mLastRingerProgress
private int
mDemoIcon
private final boolean
mPlayMasterStreamTones
private final android.view.View
mView
Volume panel content view
private final android.app.Dialog
mDialog
Dialog hosting the panel
private final android.view.ViewGroup
mPanel
The visible portion of the volume overlay
private final android.view.ViewGroup
mSliderPanel
Contains the slider and its touchable icons
private ZenModePanel
mZenPanel
The zen mode configuration panel view
private android.content.ComponentName
mNotificationEffectsSuppressor
The component currently suppressing notification stream effects
private Callback
mCallback
private int
mActiveStreamType
Currently active stream that shows up at the top of the list of sliders
private android.util.SparseArray
mStreamControls
All the slider controls mapped by stream type
private final android.view.accessibility.AccessibilityManager
mAccessibilityManager
private final SecondaryIconTransition
mSecondaryIconTransition
private final IconPulser
mIconPulser
private static final StreamResources[]
STREAMS
private android.media.ToneGenerator[]
mToneGenerators
private android.os.Vibrator
mVibrator
private boolean
mHasVibrator
private static android.app.AlertDialog
sSafetyWarning
private static Object
sSafetyWarningLock
private final android.widget.SeekBar.OnSeekBarChangeListener
mSeekListener
private final ZenModeController.Callback
mZenCallback
private final MediaController.Callback
mMediaControllerCb
Constructors Summary
public VolumePanel(android.content.Context context, com.android.systemui.statusbar.policy.ZenModeController zenController)

    

         
        mTag = String.format("%s.%08x", TAG, hashCode());
        mContext = context;
        mZenController = zenController;
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
                Context.ACCESSIBILITY_SERVICE);
        mSecondaryIconTransition = new SecondaryIconTransition();
        mIconPulser = new IconPulser(context);

        // For now, only show master volume if master volume is supported
        final Resources res = context.getResources();
        final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
        if (useMasterVolume) {
            for (int i = 0; i < STREAMS.length; i++) {
                StreamResources streamRes = STREAMS[i];
                streamRes.show = (streamRes.streamType == STREAM_MASTER);
            }
        }
        if (LOGD) Log.d(mTag, "new VolumePanel");

        mDisabledAlpha = 0.5f;
        if (mContext.getTheme() != null) {
            final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
                    new int[] { android.R.attr.disabledAlpha });
            mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
            arr.recycle();
        }

        mDialog = new Dialog(context) {
            @Override
            public boolean onTouchEvent(MotionEvent event) {
                if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
                        sSafetyWarning == null) {
                    forceTimeout(0);
                    return true;
                }
                return false;
            }
        };

        final Window window = mDialog.getWindow();
        window.requestFeature(Window.FEATURE_NO_TITLE);
        mDialog.setCanceledOnTouchOutside(true);
        mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
        mDialog.setOnDismissListener(new OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                mActiveStreamType = -1;
                mAudioManager.forceVolumeControlStream(mActiveStreamType);
                setZenPanelVisible(false);
                mDemoIcon = 0;
                mSecondaryIconTransition.cancel();
            }
        });

        mDialog.create();

        final LayoutParams lp = window.getAttributes();
        lp.token = null;
        lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
        lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
        lp.format = PixelFormat.TRANSLUCENT;
        lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
        lp.setTitle(TAG);
        window.setAttributes(lp);

        updateWidth();

        window.setBackgroundDrawable(new ColorDrawable(0x00000000));
        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
                | LayoutParams.FLAG_NOT_TOUCH_MODAL
                | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                | LayoutParams.FLAG_HARDWARE_ACCELERATED);
        mView = window.findViewById(R.id.content);
        Interaction.register(mView, new Interaction.Callback() {
            @Override
            public void onInteraction() {
                resetTimeout();
            }
        });

        mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
        mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
        mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
        initZenModePanel();

        mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
        mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
        mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);

        if (mZenController != null && !useMasterVolume) {
            mZenModeAvailable = mZenController.isZenAvailable();
            mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
            mZenController.addCallback(mZenCallback);
        }

        final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
        final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
        mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;

        registerReceiver();
    
Methods Summary
private voidannounceDialogShown()

        mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    
private voidclearRemoteStreamController()
Clear the current remote stream controller.

        if (mStreamControls != null) {
            StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
            if (sc != null) {
                if (sc.controller != null) {
                    sc.controller.unregisterCallback(mMediaControllerCb);
                    sc.controller = null;
                }
            }
        }
    
private voidcreateSliders()

        final Resources res = mContext.getResources();
        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);

        mStreamControls = new SparseArray<StreamControl>(STREAMS.length);

        final StreamResources notificationStream = StreamResources.NotificationStream;
        for (int i = 0; i < STREAMS.length; i++) {
            StreamResources streamRes = STREAMS[i];

            final int streamType = streamRes.streamType;
            final boolean isNotification = isNotificationOrRing(streamType);

            final StreamControl sc = new StreamControl();
            sc.streamType = streamType;
            sc.group = (ViewGroup) inflater.inflate(
                    com.android.systemui.R.layout.volume_panel_item, null);
            sc.group.setTag(sc);
            sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
            sc.icon.setTag(sc);
            sc.icon.setContentDescription(res.getString(streamRes.descRes));
            sc.iconRes = streamRes.iconRes;
            sc.iconMuteRes = streamRes.iconMuteRes;
            sc.icon.setImageResource(sc.iconRes);
            sc.icon.setClickable(isNotification && mHasVibrator);
            if (isNotification) {
                if (mHasVibrator) {
                    sc.icon.setSoundEffectsEnabled(false);
                    sc.iconMuteRes = com.android.systemui.R.drawable.ic_ringer_vibrate;
                    sc.icon.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            resetTimeout();
                            toggleRinger(sc);
                        }
                    });
                }
                sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
            }
            sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
            sc.suppressorView =
                    (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
            sc.suppressorView.setVisibility(View.GONE);
            final boolean showSecondary = !isNotification && notificationStream.show;
            sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
            sc.secondaryIcon = (ImageView) sc.group
                    .findViewById(com.android.systemui.R.id.secondary_icon);
            sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
            sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
            sc.secondaryIcon.setClickable(showSecondary);
            sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
            sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
            if (showSecondary) {
                sc.secondaryIcon.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mSecondaryIconTransition.start(sc);
                    }
                });
            }
            final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
                    streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
            sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
            sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
            sc.seekbarView.setTag(sc);
            mStreamControls.put(streamType, sc);
        }
    
public voiddispatchDemoCommand(java.lang.String command, android.os.Bundle args)

        if (!COMMAND_VOLUME.equals(command)) return;
        String icon = args.getString("icon");
        final String iconMute = args.getString("iconmute");
        final boolean mute = iconMute != null;
        icon = mute ? iconMute : icon;
        icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
        final StreamResources sr = StreamResources.valueOf(icon);
        mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
        final int forcedStreamType = StreamResources.MediaStream.streamType;
        mAudioManager.forceVolumeControlStream(forcedStreamType);
        mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
                AudioManager.FLAG_SHOW_UI);
    
public voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        pw.println("VolumePanel state:");
        pw.print("  mTag="); pw.println(mTag);
        pw.print("  mRingIsSilent="); pw.println(mRingIsSilent);
        pw.print("  mVoiceCapable="); pw.println(mVoiceCapable);
        pw.print("  mHasVibrator="); pw.println(mHasVibrator);
        pw.print("  mZenModeAvailable="); pw.println(mZenModeAvailable);
        pw.print("  mZenPanelExpanded="); pw.println(mZenPanelExpanded);
        pw.print("  mNotificationEffectsSuppressor="); pw.println(mNotificationEffectsSuppressor);
        pw.print("  mTimeoutDelay="); pw.println(mTimeoutDelay);
        pw.print("  mDisabledAlpha="); pw.println(mDisabledAlpha);
        pw.print("  mLastRingerMode="); pw.println(mLastRingerMode);
        pw.print("  mLastRingerProgress="); pw.println(mLastRingerProgress);
        pw.print("  mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
        pw.print("  isShowing()="); pw.println(isShowing());
        pw.print("  mCallback="); pw.println(mCallback);
        pw.print("  sConfirmSafeVolumeDialog=");
        pw.println(sSafetyWarning != null ? "<not null>" : null);
        pw.print("  mActiveStreamType="); pw.println(mActiveStreamType);
        pw.print("  mStreamControls=");
        if (mStreamControls == null) {
            pw.println("null");
        } else {
            final int N = mStreamControls.size();
            pw.print("<size "); pw.print(N); pw.println('>");
            for (int i = 0; i < N; i++) {
                final StreamControl sc = mStreamControls.valueAt(i);
                pw.print("    stream "); pw.print(sc.streamType); pw.print(":");
                if (sc.seekbarView != null) {
                    pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
                    pw.print(" of "); pw.print(sc.seekbarView.getMax());
                    if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
                }
                if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
                pw.println();
            }
        }
        if (mZenPanel != null) {
            mZenPanel.dump(fd, pw, args);
        }
    
private static java.lang.StringflagsToString(int flags)

        return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
    
private voidforceTimeout(long delay)

        if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
        removeMessages(MSG_TIMEOUT);
        sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
    
private android.media.ToneGeneratorgetOrCreateToneGenerator(int streamType)
Lock on this VolumePanel instance as long as you use the returned ToneGenerator.

        if (streamType == STREAM_MASTER) {
            // For devices that use the master volume setting only but still want to
            // play a volume-changed tone, direct the master volume pseudostream to
            // the system stream's tone generator.
            if (mPlayMasterStreamTones) {
                streamType = AudioManager.STREAM_SYSTEM;
            } else {
                return null;
            }
        }
        synchronized (this) {
            if (mToneGenerators[streamType] == null) {
                try {
                    mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
                } catch (RuntimeException e) {
                    if (LOGD) {
                        Log.d(mTag, "ToneGenerator constructor failed with "
                                + "RuntimeException: " + e);
                    }
                }
            }
            return mToneGenerators[streamType];
        }
    
private intgetStreamMaxVolume(int streamType)

        if (streamType == STREAM_MASTER) {
            return mAudioManager.getMasterMaxVolume();
        } else if (streamType == STREAM_REMOTE_MUSIC) {
            if (mStreamControls != null) {
                StreamControl sc = mStreamControls.get(streamType);
                if (sc != null && sc.controller != null) {
                    PlaybackInfo ai = sc.controller.getPlaybackInfo();
                    return ai.getMaxVolume();
                }
            }
            return -1;
        } else {
            return mAudioManager.getStreamMaxVolume(streamType);
        }
    
private intgetStreamVolume(int streamType)

        if (streamType == STREAM_MASTER) {
            return mAudioManager.getMasterVolume();
        } else if (streamType == STREAM_REMOTE_MUSIC) {
            if (mStreamControls != null) {
                StreamControl sc = mStreamControls.get(streamType);
                if (sc != null && sc.controller != null) {
                    PlaybackInfo ai = sc.controller.getPlaybackInfo();
                    return ai.getCurrentVolume();
                }
            }
            return -1;
        } else {
            return mAudioManager.getStreamVolume(streamType);
        }
    
private java.lang.StringgetSuppressorCaption(android.content.ComponentName suppressor)

        final PackageManager pm = mContext.getPackageManager();
        try {
            final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
            if (info != null) {
                final CharSequence seq = info.loadLabel(pm);
                if (seq != null) {
                    final String str = seq.toString().trim();
                    if (str.length() > 0) {
                        return str;
                    }
                }
            }
        } catch (Throwable e) {
            Log.w(TAG, "Error loading suppressor caption", e);
        }
        return suppressor.getPackageName();
    
public com.android.systemui.statusbar.policy.ZenModeControllergetZenController()

        return mZenController;
    
public voidhandleMessage(android.os.Message msg)

        switch (msg.what) {

            case MSG_VOLUME_CHANGED: {
                onVolumeChanged(msg.arg1, msg.arg2);
                break;
            }

            case MSG_MUTE_CHANGED: {
                onMuteChanged(msg.arg1, msg.arg2);
                break;
            }

            case MSG_FREE_RESOURCES: {
                onFreeResources();
                break;
            }

            case MSG_STOP_SOUNDS: {
                onStopSounds();
                break;
            }

            case MSG_PLAY_SOUND: {
                onPlaySound(msg.arg1, msg.arg2);
                break;
            }

            case MSG_VIBRATE: {
                onVibrate();
                break;
            }

            case MSG_TIMEOUT: {
                if (isShowing()) {
                    mDialog.dismiss();
                    clearRemoteStreamController();
                    mActiveStreamType = -1;
                    if (mCallback != null) {
                        mCallback.onVisible(false);
                    }
                }
                synchronized (sSafetyWarningLock) {
                    if (sSafetyWarning != null) {
                        if (LOGD) Log.d(mTag, "SafetyWarning timeout");
                        sSafetyWarning.dismiss();
                    }
                }
                break;
            }

            case MSG_RINGER_MODE_CHANGED:
            case MSG_INTERNAL_RINGER_MODE_CHANGED:
            case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
                if (isShowing()) {
                    updateActiveSlider();
                }
                break;
            }

            case MSG_REMOTE_VOLUME_CHANGED: {
                onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
                break;
            }

            case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
                onRemoteVolumeUpdateIfShown();
                break;

            case MSG_SLIDER_VISIBILITY_CHANGED:
                onSliderVisibilityChanged(msg.arg1, msg.arg2);
                break;

            case MSG_DISPLAY_SAFE_VOLUME_WARNING:
                onDisplaySafeVolumeWarning(msg.arg1);
                break;

            case MSG_LAYOUT_DIRECTION:
                setLayoutDirection(msg.arg1);
                break;

            case MSG_ZEN_MODE_AVAILABLE_CHANGED:
                mZenModeAvailable = msg.arg1 != 0;
                updateZenPanelVisible();
                break;

            case MSG_USER_ACTIVITY:
                if (mCallback != null) {
                    mCallback.onInteraction();
                }
                break;
        }
    
private voidinitZenModePanel()

        mZenPanel.init(mZenController);
        mZenPanel.setCallback(new ZenModePanel.Callback() {
            @Override
            public void onMoreSettings() {
                if (mCallback != null) {
                    mCallback.onZenSettings();
                }
            }

            @Override
            public void onInteraction() {
                resetTimeout();
            }

            @Override
            public void onExpanded(boolean expanded) {
                if (mZenPanelExpanded == expanded) return;
                mZenPanelExpanded = expanded;
                updateTimeoutDelay();
                resetTimeout();
            }
        });
    
private booleanisMuted(int streamType)

        if (streamType == STREAM_MASTER) {
            return mAudioManager.isMasterMute();
        } else if (streamType == STREAM_REMOTE_MUSIC) {
            // TODO do we need to support a distinct mute property for remote?
            return false;
        } else {
            return mAudioManager.isStreamMute(streamType);
        }
    
private static booleanisNotificationOrRing(int streamType)

        return streamType == AudioManager.STREAM_RING
                || streamType == AudioManager.STREAM_NOTIFICATION;
    
private booleanisShowing()

        return mDialog.isShowing();
    
private booleanisZenPanelVisible()

        return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
    
public voidonConfigurationChanged(android.content.res.Configuration newConfig)

        updateWidth();
        if (mZenPanel != null) {
            mZenPanel.updateLocale();
        }
    
protected voidonDisplaySafeVolumeWarning(int flags)

        if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
                || isShowing()) {
            synchronized (sSafetyWarningLock) {
                if (sSafetyWarning != null) {
                    return;
                }
                sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
                sSafetyWarning.show();
            }
            updateStates();
        }
        if (mAccessibilityManager.isTouchExplorationEnabled()) {
            removeMessages(MSG_TIMEOUT);
        } else {
            updateTimeoutDelay();
            resetTimeout();
        }
    
protected voidonFreeResources()

        synchronized (this) {
            for (int i = mToneGenerators.length - 1; i >= 0; i--) {
                if (mToneGenerators[i] != null) {
                    mToneGenerators[i].release();
                }
                mToneGenerators[i] = null;
            }
        }
    
protected voidonMuteChanged(int streamType, int flags)


        if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamToString(streamType)
                + ", flags: " + flagsToString(flags) + ")");

        StreamControl sc = mStreamControls.get(streamType);
        if (sc != null) {
            updateSliderIcon(sc, isMuted(sc.streamType));
        }

        onVolumeChanged(streamType, flags);
    
protected voidonPlaySound(int streamType, int flags)


        if (hasMessages(MSG_STOP_SOUNDS)) {
            removeMessages(MSG_STOP_SOUNDS);
            // Force stop right now
            onStopSounds();
        }

        synchronized (this) {
            ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
            if (toneGen != null) {
                toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
                sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
            }
        }
    
protected voidonRemoteVolumeChanged(android.media.session.MediaController controller, int flags)

        if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: "
                + flagsToString(flags) + ")");

        if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
            synchronized (this) {
                if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
                    reorderSliders(STREAM_REMOTE_MUSIC);
                }
                onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
            }
        } else {
            if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
        }

        removeMessages(MSG_FREE_RESOURCES);
        sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
        resetTimeout();
    
protected voidonRemoteVolumeUpdateIfShown()

        if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
        if (isShowing()
                && (mActiveStreamType == STREAM_REMOTE_MUSIC)
                && (mStreamControls != null)) {
            onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
        }
    
protected voidonShowVolumeChanged(int streamType, int flags, android.media.session.MediaController controller)

        int index = getStreamVolume(streamType);

        mRingIsSilent = false;

        if (LOGD) {
            Log.d(mTag, "onShowVolumeChanged(streamType: " + streamToString(streamType)
                    + ", flags: " + flagsToString(flags) + "), index: " + index);
        }

        // get max volume for progress bar

        int max = getStreamMaxVolume(streamType);
        StreamControl sc = mStreamControls.get(streamType);

        switch (streamType) {

            case AudioManager.STREAM_RING: {
                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
                        mContext, RingtoneManager.TYPE_RINGTONE);
                if (ringuri == null) {
                    mRingIsSilent = true;
                }
                break;
            }

            case AudioManager.STREAM_MUSIC: {
                // Special case for when Bluetooth is active for music
                if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
                        (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
                        AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
                        AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
                    setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
                } else {
                    setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
                }
                break;
            }

            case AudioManager.STREAM_VOICE_CALL: {
                /*
                 * For in-call voice call volume, there is no inaudible volume.
                 * Rescale the UI control so the progress bar doesn't go all
                 * the way to zero and don't show the mute icon.
                 */
                index++;
                max++;
                break;
            }

            case AudioManager.STREAM_ALARM: {
                break;
            }

            case AudioManager.STREAM_NOTIFICATION: {
                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
                        mContext, RingtoneManager.TYPE_NOTIFICATION);
                if (ringuri == null) {
                    mRingIsSilent = true;
                }
                break;
            }

            case AudioManager.STREAM_BLUETOOTH_SCO: {
                /*
                 * For in-call voice call volume, there is no inaudible volume.
                 * Rescale the UI control so the progress bar doesn't go all
                 * the way to zero and don't show the mute icon.
                 */
                index++;
                max++;
                break;
            }

            case STREAM_REMOTE_MUSIC: {
                if (controller == null && sc != null) {
                    // If we weren't passed one try using the last one set.
                    controller = sc.controller;
                }
                if (controller == null) {
                    // We still don't have one, ignore the command.
                    Log.w(mTag, "sent remote volume change without a controller!");
                } else {
                    PlaybackInfo vi = controller.getPlaybackInfo();
                    index = vi.getCurrentVolume();
                    max = vi.getMaxVolume();
                    if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
                        // if the remote volume is fixed add the flag for the UI
                        flags |= AudioManager.FLAG_FIXED_VOLUME;
                    }
                }
                if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
                break;
            }
        }

        if (sc != null) {
            if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
                if (sc.controller != null) {
                    sc.controller.unregisterCallback(mMediaControllerCb);
                }
                sc.controller = controller;
                if (controller != null) {
                    sc.controller.registerCallback(mMediaControllerCb);
                }
            }
            if (sc.seekbarView.getMax() != max) {
                sc.seekbarView.setMax(max);
            }
            updateSliderProgress(sc, index);
            final boolean muted = isMuted(streamType);
            updateSliderEnabled(sc, muted, (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
            if (isNotificationOrRing(streamType)) {
                // check for secondary-icon transition completion
                if (mSecondaryIconTransition.isRunning()) {
                    mSecondaryIconTransition.cancel();  // safe to reset
                    sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
                    mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
                }
                updateSliderIcon(sc, muted);
            }
        }

        if (!isShowing()) {
            int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
            // when the stream is for remote playback, use -1 to reset the stream type evaluation
            if (stream != STREAM_MASTER) {
                mAudioManager.forceVolumeControlStream(stream);
            }
            mDialog.show();
            if (mCallback != null) {
                mCallback.onVisible(true);
            }
            announceDialogShown();
        }

        // Do a little vibrate if applicable (only when going into vibrate mode)
        if ((streamType != STREAM_REMOTE_MUSIC) &&
                ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
                isNotificationOrRing(streamType) &&
                mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
            sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
        }

        // Pulse the zen icon if an adjustment was suppressed due to silent mode.
        if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
            showSilentHint();
        }

        // Pulse the slider icon & vibrate if an adjustment down was suppressed due to vibrate mode.
        if ((flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
            showVibrateHint();
        }
    
protected synchronized voidonSliderVisibilityChanged(int streamType, int visible)
Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider

param
streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
param
visible

        if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
        boolean isVisible = (visible == 1);
        for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
            StreamResources streamRes = STREAMS[i];
            if (streamRes.streamType == streamType) {
                streamRes.show = isVisible;
                if (!isVisible && (mActiveStreamType == streamType)) {
                    mActiveStreamType = -1;
                }
                break;
            }
        }
    
protected voidonStopSounds()


        synchronized (this) {
            int numStreamTypes = AudioSystem.getNumStreamTypes();
            for (int i = numStreamTypes - 1; i >= 0; i--) {
                ToneGenerator toneGen = mToneGenerators[i];
                if (toneGen != null) {
                    toneGen.stopTone();
                }
            }
        }
    
protected voidonVibrate()


        // Make sure we ended up in vibrate ringer mode
        if (mAudioManager.getRingerModeInternal() != AudioManager.RINGER_MODE_VIBRATE) {
            return;
        }
        if (mVibrator != null) {
            mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
        }
    
protected voidonVolumeChanged(int streamType, int flags)
Override this if you have other work to do when the volume changes (for example, vibrating, playing a sound, etc.). Make sure to call through to the superclass implementation.


        if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamToString(streamType)
                + ", flags: " + flagsToString(flags) + ")");

        if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
            synchronized (this) {
                if (mActiveStreamType != streamType) {
                    reorderSliders(streamType);
                }
                onShowVolumeChanged(streamType, flags, null);
            }
        }

        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
            removeMessages(MSG_PLAY_SOUND);
            sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
        }

        if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
            removeMessages(MSG_PLAY_SOUND);
            removeMessages(MSG_VIBRATE);
            onStopSounds();
        }

        removeMessages(MSG_FREE_RESOURCES);
        sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
        resetTimeout();
    
public voidpostDismiss(long delay)

        forceTimeout(delay);
    
public voidpostDisplaySafeVolumeWarning(int flags)

        if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
        obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
    
public voidpostHasNewRemotePlaybackInfo()
Called by AudioService when it has received new remote playback information that would affect the VolumePanel display (mainly volumes). The difference with {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being displayed. This special code path is due to the fact that remote volume updates arrive to AudioService asynchronously. So after AudioService has sent the volume update (which should be treated as a request to update the volume), the application will likely set a new volume. If the UI is still up, we need to refresh the display to show this new value.

        if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
        // don't create or prevent resources to be freed, if they disappear, this update came too
        //   late and shouldn't warrant the panel to be displayed longer
        obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
    
public voidpostLayoutDirection(int layoutDirection)

        removeMessages(MSG_LAYOUT_DIRECTION);
        obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
    
public voidpostMasterMuteChanged(int flags)

        postMuteChanged(STREAM_MASTER, flags);
    
public voidpostMasterVolumeChanged(int flags)

        postVolumeChanged(STREAM_MASTER, flags);
    
public voidpostMuteChanged(int streamType, int flags)

        if (hasMessages(MSG_VOLUME_CHANGED)) return;
        synchronized (this) {
            if (mStreamControls == null) {
                createSliders();
            }
        }
        removeMessages(MSG_FREE_RESOURCES);
        obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
    
public voidpostRemoteSliderVisibility(boolean visible)

        obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
                STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
    
public voidpostRemoteVolumeChanged(android.media.session.MediaController controller, int flags)

        if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
        synchronized (this) {
            if (mStreamControls == null) {
                createSliders();
            }
        }
        removeMessages(MSG_FREE_RESOURCES);
        obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
    
public voidpostVolumeChanged(int streamType, int flags)

        if (hasMessages(MSG_VOLUME_CHANGED)) return;
        synchronized (this) {
            if (mStreamControls == null) {
                createSliders();
            }
        }
        removeMessages(MSG_FREE_RESOURCES);
        obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
    
private voidregisterReceiver()

        final IntentFilter filter = new IntentFilter();
        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
        filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String action = intent.getAction();

                if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
                    removeMessages(MSG_RINGER_MODE_CHANGED);
                    sendEmptyMessage(MSG_RINGER_MODE_CHANGED);
                }

                if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
                    removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
                    sendEmptyMessage(MSG_INTERNAL_RINGER_MODE_CHANGED);
                }

                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                    postDismiss(0);
                }
            }
        }, filter);
    
private voidreorderSliders(int activeStreamType)

        mSliderPanel.removeAllViews();

        final StreamControl active = mStreamControls.get(activeStreamType);
        if (active == null) {
            Log.e(TAG, "Missing stream type! - " + activeStreamType);
            mActiveStreamType = -1;
        } else {
            mSliderPanel.addView(active.group);
            mActiveStreamType = activeStreamType;
            active.group.setVisibility(View.VISIBLE);
            updateSlider(active, true /*forceReloadIcon*/);
            updateTimeoutDelay();
            updateZenPanelVisible();
        }
    
private voidresetTimeout()

        final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
        if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
                + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
        if (sSafetyWarning == null || !touchExploration) {
            removeMessages(MSG_TIMEOUT);
            sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
            removeMessages(MSG_USER_ACTIVITY);
            sendEmptyMessage(MSG_USER_ACTIVITY);
        }
    
public voidsetCallback(com.android.systemui.volume.VolumePanel$Callback callback)

        mCallback = callback;
    
private voidsetLayoutDirection(int layoutDirection)

        mPanel.setLayoutDirection(layoutDirection);
        updateStates();
    
private voidsetMusicIcon(int resId, int resMuteId)
Switch between icons because Bluetooth music is same as music volume, but with different icons.

        StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
        if (sc != null) {
            sc.iconRes = resId;
            sc.iconMuteRes = resMuteId;
            updateSliderIcon(sc, isMuted(sc.streamType));
        }
    
private voidsetStreamVolume(com.android.systemui.volume.VolumePanel$StreamControl sc, int index, int flags)

        if (sc.streamType == STREAM_REMOTE_MUSIC) {
            if (sc.controller != null) {
                sc.controller.setVolumeTo(index, flags);
            } else {
                Log.w(mTag, "Adjusting remote volume without a controller!");
            }
        } else if (getStreamVolume(sc.streamType) != index) {
            if (sc.streamType == STREAM_MASTER) {
                mAudioManager.setMasterVolume(index, flags);
            } else {
                mAudioManager.setStreamVolume(sc.streamType, index, flags);
            }
        }
    
private voidsetZenPanelVisible(boolean visible)

        if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
        final boolean changing = visible != isZenPanelVisible();
        if (visible) {
            mZenPanel.setHidden(false);
            resetTimeout();
        } else {
            mZenPanel.setHidden(true);
        }
        if (changing) {
            updateTimeoutDelay();
            resetTimeout();
        }
    
private voidshowSilentHint()

        if (mZenPanel != null) {
            mZenPanel.showSilentHint();
        }
    
private voidshowVibrateHint()

        final StreamControl active = mStreamControls.get(mActiveStreamType);
        if (active != null) {
            mIconPulser.start(active.icon);
            if (!hasMessages(MSG_VIBRATE)) {
                sendEmptyMessageDelayed(MSG_VIBRATE, VIBRATE_DELAY);
            }
        }
    
private static java.lang.StringstreamToString(int stream)

        return AudioService.streamToString(stream);
    
private voidtoggleRinger(com.android.systemui.volume.VolumePanel$StreamControl sc)

        if (!mHasVibrator) return;
        if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL) {
            mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
            postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
        } else {
            mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
            postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
        }
    
private voidupdateActiveSlider()

        final StreamControl active = mStreamControls.get(mActiveStreamType);
        if (active != null) {
            updateSlider(active, false /*forceReloadIcon*/);
        }
    
private voidupdateSlider(com.android.systemui.volume.VolumePanel$StreamControl sc, boolean forceReloadIcon)
Update the mute and progress state of a slider

        updateSliderProgress(sc, -1);
        final boolean muted = isMuted(sc.streamType);
        if (forceReloadIcon) {
            sc.icon.setImageDrawable(null);
        }
        updateSliderIcon(sc, muted);
        updateSliderEnabled(sc, muted, false);
        updateSliderSuppressor(sc);
    
private voidupdateSliderEnabled(com.android.systemui.volume.VolumePanel$StreamControl sc, boolean muted, boolean fixedVolume)

        final boolean wasEnabled = sc.seekbarView.isEnabled();
        final boolean isRinger = isNotificationOrRing(sc.streamType);
        if (sc.streamType == STREAM_REMOTE_MUSIC) {
            // never disable touch interactions for remote playback, the muting is not tied to
            // the state of the phone.
            sc.seekbarView.setEnabled(!fixedVolume);
        } else if (isRinger && mNotificationEffectsSuppressor != null) {
            sc.icon.setEnabled(true);
            sc.icon.setAlpha(1f);
            sc.icon.setClickable(false);
        } else if (isRinger
                && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
            sc.seekbarView.setEnabled(false);
            sc.icon.setEnabled(false);
            sc.icon.setAlpha(mDisabledAlpha);
            sc.icon.setClickable(false);
        } else if (fixedVolume ||
                (sc.streamType != mAudioManager.getMasterStreamType() && !isRinger && muted) ||
                (sSafetyWarning != null)) {
            sc.seekbarView.setEnabled(false);
        } else {
            sc.seekbarView.setEnabled(true);
            sc.icon.setEnabled(true);
            sc.icon.setAlpha(1f);
        }
        // show the silent hint when the disabled slider is touched in silent mode
        if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
            if (sc.seekbarView.isEnabled()) {
                sc.group.setOnTouchListener(null);
                sc.icon.setClickable(mHasVibrator);
            } else {
                final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        resetTimeout();
                        showSilentHint();
                        return false;
                    }
                };
                sc.group.setOnTouchListener(showHintOnTouch);
            }
        }
    
private voidupdateSliderIcon(com.android.systemui.volume.VolumePanel$StreamControl sc, boolean muted)

        ComponentName suppressor = null;
        if (isNotificationOrRing(sc.streamType)) {
            suppressor = mNotificationEffectsSuppressor;
            int ringerMode = mAudioManager.getRingerModeInternal();
            if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
                ringerMode = mLastRingerMode;
            } else {
                mLastRingerMode = ringerMode;
            }
            if (mHasVibrator) {
                muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
            } else {
                muted = false;
            }
        }
        sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon
                : suppressor != null ? sc.iconSuppressedRes
                : muted ? sc.iconMuteRes
                : sc.iconRes);
    
private voidupdateSliderProgress(com.android.systemui.volume.VolumePanel$StreamControl sc, int progress)

        final boolean isRinger = isNotificationOrRing(sc.streamType);
        if (isRinger && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
            progress = mLastRingerProgress;
        }
        if (progress < 0) {
            progress = getStreamVolume(sc.streamType);
        }
        sc.seekbarView.setProgress(progress);
        if (isRinger) {
            mLastRingerProgress = progress;
        }
    
private voidupdateSliderSuppressor(com.android.systemui.volume.VolumePanel$StreamControl sc)

        final ComponentName suppressor = isNotificationOrRing(sc.streamType)
                ? mNotificationEffectsSuppressor : null;
        if (suppressor == null) {
            sc.seekbarView.setVisibility(View.VISIBLE);
            sc.suppressorView.setVisibility(View.GONE);
        } else {
            sc.seekbarView.setVisibility(View.GONE);
            sc.suppressorView.setVisibility(View.VISIBLE);
            sc.suppressorView.setText(mContext.getString(R.string.muted_by,
                    getSuppressorCaption(suppressor)));
        }
    
private voidupdateStates()

        final int count = mSliderPanel.getChildCount();
        for (int i = 0; i < count; i++) {
            StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
            updateSlider(sc, true /*forceReloadIcon*/);
        }
    
private voidupdateTimeoutDelay()

        mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
                : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
                : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
                : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
                : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
                : TIMEOUT_DELAY;
    
private voidupdateWidth()

        final Resources res = mContext.getResources();
        final LayoutParams lp = mDialog.getWindow().getAttributes();
        lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
        lp.gravity =
                res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
        mDialog.getWindow().setAttributes(lp);
    
private voidupdateZenPanelVisible()

        setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));