Merge "Expand volume APIs for MediaRouter" into jb-dev
This commit is contained in:
@ -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 {
|
||||
|
@ -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));
|
||||
mIgnoreSliderVolumeChanges = true;
|
||||
|
||||
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 {
|
||||
final RouteInfo firstSelected;
|
||||
if (selectedRoute instanceof RouteGroup) {
|
||||
firstSelected = ((RouteGroup) selectedRoute).getRouteAt(0);
|
||||
} else {
|
||||
firstSelected = selectedRoute;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
*/
|
||||
@ -752,6 +789,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user