VolumePanel: Add support for master volume

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 24a3066..0d60d3f 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -92,6 +92,9 @@
     private static final int MSG_TIMEOUT = 5;
     private static final int MSG_RINGER_MODE_CHANGED = 6;
 
+    // Pseudo stream type for master volume
+    private static final int STREAM_MASTER = -1;
+
     protected Context mContext;
     private AudioManager mAudioManager;
     protected AudioService mAudioService;
@@ -148,7 +151,13 @@
                 R.string.volume_icon_description_notification,
                 R.drawable.ic_audio_notification,
                 R.drawable.ic_audio_notification_mute,
-                true);
+                true),
+        // for now, use media resources for master volume
+        MasterStream(STREAM_MASTER,
+                R.string.volume_icon_description_media,
+                R.drawable.ic_audio_vol,
+                R.drawable.ic_audio_vol_mute,
+                false);
 
         int streamType;
         int descRes;
@@ -173,7 +182,8 @@
         StreamResources.VoiceStream,
         StreamResources.MediaStream,
         StreamResources.NotificationStream,
-        StreamResources.AlarmStream
+        StreamResources.AlarmStream,
+        StreamResources.MasterStream
     };
 
     /** Object that contains data for each slider */
@@ -195,6 +205,16 @@
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mAudioService = volumeService;
 
+        // For now, only show master volume if master volume is supported
+        boolean useMasterVolume = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
+        if (useMasterVolume) {
+            for (int i = 0; i < STREAMS.length; i++) {
+                StreamResources streamRes = STREAMS[i];
+                streamRes.show = (streamRes.streamType == STREAM_MASTER);
+            }
+        }
+
         LayoutInflater inflater = (LayoutInflater) context
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         View view = mView = inflater.inflate(R.layout.volume_adjust, null);
@@ -245,7 +265,7 @@
         mVibrator = new Vibrator();
 
         mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
-        mShowCombinedVolumes = !mVoiceCapable;
+        mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
         // If we don't want to show multiple volumes, hide the settings button and divider
         if (!mShowCombinedVolumes) {
             mMoreButton.setVisibility(View.GONE);
@@ -274,7 +294,49 @@
     }
 
     private boolean isMuted(int streamType) {
-        return mAudioManager.isStreamMute(streamType);
+        if (streamType == STREAM_MASTER) {
+            // master volume mute not yet supported
+            return false;
+        } else {
+            return mAudioService.isStreamMute(streamType);
+        }
+    }
+
+    private int getStreamMaxVolume(int streamType) {
+        // master volume is 0.0f - 1.0f, but we will use 0 - 100 internally
+        if (streamType == STREAM_MASTER) {
+            return 100;
+        } else {
+            return mAudioService.getStreamMaxVolume(streamType);
+        }
+    }
+
+    private int getStreamVolume(int streamType) {
+         // master volume is 0.0f - 1.0f, but we will use 0 - 100 internally
+        if (streamType == STREAM_MASTER) {
+            return Math.round(mAudioService.getMasterVolume() * 100);
+        } else {
+            return mAudioService.getStreamVolume(streamType);
+        }
+    }
+
+    private void setStreamVolume(int streamType, int index, int flags) {
+         // master volume is 0.0f - 1.0f, but we will use 0 - 100 internally
+        if (streamType == STREAM_MASTER) {
+            mAudioService.setMasterVolume((float)index / 100.0f);
+        } else {
+            mAudioService.setStreamVolume(streamType, index, flags);
+        }
+    }
+
+    private int getLastAudibleStreamVolume(int streamType) {
+         // master volume is 0.0f - 1.0f, but we will use 0 - 100 internally
+        if (streamType == STREAM_MASTER) {
+            // master volume mute not yet supported
+            return getStreamVolume(STREAM_MASTER);
+        } else {
+            return mAudioService.getLastAudibleStreamVolume(streamType);
+        }
     }
 
     private void createSliders() {
@@ -301,7 +363,7 @@
             sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
             int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
                     streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
-            sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(streamType) + plusOne);
+            sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
             sc.seekbarView.setOnSeekBarChangeListener(this);
             sc.seekbarView.setTag(sc);
             mStreamControls.put(streamType, sc);
