summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
authorJean-Michel Trivi <jmtrivi@google.com>2012-06-19 10:54:32 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-06-19 10:54:32 -0700
commit2f4423043ffeaf232ec984be03743326f08cdc8a (patch)
treee1ea822923119e84b19c67ff9ec057cee02f2b4d /core/java
parent5bb835a95942b0b313b2fa4e3f05941e630f9be7 (diff)
parent3114ce3861f20f9a5c2c59dd2629197a1f4874a8 (diff)
downloadframeworks_base-2f4423043ffeaf232ec984be03743326f08cdc8a.zip
frameworks_base-2f4423043ffeaf232ec984be03743326f08cdc8a.tar.gz
frameworks_base-2f4423043ffeaf232ec984be03743326f08cdc8a.tar.bz2
Merge "Remote volume handling" into jb-dev
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/content/Intent.java35
-rw-r--r--core/java/android/view/VolumePanel.java172
2 files changed, 197 insertions, 10 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index edd509b..50972e8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.media.RemoteControlClient;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -2152,6 +2153,19 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.USB_AUDIO_DEVICE_PLUG";
/**
+ * @hide (to be un-hidden)
+ * Broadcast Action: the volume handled by the receiver should be updated based on the
+ * mutually exclusive extras, {@link #EXTRA_VOLUME_UPDATE_DIRECTION}
+ * and {@link #EXTRA_VOLUME_UPDATE_VALUE}.
+ *
+ * @see #EXTRA_VOLUME_UPDATE_DIRECTION
+ * @see #EXTRA_VOLUME_UPDATE_VALUE
+ * @see android.media.RemoteControlClient
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_VOLUME_UPDATE = "android.intent.action.VOLUME_UPDATE";
+
+ /**
* <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
* <ul>
* <li><em>state</em> - A boolean value indicating whether the settings is on or off.</li>
@@ -2839,6 +2853,27 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final String EXTRA_USERID =
"android.intent.extra.user_id";
+
+ /**
+ * @hide (to be un-hidden)
+ * An integer indicating whether the volume is to be increased (positive value) or decreased
+ * (negative value). For bundled changes, the absolute value indicates the number of changes
+ * in the same direction, e.g. +3 corresponds to three "volume up" changes.
+ * @see #ACTION_VOLUME_UPDATE
+ */
+ public static final String EXTRA_VOLUME_UPDATE_DIRECTION =
+ "android.intent.extra.VOLUME_UPDATE_DIRECTION";
+
+ /**
+ * @hide (to be un-hidden)
+ * An integer indicating the new volume value, always between 0 and the value set for
+ * {@link RemoteControlClient#PLAYBACKINFO_VOLUME_MAX} with
+ * {@link RemoteControlClient#setPlaybackInformation(int, int)}
+ * @see #ACTION_VOLUME_UPDATE
+ */
+ public static final String EXTRA_VOLUME_UPDATE_VALUE =
+ "android.intent.extra.VOLUME_UPDATE_VALUE";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 5ffc2c3..cf9bcdd 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -34,10 +34,7 @@ import android.media.ToneGenerator;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.os.Vibrator;
-import android.provider.Settings;
-import android.provider.Settings.System;
import android.util.Log;
import android.view.WindowManager.LayoutParams;
import android.widget.ImageView;
@@ -92,9 +89,13 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
private static final int MSG_TIMEOUT = 5;
private static final int MSG_RINGER_MODE_CHANGED = 6;
private static final int MSG_MUTE_CHANGED = 7;
+ private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
+ private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
+ private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
// Pseudo stream type for master volume
private static final int STREAM_MASTER = -100;
+ // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
protected Context mContext;
private AudioManager mAudioManager;
@@ -155,10 +156,15 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
true),
// for now, use media resources for master volume
MasterStream(STREAM_MASTER,
- R.string.volume_icon_description_media,
+ R.string.volume_icon_description_media, //FIXME should have its own description
R.drawable.ic_audio_vol,
R.drawable.ic_audio_vol_mute,
- false);
+ false),
+ RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
+ R.string.volume_icon_description_media, //FIXME should have its own description
+ R.drawable.ic_media_route_on_holo_dark,
+ R.drawable.ic_media_route_disabled_holo_dark,
+ false);// will be dynamically updated
int streamType;
int descRes;
@@ -184,7 +190,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
StreamResources.MediaStream,
StreamResources.NotificationStream,
StreamResources.AlarmStream,
- StreamResources.MasterStream
+ StreamResources.MasterStream,
+ StreamResources.RemoteStream
};
/** Object that contains data for each slider */
@@ -297,6 +304,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
private boolean isMuted(int streamType) {
if (streamType == STREAM_MASTER) {
return mAudioManager.isMasterMute();
+ } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ return (mAudioService.getRemoteStreamVolume() <= 0);
} else {
return mAudioManager.isStreamMute(streamType);
}
@@ -305,6 +314,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
private int getStreamMaxVolume(int streamType) {
if (streamType == STREAM_MASTER) {
return mAudioManager.getMasterMaxVolume();
+ } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ return mAudioService.getRemoteStreamMaxVolume();
} else {
return mAudioManager.getStreamMaxVolume(streamType);
}
@@ -313,6 +324,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
private int getStreamVolume(int streamType) {
if (streamType == STREAM_MASTER) {
return mAudioManager.getMasterVolume();
+ } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ return mAudioService.getRemoteStreamVolume();
} else {
return mAudioManager.getStreamVolume(streamType);
}
@@ -321,6 +334,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
private void setStreamVolume(int streamType, int index, int flags) {
if (streamType == STREAM_MASTER) {
mAudioManager.setMasterVolume(index, flags);
+ } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ mAudioService.setRemoteStreamVolume(index);
} else {
mAudioManager.setStreamVolume(streamType, index, flags);
}
@@ -398,7 +413,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
}
- if (sc.streamType != mAudioManager.getMasterStreamType() && muted) {
+ if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ // never disable touch interactions for remote playback, the muting is not tied to
+ // the state of the phone.
+ sc.seekbarView.setEnabled(true);
+ } else if (sc.streamType != mAudioManager.getMasterStreamType() && muted) {
sc.seekbarView.setEnabled(false);
} else {
sc.seekbarView.setEnabled(true);
@@ -446,6 +465,40 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
}
+ public void postRemoteVolumeChanged(int streamType, int flags) {
+ if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
+ synchronized (this) {
+ if (mStreamControls == null) {
+ createSliders();
+ }
+ }
+ removeMessages(MSG_FREE_RESOURCES);
+ obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
+ }
+
+ public void postRemoteSliderVisibility(boolean visible) {
+ obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
+ AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
+ }
+
+ /**
+ * 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.
+ */
+ public void postHasNewRemotePlaybackInfo() {
+ 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 void postMasterVolumeChanged(int flags) {
postVolumeChanged(STREAM_MASTER, flags);
}
@@ -585,6 +638,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
max++;
break;
}
+
+ case AudioService.STREAM_REMOTE_MUSIC: {
+ if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
+ break;
+ }
}
StreamControl sc = mStreamControls.get(streamType);
@@ -593,7 +651,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
sc.seekbarView.setMax(max);
}
sc.seekbarView.setProgress(index);
- if (streamType != mAudioManager.getMasterStreamType() && isMuted(streamType)) {
+ if (streamType != mAudioManager.getMasterStreamType()
+ && streamType != AudioService.STREAM_REMOTE_MUSIC && isMuted(streamType)) {
sc.seekbarView.setEnabled(false);
} else {
sc.seekbarView.setEnabled(true);
@@ -601,7 +660,9 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
}
if (!mDialog.isShowing()) {
- mAudioManager.forceVolumeControlStream(streamType);
+ int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
+ // when the stream is for remote playback, use -1 to reset the stream type evaluation
+ mAudioManager.forceVolumeControlStream(stream);
mDialog.setContentView(mView);
// Showing dialog - use collapsed state
if (mShowCombinedVolumes) {
@@ -611,7 +672,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
}
// Do a little vibrate if applicable (only when going into vibrate mode)
- if ((flags & AudioManager.FLAG_VIBRATE) != 0 &&
+ if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
+ ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
mAudioService.isStreamAffectedByRingerMode(streamType) &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
@@ -658,6 +720,72 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
mVibrator.vibrate(VIBRATE_DURATION);
}
+ protected void onRemoteVolumeChanged(int streamType, int flags) {
+ // streamType is the real stream type being affected, but for the UI sliders, we
+ // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
+ // stream type.
+ if (LOGD) Log.d(TAG, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
+
+ if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || mDialog.isShowing()) {
+ synchronized (this) {
+ if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
+ reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
+ }
+ onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
+ }
+ } else {
+ if (LOGD) Log.d(TAG, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
+ }
+
+ 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();
+ }
+
+ protected void onRemoteVolumeUpdateIfShown() {
+ if (LOGD) Log.d(TAG, "onRemoteVolumeUpdateIfShown()");
+ if (mDialog.isShowing()
+ && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
+ && (mStreamControls != null)) {
+ onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
+ }
+ }
+
+
+ /**
+ * 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 AudioService.STREAM_REMOTE_MUSIC
+ * @param visible
+ */
+ synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
+ if (LOGD) Log.d(TAG, "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;
+ }
+ }
+ }
+
/**
* Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
*/
@@ -750,6 +878,19 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
}
break;
}
+
+ case MSG_REMOTE_VOLUME_CHANGED: {
+ onRemoteVolumeChanged(msg.arg1, msg.arg2);
+ break;
+ }
+
+ case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
+ onRemoteVolumeUpdateIfShown();
+ break;
+
+ case MSG_SLIDER_VISIBILITY_CHANGED:
+ onSliderVisibilityChanged(msg.arg1, msg.arg2);
+ break;
}
}
@@ -779,6 +920,17 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
}
public void onStopTrackingTouch(SeekBar seekBar) {
+ final Object tag = seekBar.getTag();
+ if (tag instanceof StreamControl) {
+ StreamControl sc = (StreamControl) tag;
+ // because remote volume updates are asynchronous, AudioService might have received
+ // a new remote volume value since the finger adjusted the slider. So when the
+ // progress of the slider isn't being tracked anymore, adjust the slider to the last
+ // "published" remote volume value, so the UI reflects the actual volume.
+ if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
+ }
+ }
}
public void onClick(View v) {