Volume: Improve inline volume controls used in settings.

 - Make the system internal ringer mode change signal available to
   other system components via a standard intent, instead of a
   volume-controller-only binder call.
 - Update sysui to use new intent for icon policy.
 - Add proper muting state to the volumizer, to look more like
   the associated volume panel slider.
 - Add basic ringer mode state tracking for the notification-ring
   slider setting.

Bug: 17899613
Change-Id: Ie2df52edb070055ef59c74039162a0e785d2fdbb
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 3130b64..c3dd4ce 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -44,6 +44,8 @@
 
     public interface Callback {
         void onSampleStarting(SeekBarVolumizer sbv);
+        void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch);
+        void onMuted(boolean muted);
     }
 
     private final Context mContext;
@@ -53,6 +55,8 @@
     private final AudioManager mAudioManager;
     private final int mStreamType;
     private final int mMaxStreamVolume;
+    private boolean mAffectedByRingerMode;
+    private boolean mNotificationOrRing;
     private final Receiver mReceiver = new Receiver();
 
     private Handler mHandler;
@@ -60,8 +64,10 @@
     private int mOriginalStreamVolume;
     private Ringtone mRingtone;
     private int mLastProgress = -1;
+    private boolean mMuted;
     private SeekBar mSeekBar;
     private int mVolumeBeforeMute = -1;
+    private int mRingerMode;
 
     private static final int MSG_SET_STREAM_VOLUME = 0;
     private static final int MSG_START_SAMPLE = 1;
@@ -69,14 +75,22 @@
     private static final int MSG_INIT_SAMPLE = 3;
     private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
 
-    public SeekBarVolumizer(Context context, int streamType, Uri defaultUri,
-            Callback callback) {
+    public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) {
         mContext = context;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mStreamType = streamType;
+        mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
+        mNotificationOrRing = isNotificationOrRing(mStreamType);
+        if (mNotificationOrRing) {
+            mRingerMode = mAudioManager.getRingerModeInternal();
+        }
         mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
         mCallback = callback;
         mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
+        mMuted = mAudioManager.isStreamMute(mStreamType);
+        if (mCallback != null) {
+            mCallback.onMuted(mMuted);
+        }
         if (defaultUri == null) {
             if (mStreamType == AudioManager.STREAM_RING) {
                 defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
@@ -89,6 +103,10 @@
         mDefaultUri = defaultUri;
     }
 
+    private static boolean isNotificationOrRing(int stream) {
+        return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
+    }
+
     public void setSeekBar(SeekBar seekBar) {
         if (mSeekBar != null) {
             mSeekBar.setOnSeekBarChangeListener(null);
@@ -96,10 +114,23 @@
         mSeekBar = seekBar;
         mSeekBar.setOnSeekBarChangeListener(null);
         mSeekBar.setMax(mMaxStreamVolume);
-        mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);
+        updateSeekBar();
         mSeekBar.setOnSeekBarChangeListener(this);
     }
 
+    protected void updateSeekBar() {
+        if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            mSeekBar.setEnabled(true);
+            mSeekBar.setProgress(0);
+        } else if (mMuted) {
+            mSeekBar.setEnabled(false);
+            mSeekBar.setProgress(0);
+        } else {
+            mSeekBar.setEnabled(true);
+            mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);
+        }
+    }
+
     @Override
     public boolean handleMessage(Message msg) {
         switch (msg.what) {
@@ -193,13 +224,13 @@
         mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0);
     }
 