@@ -342,7 +404,7 @@
 
     /** Update the mute and progress state of a slider */
     private void updateSlider(StreamControl sc) {
-        sc.seekbarView.setProgress(mAudioManager.getLastAudibleStreamVolume(sc.streamType));
+        sc.seekbarView.setProgress(getLastAudibleStreamVolume(sc.streamType));
         final boolean muted = isMuted(sc.streamType);
         sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
         if (sc.streamType == AudioManager.STREAM_RING && muted
@@ -390,6 +452,10 @@
         obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
     }
 
+    public void postMasterVolumeChanged(int flags) {
+        postVolumeChanged(STREAM_MASTER, 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
@@ -424,9 +490,9 @@
     }
 
     protected void onShowVolumeChanged(int streamType, int flags) {
-        int index = mAudioService.isStreamMute(streamType) ?
-                mAudioService.getLastAudibleStreamVolume(streamType)
-                : mAudioService.getStreamVolume(streamType);
+        int index = isMuted(streamType) ?
+                getLastAudibleStreamVolume(streamType)
+                : getStreamVolume(streamType);
 
         mRingIsSilent = false;
 
@@ -437,7 +503,7 @@
 
         // get max volume for progress bar
 
-        int max = mAudioService.getStreamMaxVolume(streamType);
+        int max = getStreamMaxVolume(streamType);
 
         switch (streamType) {
 
@@ -571,6 +637,7 @@
      * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
      */
     private ToneGenerator getOrCreateToneGenerator(int streamType) {
+        if (streamType == STREAM_MASTER) return null;
         synchronized (this) {
             if (mToneGenerators[streamType] == null) {
                 try {
@@ -671,8 +738,8 @@
         final Object tag = seekBar.getTag();
         if (fromUser && tag instanceof StreamControl) {
             StreamControl sc = (StreamControl) tag;
-            if (mAudioManager.getStreamVolume(sc.streamType) != progress) {
-                mAudioManager.setStreamVolume(sc.streamType, progress, 0);
+            if (getStreamVolume(sc.streamType) != progress) {
+                setStreamVolume(sc.streamType, progress, 0);
             }
         }
         resetTimeout();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d131176..3f5d192 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -386,7 +386,7 @@
              * since the last volume key up, so cancel any sounds.
              */
             if (mUseMasterVolume) {
-                adjustMasterVolume(ADJUST_SAME);
+                adjustMasterVolume(ADJUST_SAME, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
             } else {
                 adjustSuggestedStreamVolume(ADJUST_SAME,
                         stream, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
@@ -410,7 +410,8 @@
                     adjustMasterVolume(
                             keyCode == KeyEvent.KEYCODE_VOLUME_UP
                                     ? ADJUST_RAISE
-                                    : ADJUST_LOWER);
+                                    : ADJUST_LOWER,
+                            flags);
                 } else {
                     if (mVolumeControlStream != -1) {
                         stream = mVolumeControlStream;
@@ -443,7 +444,7 @@
                  */
                 if (mUseMasterVolume) {
                     if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
-                        adjustMasterVolume(ADJUST_SAME);
+                        adjustMasterVolume(ADJUST_SAME, FLAG_PLAY_SOUND);
                     }
                 } else {
                     int flags = FLAG_PLAY_SOUND;
@@ -549,11 +550,12 @@
      * @param direction The direction to adjust the volume. One of
      *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
      *            {@link #ADJUST_SAME}.
+     * @param flags One or more flags.
      */
-    private void adjustMasterVolume(int direction) {
+    private void adjustMasterVolume(int direction, int flags) {
         IAudioService service = getService();
         try {
-            service.adjustMasterVolume(direction);
+            service.adjustMasterVolume(direction, flags);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in adjustMasterVolume", e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 8b97594..8259202 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -615,7 +615,7 @@
     }
 
     /** @see AudioManager#adjustMasterVolume(int) */
-    public void adjustMasterVolume(int direction) {
+    public void adjustMasterVolume(int direction, int flags) {
         ensureValidDirection(direction);
 
         float volume = AudioSystem.getMasterVolume();
@@ -631,6 +631,7 @@
             long origCallerIdentityToken = Binder.clearCallingIdentity();
             Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER, volume);
             Binder.restoreCallingIdentity(origCallerIdentityToken);
+            mVolumePanel.postMasterVolumeChanged(flags);
         }
     }
 
@@ -764,6 +765,14 @@
         return (mStreamStates[streamType].getIndex(device, false  /* lastAudible */) + 5) / 10;
     }
 
+    public float getMasterVolume() {
+        return AudioSystem.getMasterVolume();
+    }
+
+    public void setMasterVolume(float volume) {
+        AudioSystem.setMasterVolume(volume);
+    }
+
     /** @see AudioManager#getStreamMaxVolume(int) */
     public int getStreamMaxVolume(int streamType) {
         ensureValidStreamType(streamType);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fd15c69..01a2314 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -33,7 +33,7 @@
 
     void adjustStreamVolume(int streamType, int direction, int flags);
 
-    void adjustMasterVolume(int direction);
+    void adjustMasterVolume(int direction, int flags);
 
     void setStreamVolume(int streamType, int index, int flags);
     
@@ -44,7 +44,9 @@
     boolean isStreamMute(int streamType);
 
     int getStreamVolume(int streamType);
-    
+
+    float getMasterVolume();
+
     int getStreamMaxVolume(int streamType);
     
     int getLastAudibleStreamVolume(int streamType);