diff options
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java | 85 | ||||
-rw-r--r-- | media/java/android/media/MediaRouter.java | 196 |
3 files changed, 238 insertions, 47 deletions
diff --git a/api/current.txt b/api/current.txt index 3069221..f25b11e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11538,6 +11538,7 @@ package android.media { method public abstract void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo); method public abstract void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup); method public abstract void onRouteUnselected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo); + method public abstract void onRouteVolumeChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo); } public static class MediaRouter.RouteCategory { @@ -11573,6 +11574,8 @@ package android.media { method public int getVolume(); method public int getVolumeHandling(); method public int getVolumeMax(); + method public void requestSetVolume(int); + method public void requestUpdateVolume(int); method public void setTag(java.lang.Object); field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0 field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1 @@ -11589,6 +11592,7 @@ package android.media { method public void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo); method public void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup); method public void onRouteUnselected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo); + method public void onRouteVolumeChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo); } public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo { diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java index b615b9c..b747694 100644 --- a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java +++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java @@ -30,8 +30,6 @@ import android.media.MediaRouter; import android.media.MediaRouter.RouteCategory; import android.media.MediaRouter.RouteGroup; import android.media.MediaRouter.RouteInfo; -import android.media.MediaRouter.UserRouteInfo; -import android.media.RemoteControlClient; import android.os.Bundle; import android.text.TextUtils; import android.view.KeyEvent; @@ -85,7 +83,8 @@ public class MediaRouteChooserDialogFragment extends DialogFragment { final RouteComparator mComparator = new RouteComparator(); final MediaRouterCallback mCallback = new MediaRouterCallback(); - private boolean mIgnoreVolumeChanges; + private boolean mIgnoreSliderVolumeChanges; + private boolean mIgnoreCallbackVolumeChanges; public MediaRouteChooserDialogFragment() { setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog); @@ -126,52 +125,34 @@ public class MediaRouteChooserDialogFragment extends DialogFragment { void updateVolume() { final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - final boolean defaultAudioSelected = selectedRoute == mRouter.getSystemAudioRoute(); - final boolean selectedSystemRoute = - selectedRoute.getCategory() == mRouter.getSystemAudioCategory(); - mVolumeIcon.setImageResource(defaultAudioSelected ? + mVolumeIcon.setImageResource( + selectedRoute.getPlaybackType() == RouteInfo.PLAYBACK_TYPE_LOCAL ? R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark); - mIgnoreVolumeChanges = true; - mVolumeSlider.setEnabled(true); - if (selectedSystemRoute) { - // Use the standard media audio stream - mVolumeSlider.setMax(mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)); - mVolumeSlider.setProgress(mAudio.getStreamVolume(AudioManager.STREAM_MUSIC)); - } else { - final RouteInfo firstSelected; - if (selectedRoute instanceof RouteGroup) { - firstSelected = ((RouteGroup) selectedRoute).getRouteAt(0); - } else { - firstSelected = selectedRoute; - } + mIgnoreSliderVolumeChanges = true; - RemoteControlClient rcc = null; - if (firstSelected instanceof UserRouteInfo) { - rcc = ((UserRouteInfo) firstSelected).getRemoteControlClient(); - } - - if (rcc == null) { - // No RemoteControlClient? Assume volume can't be controlled. - // Disable the slider and show it at max volume. - mVolumeSlider.setMax(1); - mVolumeSlider.setProgress(1); - mVolumeSlider.setEnabled(false); - } else { - // TODO: Connect this to the remote control volume - } + if (selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_FIXED) { + // Disable the slider and show it at max volume. + mVolumeSlider.setMax(1); + mVolumeSlider.setProgress(1); + mVolumeSlider.setEnabled(false); + } else { + mVolumeSlider.setEnabled(true); + mVolumeSlider.setMax(selectedRoute.getVolumeMax()); + mVolumeSlider.setProgress(selectedRoute.getVolume()); } - mIgnoreVolumeChanges = false; + + mIgnoreSliderVolumeChanges = false; } void changeVolume(int newValue) { - if (mIgnoreVolumeChanges) return; + if (mIgnoreSliderVolumeChanges) return; - RouteCategory selectedCategory = mRouter.getSelectedRoute(mRouteTypes).getCategory(); - if (selectedCategory == mRouter.getSystemAudioCategory()) { + final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); + if (selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_VARIABLE) { final int maxVolume = mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC); newValue = Math.max(0, Math.min(newValue, maxVolume)); - mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, newValue, 0); + selectedRoute.requestSetVolume(newValue); } } @@ -595,7 +576,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment { @Override public void onRouteAdded(MediaRouter router, RouteInfo info) { mAdapter.update(); - updateVolume(); } @Override @@ -604,7 +584,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment { mAdapter.finishGrouping(); } mAdapter.update(); - updateVolume(); } @Override @@ -622,6 +601,13 @@ public class MediaRouteChooserDialogFragment extends DialogFragment { public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { mAdapter.update(); } + + @Override + public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) { + if (!mIgnoreCallbackVolumeChanges) { + updateVolume(); + } + } } class RouteComparator implements Comparator<RouteInfo> { @@ -648,15 +634,25 @@ public class MediaRouteChooserDialogFragment extends DialogFragment { public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) { - mVolumeSlider.incrementProgressBy(-1); + mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(-1); return true; } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) { - mVolumeSlider.incrementProgressBy(1); + mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(1); return true; } else { return super.onKeyDown(keyCode, event); } } + + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) { + return true; + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) { + return true; + } else { + return super.onKeyUp(keyCode, event); + } + } } /** @@ -675,10 +671,13 @@ public class MediaRouteChooserDialogFragment extends DialogFragment { @Override public void onStartTrackingTouch(SeekBar seekBar) { + mIgnoreCallbackVolumeChanges = true; } @Override public void onStopTrackingTouch(SeekBar seekBar) { + mIgnoreCallbackVolumeChanges = false; + updateVolume(); } } diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index b6187da..3de2db2 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -16,7 +16,10 @@ package android.media; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -87,12 +90,15 @@ public class MediaRouter { } // Called after sStatic is initialized - void startMonitoringRoutes() { + void startMonitoringRoutes(Context appContext) { mDefaultAudio = new RouteInfo(mSystemCategory); mDefaultAudio.mNameResId = com.android.internal.R.string.default_audio_route_name; mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO; addRoute(mDefaultAudio); + appContext.registerReceiver(new VolumeChangeReceiver(), + new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION)); + AudioRoutesInfo newRoutes = null; try { newRoutes = mAudioService.startWatchingRoutes(mRoutesObserver); @@ -190,8 +196,9 @@ public class MediaRouter { public MediaRouter(Context context) { synchronized (Static.class) { if (sStatic == null) { - sStatic = new Static(context.getApplicationContext()); - sStatic.startMonitoringRoutes(); + final Context appContext = context.getApplicationContext(); + sStatic = new Static(appContext); + sStatic.startMonitoringRoutes(appContext); } } } @@ -578,6 +585,33 @@ public class MediaRouter { } } + static void dispatchRouteVolumeChanged(RouteInfo info) { + for (CallbackInfo cbi : sStatic.mCallbacks) { + if ((cbi.type & info.mSupportedTypes) != 0) { + cbi.cb.onRouteVolumeChanged(cbi.router, info); + } + } + } + + static void systemVolumeChanged(int newValue) { + final RouteInfo selectedRoute = sStatic.mSelectedRoute; + if (selectedRoute == null) return; + + if (selectedRoute == sStatic.mBluetoothA2dpRoute || + selectedRoute == sStatic.mDefaultAudio) { + dispatchRouteVolumeChanged(selectedRoute); + } else if (sStatic.mBluetoothA2dpRoute != null) { + try { + dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ? + sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudio); + } catch (RemoteException e) { + Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e); + } + } else { + dispatchRouteVolumeChanged(sStatic.mDefaultAudio); + } + } + /** * Information about a media route. */ @@ -735,6 +769,9 @@ public class MediaRouter { } /** + * Return the current volume for this route. Depending on the route, this may only + * be valid if the route is currently selected. + * * @return the volume at which the playback associated with this route is performed * @see UserRouteInfo#setVolume(int) */ @@ -753,6 +790,44 @@ public class MediaRouter { } /** + * Request a volume change for this route. + * @param volume value between 0 and getVolumeMax + */ + public void requestSetVolume(int volume) { + if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { + try { + sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0); + } catch (RemoteException e) { + Log.e(TAG, "Error setting local stream volume", e); + } + } else { + Log.e(TAG, getClass().getSimpleName() + ".requestSetVolume(): " + + "Non-local volume playback on system route? " + + "Could not request volume change."); + } + } + + /** + * Request an incremental volume update for this route. + * @param direction Delta to apply to the current volume + */ + public void requestUpdateVolume(int direction) { + if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { + try { + final int volume = + Math.max(0, Math.min(getVolume() + direction, getVolumeMax())); + sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0); + } catch (RemoteException e) { + Log.e(TAG, "Error setting local stream volume", e); + } + } else { + Log.e(TAG, getClass().getSimpleName() + ".requestChangeVolume(): " + + "Non-local volume playback on system route? " + + "Could not request volume change."); + } + } + + /** * @return the maximum volume at which the playback associated with this route is performed * @see UserRouteInfo#setVolumeMax(int) */ @@ -821,6 +896,8 @@ public class MediaRouter { /** * Information about a route that the application may define and modify. + * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and + * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}. * * @see MediaRouter.RouteInfo */ @@ -830,6 +907,8 @@ public class MediaRouter { UserRouteInfo(RouteCategory category) { super(category); mSupportedTypes = ROUTE_TYPE_USER; + mPlaybackType = PLAYBACK_TYPE_REMOTE; + mVolumeHandling = PLAYBACK_VOLUME_FIXED; } /** @@ -949,9 +1028,33 @@ public class MediaRouter { * @param volume */ public void setVolume(int volume) { + volume = Math.max(0, Math.min(volume, getVolumeMax())); if (mVolume != volume) { mVolume = volume; setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); + dispatchRouteVolumeChanged(this); + } + } + + @Override + public void requestSetVolume(int volume) { + if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) { + if (mVcb == null) { + Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set"); + return; + } + mVcb.vcb.onVolumeSetRequest(this, volume); + } + } + + @Override + public void requestUpdateVolume(int direction) { + if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) { + if (mVcb == null) { + Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set"); + return; + } + mVcb.vcb.onVolumeUpdateRequest(this, direction); } } @@ -1018,6 +1121,7 @@ public class MediaRouter { RouteGroup(RouteCategory category) { super(category); mGroup = this; + mVolumeHandling = PLAYBACK_VOLUME_FIXED; } CharSequence getName(Resources res) { @@ -1138,6 +1242,45 @@ public class MediaRouter { setIconDrawable(sStatic.mResources.getDrawable(resId)); } + @Override + public void requestSetVolume(int volume) { + final int maxVol = getVolumeMax(); + if (maxVol == 0) { + return; + } + + final float scaledVolume = (float) volume / maxVol; + final int routeCount = getRouteCount(); + for (int i = 0; i < routeCount; i++) { + final RouteInfo route = getRouteAt(i); + final int routeVol = (int) (scaledVolume * route.getVolumeMax()); + route.requestSetVolume(routeVol); + } + if (volume != mVolume) { + mVolume = volume; + dispatchRouteVolumeChanged(this); + } + } + + @Override + public void requestUpdateVolume(int direction) { + final int maxVol = getVolumeMax(); + if (maxVol == 0) { + return; + } + + final int routeCount = getRouteCount(); + for (int i = 0; i < routeCount; i++) { + final RouteInfo route = getRouteAt(i); + route.requestUpdateVolume(direction); + } + final int volume = Math.max(0, Math.min(mVolume + direction, maxVol)); + if (volume != mVolume) { + mVolume = volume; + dispatchRouteVolumeChanged(this); + } + } + void memberNameChanged(RouteInfo info, CharSequence name) { mUpdateName = true; routeUpdated(); @@ -1157,10 +1300,23 @@ public class MediaRouter { return; } + int maxVolume = 0; + boolean isLocal = true; + boolean isFixedVolume = true; for (int i = 0; i < count; i++) { - types |= mRoutes.get(i).mSupportedTypes; + final RouteInfo route = mRoutes.get(i); + types |= route.mSupportedTypes; + final int routeMaxVolume = route.getVolumeMax(); + if (routeMaxVolume > maxVolume) { + maxVolume = routeMaxVolume; + } + isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL; + isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED; } + mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE; + mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE; mSupportedTypes = types; + mVolumeMax = maxVolume; mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null; super.routeUpdated(); } @@ -1381,6 +1537,14 @@ public class MediaRouter { * @param group The group the route was removed from */ public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group); + + /** + * Called when a route's volume changes. + * + * @param router the MediaRouter reporting the event + * @param info The route with altered volume + */ + public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info); } /** @@ -1419,6 +1583,9 @@ public class MediaRouter { public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { } + @Override + public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) { + } } static class VolumeCallbackInfo { @@ -1459,4 +1626,25 @@ public class MediaRouter { public abstract void onVolumeSetRequest(RouteInfo info, int volume); } + static class VolumeChangeReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) { + final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, + -1); + if (streamType != AudioManager.STREAM_MUSIC) { + return; + } + + final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); + final int oldVolume = intent.getIntExtra( + AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0); + if (newVolume != oldVolume) { + systemVolumeChanged(newVolume); + } + } + } + + } } |