-    public void onProgressChanged(SeekBar seekBar, int progress,
-            boolean fromTouch) {
-        if (!fromTouch) {
-            return;
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+        if (fromTouch) {
+            postSetVolume(progress);
         }
-
-        postSetVolume(progress);
+        if (mCallback != null) {
+            mCallback.onProgressChanged(seekBar, progress, fromTouch);
+        }
     }
 
     private void postSetVolume(int progress) {
@@ -276,14 +307,29 @@
         public void handleMessage(Message msg) {
             if (msg.what == UPDATE_SLIDER) {
                 if (mSeekBar != null) {
-                    mSeekBar.setProgress(msg.arg1);
-                    mLastProgress = mSeekBar.getProgress();
+                    mLastProgress = msg.arg1;
+                    final boolean muted = msg.arg2 != 0;
+                    if (muted != mMuted) {
+                        mMuted = muted;
+                        if (mCallback != null) {
+                            mCallback.onMuted(mMuted);
+                        }
+                    }
+                    updateSeekBar();
                 }
             }
         }
 
-        public void postUpdateSlider(int volume) {
-            obtainMessage(UPDATE_SLIDER, volume, 0).sendToTarget();
+        public void postUpdateSlider(int volume, boolean mute) {
+            obtainMessage(UPDATE_SLIDER, volume, mute ? 1 : 0).sendToTarget();
+        }
+    }
+
+    private void updateSlider() {
+        if (mSeekBar != null && mAudioManager != null) {
+            final int volume = mAudioManager.getStreamVolume(mStreamType);
+            final boolean mute = mAudioManager.isStreamMute(mStreamType);
+            mUiHandler.postUpdateSlider(volume, mute);
         }
     }
 
@@ -295,10 +341,7 @@
         @Override
         public void onChange(boolean selfChange) {
             super.onChange(selfChange);
-            if (mSeekBar != null && mAudioManager != null) {
-                final int volume = mAudioManager.getStreamVolume(mStreamType);
-                mUiHandler.postUpdateSlider(volume);
-            }
+            updateSlider();
         }
     }
 
@@ -310,6 +353,7 @@
             mListening = listening;
             if (listening) {
                 final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
+                filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
                 mContext.registerReceiver(this, filter);
             } else {
                 mContext.unregisterReceiver(this);
@@ -318,11 +362,23 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (!AudioManager.VOLUME_CHANGED_ACTION.equals(intent.getAction())) return;
-            final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
-            final int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
-            if (mSeekBar != null && streamType == mStreamType && streamValue != -1) {
-                mUiHandler.postUpdateSlider(streamValue);
+            final String action = intent.getAction();
+            if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
+                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
+                final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
+                        : (streamType == mStreamType);
+                if (mSeekBar != null && streamMatch && streamValue != -1) {
+                    final boolean muted = mAudioManager.isStreamMute(mStreamType);
+                    mUiHandler.postUpdateSlider(streamValue, muted);
+                }
+            } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
+                if (mNotificationOrRing) {
+                    mRingerMode = mAudioManager.getRingerModeInternal();
+                }
+                if (mAffectedByRingerMode) {
+                    updateSlider();
+                }
             }
         }
     }
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 0d4c0b6..a2da01b 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -150,6 +150,16 @@
     }
 
     @Override
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+        // noop
+    }
+
+    @Override
+    public void onMuted(boolean muted) {
+        // noop
+    }
+
+    @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
         if (isPersistent()) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 0642bcd..3669986 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -90,6 +90,17 @@
     public static final String RINGER_MODE_CHANGED_ACTION = "android.media.RINGER_MODE_CHANGED";
 
     /**
+     * @hide
+     * Sticky broadcast intent action indicating that the internal ringer mode has
+     * changed. Includes the new ringer mode.
+     *
+     * @see #EXTRA_RINGER_MODE
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String INTERNAL_RINGER_MODE_CHANGED_ACTION =
+            "android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION";
+
+    /**
      * The new ringer mode.
      *
      * @see #RINGER_MODE_CHANGED_ACTION
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 0175fd4..7ac079a 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -927,8 +927,9 @@
 
         // Each stream will read its own persisted settings
 
-        // Broadcast the sticky intent
-        broadcastRingerMode(ringerMode);
+        // Broadcast the sticky intents
+        broadcastRingerMode(AudioManager.RINGER_MODE_CHANGED_ACTION, mRingerModeExternal);
+        broadcastRingerMode(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, mRingerMode);
 
         // Broadcast vibrate settings
         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
@@ -1837,7 +1838,6 @@
         } else /*internal*/ {
             if (ringerMode != ringerModeInternal) {
                 setRingerModeInt(ringerMode, true /*persist*/);
-                mVolumeController.postInternalRingerModeChanged(ringerMode);
             }
             if (mRingerModeDelegate != null) {
                 ringerMode = mRingerModeDelegate.onSetRingerModeInternal(ringerModeInternal,
@@ -1853,11 +1853,13 @@
             mRingerModeExternal = ringerMode;
         }
         // Send sticky broadcast
-        broadcastRingerMode(ringerMode);
+        broadcastRingerMode(AudioManager.RINGER_MODE_CHANGED_ACTION, ringerMode);
     }
 
     private void setRingerModeInt(int ringerMode, boolean persist) {
+        final boolean change;
         synchronized(mSettingsLock) {
+            change = mRingerMode != ringerMode;
             mRingerMode = ringerMode;
         }
 
@@ -1903,6 +1905,10 @@
             sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
                     SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
         }
+        if (change) {
+            // Send sticky broadcast
+            broadcastRingerMode(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, ringerMode);
+        }
     }
 
     private void restoreMasterVolume() {
@@ -3276,9 +3282,9 @@
         return suggestedStreamType;
     }
 
