diff options
author | Jean-Michel Trivi <jmtrivi@google.com> | 2012-06-20 09:12:59 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2012-06-20 09:12:59 -0700 |
commit | 6009dc014f8fa0aefc68e7265761960c5433d108 (patch) | |
tree | e07d665f8971abdbfbd8e92063b32db9c475991c /media | |
parent | 0fa755a7bb78f67b244d26c472bf279800558c57 (diff) | |
parent | b92d2a13c4ab34862968f47191d2713de3701757 (diff) | |
download | frameworks_base-6009dc014f8fa0aefc68e7265761960c5433d108.zip frameworks_base-6009dc014f8fa0aefc68e7265761960c5433d108.tar.gz frameworks_base-6009dc014f8fa0aefc68e7265761960c5433d108.tar.bz2 |
am b92d2a13: am 18ddb6ce: Merge "Remote volume handling in MediaRouter" into jb-dev
* commit 'b92d2a13c4ab34862968f47191d2713de3701757':
Remote volume handling in MediaRouter
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/AudioService.java | 50 | ||||
-rw-r--r-- | media/java/android/media/IAudioService.aidl | 2 | ||||
-rw-r--r-- | media/java/android/media/IRemoteVolumeObserver.aidl | 26 | ||||
-rw-r--r-- | media/java/android/media/MediaRouter.java | 269 | ||||
-rw-r--r-- | media/java/android/media/RemoteControlClient.java | 29 |
5 files changed, 350 insertions, 26 deletions
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 418dc52..1b47602 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -4243,6 +4243,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public int mPlaybackVolumeHandling; public int mPlaybackStream; public int mPlaybackState; + public IRemoteVolumeObserver mRemoteVolumeObs; public void resetPlaybackInfo() { mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL; @@ -4251,6 +4252,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; mPlaybackStream = AudioManager.STREAM_MUSIC; mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED; + mRemoteVolumeObs = null; } /** precondition: mediaIntent != null, eventReceiver != null */ @@ -4335,7 +4337,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { " -- state: " + rcse.mPlaybackState + " -- vol handling: " + rcse.mPlaybackVolumeHandling + " -- vol: " + rcse.mPlaybackVolume + - " -- volMax: " + rcse.mPlaybackVolumeMax); + " -- volMax: " + rcse.mPlaybackVolumeMax + + " -- volObs: " + rcse.mRemoteVolumeObs); + } } synchronized (mMainRemote) { @@ -5018,6 +5022,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } + // FIXME send a message instead of updating the stack synchronously + public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) { + synchronized(mRCStack) { + Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); + while(stackIterator.hasNext()) { + RemoteControlStackEntry rcse = stackIterator.next(); + if (rcse.mRccId == rccId) { + rcse.mRemoteVolumeObs = rvo; + break; + } + } + } + } + /** * Checks if a remote client is active on the supplied stream type. Update the remote stream * volume state if found and playing @@ -5100,23 +5118,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // only handling discrete events return; } - String packageForRcc = null; + IRemoteVolumeObserver rvo = null; synchronized (mRCStack) { Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); while(stackIterator.hasNext()) { RemoteControlStackEntry rcse = stackIterator.next(); //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate? if (rcse.mRccId == rccId) { - packageForRcc = rcse.mReceiverComponent.getPackageName(); + rvo = rcse.mRemoteVolumeObs; break; } } } - if (packageForRcc != null) { - Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE); - intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_DIRECTION, direction); - intent.setPackage(packageForRcc); - mContext.sendBroadcast(intent); + if (rvo != null) { + try { + rvo.dispatchRemoteVolumeUpdate(direction, -1); + } catch (RemoteException e) { + Log.e(TAG, "Error dispatching relative volume update", e); + } } } @@ -5147,23 +5166,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } rccId = mMainRemote.mRccId; } - String packageForRcc = null; + IRemoteVolumeObserver rvo = null; synchronized (mRCStack) { Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); while(stackIterator.hasNext()) { RemoteControlStackEntry rcse = stackIterator.next(); if (rcse.mRccId == rccId) { //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate? - packageForRcc = rcse.mReceiverComponent.getPackageName(); + rvo = rcse.mRemoteVolumeObs; break; } } } - if (packageForRcc != null) { - Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE); - intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_VALUE, vol); - intent.setPackage(packageForRcc); - mContext.sendBroadcast(intent); + if (rvo != null) { + try { + rvo.dispatchRemoteVolumeUpdate(0, vol); + } catch (RemoteException e) { + Log.e(TAG, "Error dispatching absolute volume update", e); + } } } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 83483c6..854eb3f 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -24,6 +24,7 @@ import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; import android.media.IRemoteControlClient; import android.media.IRemoteControlDisplay; +import android.media.IRemoteVolumeObserver; import android.media.IRingtonePlayer; import android.net.Uri; import android.view.KeyEvent; @@ -135,6 +136,7 @@ interface IAudioService { oneway void setPlaybackInfoForRcc(int rccId, int what, int value); int getRemoteStreamMaxVolume(); int getRemoteStreamVolume(); + oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo); void startBluetoothSco(IBinder cb); void stopBluetoothSco(IBinder cb); diff --git a/media/java/android/media/IRemoteVolumeObserver.aidl b/media/java/android/media/IRemoteVolumeObserver.aidl new file mode 100644 index 0000000..7ff0c06 --- /dev/null +++ b/media/java/android/media/IRemoteVolumeObserver.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + + +/** + * AIDL for the AudioService to report requests for remote volume update requests. + * @hide + */ +oneway interface IRemoteVolumeObserver { + void dispatchRemoteVolumeUpdate(int direction, int value); +} diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index a9e6e3d..a309c3f 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -589,9 +589,46 @@ public class MediaRouter { RouteGroup mGroup; final RouteCategory mCategory; Drawable mIcon; + // playback information + int mPlaybackType = PLAYBACK_TYPE_LOCAL; + int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; + int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; + int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; + int mPlaybackStream = AudioManager.STREAM_MUSIC; + VolumeCallbackInfo mVcb; private Object mTag; + /** + * @hide (to be un-hidden) + * The default playback type, "local", indicating the presentation of the media is happening + * on the same device (e.g. a phone, a tablet) as where it is controlled from. + * @see #setPlaybackType(int) + */ + public final static int PLAYBACK_TYPE_LOCAL = 0; + /** + * @hide (to be un-hidden) + * A playback type indicating the presentation of the media is happening on + * a different device (i.e. the remote device) than where it is controlled from. + * @see #setPlaybackType(int) + */ + public final static int PLAYBACK_TYPE_REMOTE = 1; + /** + * @hide (to be un-hidden) + * Playback information indicating the playback volume is fixed, i.e. it cannot be + * controlled from this object. An example of fixed playback volume is a remote player, + * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather + * than attenuate at the source. + * @see #setVolumeHandling(int) + */ + public final static int PLAYBACK_VOLUME_FIXED = 0; + /** + * @hide (to be un-hidden) + * Playback information indicating the playback volume is variable and can be controlled + * from this object. + */ + public final static int PLAYBACK_VOLUME_VARIABLE = 1; + RouteInfo(RouteCategory category) { mCategory = category; } @@ -685,6 +722,71 @@ public class MediaRouter { return mTag; } + /** + * @hide (to be un-hidden) + * @return the type of playback associated with this route + * @see UserRouteInfo#setPlaybackType(int) + */ + public int getPlaybackType() { + return mPlaybackType; + } + + /** + * @hide (to be un-hidden) + * @return the stream over which the playback associated with this route is performed + * @see UserRouteInfo#setPlaybackStream(int) + */ + public int getPlaybackStream() { + return mPlaybackStream; + } + + /** + * @hide (to be un-hidden) + * @return the volume at which the playback associated with this route is performed + * @see UserRouteInfo#setVolume(int) + */ + public int getVolume() { + if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { + int vol = 0; + try { + vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream); + } catch (RemoteException e) { + Log.e(TAG, "Error getting local stream volume", e); + } + return vol; + } else { + return mVolume; + } + } + + /** + * @hide (to be un-hidden) + * @return the maximum volume at which the playback associated with this route is performed + * @see UserRouteInfo#setVolumeMax(int) + */ + public int getVolumeMax() { + if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { + int volMax = 0; + try { + volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream); + } catch (RemoteException e) { + Log.e(TAG, "Error getting local stream volume", e); + } + return volMax; + } else { + return mVolumeMax; + } + } + + /** + * @hide (to be un-hidden) + * @return how volume is handling on the route + * @see UserRouteInfo#setVolumeHandling(int) + */ + public int getVolumeHandling() { + return mVolumeHandling; + } + void setStatusInt(CharSequence status) { if (!status.equals(mStatus)) { mStatus = status; @@ -695,6 +797,24 @@ public class MediaRouter { } } + final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() { + public void dispatchRemoteVolumeUpdate(final int direction, final int value) { + sStatic.mHandler.post(new Runnable() { + @Override + public void run() { + //Log.d(TAG, "dispatchRemoteVolumeUpdate dir=" + direction + " val=" + value); + if (mVcb != null) { + if (direction != 0) { + mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction); + } else { + mVcb.vcb.onVolumeSetRequest(mVcb.route, value); + } + } + } + }); + } + }; + void routeUpdated() { updateRoute(this); } @@ -757,10 +877,14 @@ public class MediaRouter { * RemoteControlClient will be used to reflect and update information * such as route volume info in related UIs.</p> * + * <p>The RemoteControlClient must have been previously registered with + * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p> + * * @param rcc RemoteControlClient associated with this route */ public void setRemoteControlClient(RemoteControlClient rcc) { mRcc = rcc; + updatePlaybackInfoOnRcc(); } /** @@ -792,6 +916,111 @@ public class MediaRouter { public void setIconResource(int resId) { setIconDrawable(sStatic.mResources.getDrawable(resId)); } + + /** + * @hide (to be un-hidden) + * Set a callback to be notified of volume update requests + * @param vcb + */ + public void setVolumeCallback(VolumeCallback vcb) { + mVcb = new VolumeCallbackInfo(vcb, this); + } + + /** + * @hide (to be un-hidden) + * Defines whether playback associated with this route is "local" + * ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote" + * ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}). + * @param type + */ + public void setPlaybackType(int type) { + if (mPlaybackType != type) { + mPlaybackType = type; + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type); + } + } + + /** + * @hide (to be un-hidden) + * Defines whether volume for the playback associated with this route is fixed + * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified + * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}). + * @param volumeHandling + */ + public void setVolumeHandling(int volumeHandling) { + if (mVolumeHandling != volumeHandling) { + mVolumeHandling = volumeHandling; + setPlaybackInfoOnRcc( + RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling); + } + } + + /** + * @hide (to be un-hidden) + * Defines at what volume the playback associated with this route is performed (for user + * feedback purposes). This information is only used when the playback is not local. + * @param volume + */ + public void setVolume(int volume) { + if (mVolume != volume) { + mVolume = volume; + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); + } + } + + /** + * @hide (to be un-hidden) + * Defines the maximum volume at which the playback associated with this route is performed + * (for user feedback purposes). This information is only used when the playback is not + * local. + * @param volumeMax + */ + public void setVolumeMax(int volumeMax) { + if (mVolumeMax != volumeMax) { + mVolumeMax = volumeMax; + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax); + } + } + + /** + * @hide (to be un-hidden) + * Defines over what stream type the media is presented. + * @param stream + */ + public void setPlaybackStream(int stream) { + if (mPlaybackStream != stream) { + mPlaybackStream = stream; + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream); + } + } + + private void updatePlaybackInfoOnRcc() { + if ((mRcc != null) && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) { + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType); + // let AudioService know whom to call when remote volume needs to be updated + try { + sStatic.mAudioService.registerRemoteVolumeObserverForRcc( + mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */); + } catch (RemoteException e) { + Log.e(TAG, "Error registering remote volume observer", e); + } + } + } + + private void setPlaybackInfoOnRcc(int what, int value) { + if (mRcc != null) { + mRcc.setPlaybackInformation(what, value); + } + } } /** @@ -1206,4 +1435,44 @@ public class MediaRouter { } } + + static class VolumeCallbackInfo { + public final VolumeCallback vcb; + public final RouteInfo route; + + public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) { + this.vcb = vcb; + this.route = route; + } + } + + /** + * @hide (to be un-hidden) + * Interface for receiving events about volume changes. + * All methods of this interface will be called from the application's main thread. + * + * <p>A VolumeCallback will only receive events relevant to routes that the callback + * was registered for.</p> + * + * @see UserRouteInfo#setVolumeCallback(VolumeCallback) + */ + public static abstract class VolumeCallback { + /** + * Called when the volume for the route should be increased or decreased. + * @param info the route affected by this event + * @param direction 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. + */ + public abstract void onVolumeUpdateRequest(RouteInfo info, int direction); + /** + * Called when the volume for the route should be set to the given value + * @param info the route affected by this event + * @param volume an integer indicating the new volume value that should be used, always + * between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}. + */ + public abstract void onVolumeSetRequest(RouteInfo info, int volume); + } + } diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 5b8035e..79f9d37 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -134,13 +134,13 @@ public class RemoteControlClient public final static int PLAYSTATE_NONE = 0; /** - * @hide (to be un-hidden) + * @hide * The default playback type, "local", indicating the presentation of the media is happening on * the same device (e.g. a phone, a tablet) as where it is controlled from. */ public final static int PLAYBACK_TYPE_LOCAL = 0; /** - * @hide (to be un-hidden) + * @hide * A playback type indicating the presentation of the media is happening on * a different device (i.e. the remote device) than where it is controlled from. */ @@ -148,7 +148,7 @@ public class RemoteControlClient private final static int PLAYBACK_TYPE_MIN = PLAYBACK_TYPE_LOCAL; private final static int PLAYBACK_TYPE_MAX = PLAYBACK_TYPE_REMOTE; /** - * @hide (to be un-hidden) + * @hide * Playback information indicating the playback volume is fixed, i.e. it cannot be controlled * from this object. An example of fixed playback volume is a remote player, playing over HDMI * where the user prefer to control the volume on the HDMI sink, rather than attenuate at the @@ -157,7 +157,7 @@ public class RemoteControlClient */ public final static int PLAYBACK_VOLUME_FIXED = 0; /** - * @hide (to be un-hidden) + * @hide * Playback information indicating the playback volume is variable and can be controlled from * this object. * @see #PLAYBACKINFO_VOLUME_HANDLING. @@ -173,34 +173,34 @@ public class RemoteControlClient //========================================== // Public keys for playback information /** - * @hide (to be un-hidden) + * @hide * Playback information that defines the type of playback associated with this * RemoteControlClient. See {@link #PLAYBACK_TYPE_LOCAL} and {@link #PLAYBACK_TYPE_REMOTE}. */ public final static int PLAYBACKINFO_PLAYBACK_TYPE = 1; /** - * @hide (to be un-hidden) + * @hide * Playback information that defines at what volume the playback associated with this * RemoteControlClient is performed. This information is only used when the playback type is not * local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}). */ public final static int PLAYBACKINFO_VOLUME = 2; /** - * @hide (to be un-hidden) + * @hide * Playback information that defines the maximum volume volume value that is supported * by the playback associated with this RemoteControlClient. This information is only used * when the playback type is not local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}). */ public final static int PLAYBACKINFO_VOLUME_MAX = 3; /** - * @hide (to be un-hidden) + * @hide * Playback information that defines how volume is handled for the presentation of the media. * @see #PLAYBACK_VOLUME_FIXED * @see #PLAYBACK_VOLUME_VARIABLE */ public final static int PLAYBACKINFO_VOLUME_HANDLING = 4; /** - * @hide (to be un-hidden) + * @hide * Playback information that defines over what stream type the media is presented. */ public final static int PLAYBACKINFO_USES_STREAM = 5; @@ -642,7 +642,7 @@ public class RemoteControlClient private int mPlaybackStream = AudioManager.STREAM_MUSIC; /** - * @hide (to be un-hidden) + * @hide * Set information describing information related to the playback of media so the system * can implement additional behavior to handle non-local playback usecases. * @param what a key to specify the type of information to set. Valid keys are @@ -713,7 +713,7 @@ public class RemoteControlClient } /** - * @hide (to be un-hidden) + * @hide * Return playback information represented as an integer value. * @param what a key to specify the type of information to retrieve. Valid keys are * {@link #PLAYBACKINFO_PLAYBACK_TYPE}, @@ -899,6 +899,13 @@ public class RemoteControlClient mRcseId = id; } + /** + * @hide + */ + public int getRcseId() { + return mRcseId; + } + private EventHandler mEventHandler; private final static int MSG_REQUEST_PLAYBACK_STATE = 1; private final static int MSG_REQUEST_METADATA = 2; |