-    private void broadcastRingerMode(int ringerMode) {
+    private void broadcastRingerMode(String action, int ringerMode) {
         // Send sticky broadcast
-        Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
+        Intent broadcast = new Intent(action);
         broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
         broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -5711,16 +5717,6 @@
                 Log.w(TAG, "Error calling dismiss", e);
             }
         }
-
-        public void postInternalRingerModeChanged(int mode) {
-            if (mController == null)
-                return;
-            try {
-                mController.internalRingerModeChanged(mode);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Error calling internalRingerModeChanged", e);
-            }
-        }
     }
 
     /**
diff --git a/media/java/android/media/IVolumeController.aidl b/media/java/android/media/IVolumeController.aidl
index c31d80c..e3593a6 100644
--- a/media/java/android/media/IVolumeController.aidl
+++ b/media/java/android/media/IVolumeController.aidl
@@ -34,6 +34,4 @@
     void setLayoutDirection(int layoutDirection);
 
     void dismiss();
-
-    void internalRingerModeChanged(int mode);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 54eb18c..f7f969ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1030,12 +1030,6 @@
         }
     }
 
-    public void onInternalRingerModeChanged() {
-        if (mIconPolicy != null) {
-            mIconPolicy.updateVolumeZen();
-        }
-    }
-
     private void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 94401d3b..5c254a26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -90,7 +90,8 @@
                     action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
                 updateBluetooth();
             }
-            else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
+            else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
+                    action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
                 updateVolumeZen();
             }
             else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
@@ -116,6 +117,7 @@
         filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
         filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+        filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
@@ -208,7 +210,7 @@
         }
     }
 
-    public final void updateVolumeZen() {
+    private final void updateVolumeZen() {
         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
 
         boolean zenVisible = false;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 1fe4698..e79258e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -546,6 +546,7 @@
     private void registerReceiver() {
         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
@@ -554,7 +555,12 @@
 
                 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
                     removeMessages(MSG_RINGER_MODE_CHANGED);
-                    sendMessage(obtainMessage(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)) {
@@ -991,11 +997,6 @@
         obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
     }
 
-    public void postInternalRingerModeChanged(int mode) {
-        removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
-        obtainMessage(MSG_INTERNAL_RINGER_MODE_CHANGED, mode, 0).sendToTarget();
-    }
-
     private static String flagsToString(int flags) {
         return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e452b22..7102c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -182,15 +182,6 @@
         }
 
         @Override
-        public void internalRingerModeChanged(int mode) throws RemoteException {
-            mPanel.postInternalRingerModeChanged(mode);
-            final PhoneStatusBar psb = getComponent(PhoneStatusBar.class);
-            if (psb != null) {
-                psb.onInternalRingerModeChanged();
-            }
-        }
-
-        @Override
         public ZenModeController getZenController() {
             return mPanel.getZenController();
         }