Skip to content

Commit 4599696

Browse files
committed
Volume control in MediaRouter dialogs
MediaRouter dialogs now intercept the volume keys for altering the current volume. The status icon indicates if the slider/buttons are currently controlling the local device volume or a remote device's volume. Group volume for user routes is handled by using the RemoteControlClient supplied by the first route in the group. Change-Id: I40a0d054847ed5acce7a4c3b669487841b4dca15
1 parent 39d5c61 commit 4599696

File tree

4 files changed

+162
-47
lines changed

4 files changed

+162
-47
lines changed

api/current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11582,6 +11582,7 @@ package android.media {
1158211582
}
1158311583

1158411584
public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
11585+
method public android.media.RemoteControlClient getRemoteControlClient();
1158511586
method public void setIconDrawable(android.graphics.drawable.Drawable);
1158611587
method public void setIconResource(int);
1158711588
method public void setName(java.lang.CharSequence);

core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java

Lines changed: 141 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@
2525
import android.app.MediaRouteButton;
2626
import android.content.Context;
2727
import android.graphics.drawable.Drawable;
28+
import android.media.AudioManager;
2829
import android.media.MediaRouter;
2930
import android.media.MediaRouter.RouteCategory;
3031
import android.media.MediaRouter.RouteGroup;
3132
import android.media.MediaRouter.RouteInfo;
33+
import android.media.MediaRouter.UserRouteInfo;
34+
import android.media.RemoteControlClient;
3235
import android.os.Bundle;
3336
import android.text.TextUtils;
37+
import android.view.KeyEvent;
3438
import android.view.LayoutInflater;
3539
import android.view.View;
3640
import android.view.ViewGroup;
@@ -41,6 +45,7 @@
4145
import android.widget.ImageButton;
4246
import android.widget.ImageView;
4347
import android.widget.ListView;
48+
import android.widget.SeekBar;
4449
import android.widget.TextView;
4550

4651
import java.util.ArrayList;
@@ -67,15 +72,20 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
6772
};
6873

6974
MediaRouter mRouter;
75+
AudioManager mAudio;
7076
private int mRouteTypes;
7177

7278
private LayoutInflater mInflater;
7379
private LauncherListener mLauncherListener;
7480
private View.OnClickListener mExtendedSettingsListener;
7581
private RouteAdapter mAdapter;
7682
private ListView mListView;
83+
private SeekBar mVolumeSlider;
84+
private ImageView mVolumeIcon;
7785

7886
final RouteComparator mComparator = new RouteComparator();
87+
final MediaRouterCallback mCallback = new MediaRouterCallback();
88+
private boolean mIgnoreVolumeChanges;
7989

8090
public MediaRouteChooserDialogFragment() {
8191
setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
@@ -89,6 +99,7 @@ public void setLauncherListener(LauncherListener listener) {
8999
public void onAttach(Activity activity) {
90100
super.onAttach(activity);
91101
mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
102+
mAudio = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
92103
}
93104

94105
@Override
@@ -98,20 +109,13 @@ public void onDetach() {
98109
mLauncherListener.onDetached(this);
99110
}
100111
if (mAdapter != null) {
101-
mRouter.removeCallback(mAdapter.mCallback);
102112
mAdapter = null;
103113
}
104114
mInflater = null;
115+
mRouter.removeCallback(mCallback);
105116
mRouter = null;
106117
}
107118

108-
/**
109-
* Implemented by the MediaRouteButton that launched this dialog
110-
*/
111-
public interface LauncherListener {
112-
public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
113-
}
114-
115119
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
116120
mExtendedSettingsListener = listener;
117121
}
@@ -120,14 +124,70 @@ public void setRouteTypes(int types) {
120124
mRouteTypes = types;
121125
}
122126

127+
void updateVolume() {
128+
final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
129+
final boolean defaultAudioSelected = selectedRoute == mRouter.getSystemAudioRoute();
130+
final boolean selectedSystemRoute =
131+
selectedRoute.getCategory() == mRouter.getSystemAudioCategory();
132+
mVolumeIcon.setImageResource(defaultAudioSelected ?
133+
R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark);
134+
135+
mIgnoreVolumeChanges = true;
136+
mVolumeSlider.setEnabled(true);
137+
if (selectedSystemRoute) {
138+
// Use the standard media audio stream
139+
mVolumeSlider.setMax(mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC));
140+
mVolumeSlider.setProgress(mAudio.getStreamVolume(AudioManager.STREAM_MUSIC));
141+
} else {
142+
final RouteInfo firstSelected;
143+
if (selectedRoute instanceof RouteGroup) {
144+
firstSelected = ((RouteGroup) selectedRoute).getRouteAt(0);
145+
} else {
146+
firstSelected = selectedRoute;
147+
}
148+
149+
RemoteControlClient rcc = null;
150+
if (firstSelected instanceof UserRouteInfo) {
151+
rcc = ((UserRouteInfo) firstSelected).getRemoteControlClient();
152+
}
153+
154+
if (rcc == null) {
155+
// No RemoteControlClient? Assume volume can't be controlled.
156+
// Disable the slider and show it at max volume.
157+
mVolumeSlider.setMax(1);
158+
mVolumeSlider.setProgress(1);
159+
mVolumeSlider.setEnabled(false);
160+
} else {
161+
// TODO: Connect this to the remote control volume
162+
}
163+
}
164+
mIgnoreVolumeChanges = false;
165+
}
166+
167+
void changeVolume(int newValue) {
168+
if (mIgnoreVolumeChanges) return;
169+
170+
RouteCategory selectedCategory = mRouter.getSelectedRoute(mRouteTypes).getCategory();
171+
if (selectedCategory == mRouter.getSystemAudioCategory()) {
172+
final int maxVolume = mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
173+
newValue = Math.max(0, Math.min(newValue, maxVolume));
174+
mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, newValue, 0);
175+
}
176+
}
177+
123178
@Override
124179
public View onCreateView(LayoutInflater inflater, ViewGroup container,
125180
Bundle savedInstanceState) {
126181
mInflater = inflater;
127182
final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
128-
final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
183+
184+
mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon);
185+
mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider);
186+
updateVolume();
187+
mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener());
129188

130189
if (mExtendedSettingsListener != null) {
190+
final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
131191
extendedSettingsButton.setVisibility(View.VISIBLE);
132192
extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
133193
}
@@ -138,7 +198,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
138198
list.setOnItemClickListener(mAdapter);
139199

140200
mListView = list;
141-
mRouter.addCallback(mRouteTypes, mAdapter.mCallback);
201+
mRouter.addCallback(mRouteTypes, mCallback);
142202

143203
mAdapter.scrollToSelectedItem();
144204

@@ -174,7 +234,6 @@ private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickLi
174234

175235
private int mSelectedItemPosition = -1;
176236
private final ArrayList<Object> mItems = new ArrayList<Object>();
177-
final MediaRouterCallback mCallback = new MediaRouterCallback();
178237

179238
private RouteCategory mCategoryEditingGroups;
180239
private RouteGroup mEditingGroup;
@@ -443,10 +502,6 @@ void bindHeaderView(int position, ViewHolder holder) {
443502
holder.text1.setText(cat.getName(getActivity()));
444503
}
445504

446-
public int getSelectedRoutePosition() {
447-
return mSelectedItemPosition;
448-
}
449-
450505
@Override
451506
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
452507
final int type = getItemViewType(position);
@@ -523,46 +578,49 @@ public void onClick(View v) {
523578
scrollToEditingGroup();
524579
}
525580
}
581+
}
526582

527-
class MediaRouterCallback extends MediaRouter.Callback {
528-
@Override
529-
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
530-
update();
531-
}
583+
class MediaRouterCallback extends MediaRouter.Callback {
584+
@Override
585+
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
586+
mAdapter.update();
587+
updateVolume();
588+
}
532589

533-
@Override
534-
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
535-
update();
536-
}
590+
@Override
591+
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
592+
mAdapter.update();
593+
}
537594

538-
@Override
539-
public void onRouteAdded(MediaRouter router, RouteInfo info) {
540-
update();
541-
}
595+
@Override
596+
public void onRouteAdded(MediaRouter router, RouteInfo info) {
597+
mAdapter.update();
598+
updateVolume();
599+
}
542600

543-
@Override
544-
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
545-
if (info == mEditingGroup) {
546-
finishGrouping();
547-
}
548-
update();
601+
@Override
602+
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
603+
if (info == mAdapter.mEditingGroup) {
604+
mAdapter.finishGrouping();
549605
}
606+
mAdapter.update();
607+
updateVolume();
608+
}
550609

551-
@Override
552-
public void onRouteChanged(MediaRouter router, RouteInfo info) {
553-
notifyDataSetChanged();
554-
}
610+
@Override
611+
public void onRouteChanged(MediaRouter router, RouteInfo info) {
612+
mAdapter.notifyDataSetChanged();
613+
}
555614

556-
@Override
557-
public void onRouteGrouped(MediaRouter router, RouteInfo info,
558-
RouteGroup group, int index) {
559-
update();
560-
}
615+
@Override
616+
public void onRouteGrouped(MediaRouter router, RouteInfo info,
617+
RouteGroup group, int index) {
618+
mAdapter.update();
619+
}
561620

562-
@Override
563-
public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
564-
update();
565-
}
621+
@Override
622+
public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
623+
mAdapter.update();
566624
}
567625
}
568626

@@ -587,5 +645,41 @@ public void onBackPressed() {
587645
super.onBackPressed();
588646
}
589647
}
648+
649+
public boolean onKeyDown(int keyCode, KeyEvent event) {
650+
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
651+
mVolumeSlider.incrementProgressBy(-1);
652+
return true;
653+
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
654+
mVolumeSlider.incrementProgressBy(1);
655+
return true;
656+
} else {
657+
return super.onKeyDown(keyCode, event);
658+
}
659+
}
660+
}
661+
662+
/**
663+
* Implemented by the MediaRouteButton that launched this dialog
664+
*/
665+
public interface LauncherListener {
666+
public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
667+
}
668+
669+
class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {
670+
671+
@Override
672+
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
673+
changeVolume(progress);
674+
}
675+
676+
@Override
677+
public void onStartTrackingTouch(SeekBar seekBar) {
678+
}
679+
680+
@Override
681+
public void onStopTrackingTouch(SeekBar seekBar) {
682+
}
683+
590684
}
591685
}

core/res/res/values/public.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,9 @@
11681168
<java-symbol type="attr" name="externalRouteEnabledDrawable" />
11691169
<java-symbol type="id" name="extended_settings" />
11701170
<java-symbol type="id" name="check" />
1171+
<java-symbol type="id" name="volume_slider" />
1172+
<java-symbol type="id" name="volume_icon" />
1173+
<java-symbol type="drawable" name="ic_media_route_on_holo_dark" />
11711174
<java-symbol type="layout" name="media_route_chooser_layout" />
11721175
<java-symbol type="layout" name="media_route_list_item_top_header" />
11731176
<java-symbol type="layout" name="media_route_list_item_section_header" />

media/java/android/media/MediaRouter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ public RouteInfo getSystemAudioRoute() {
197197
return sStatic.mDefaultAudio;
198198
}
199199

200+
/**
201+
* @hide for use by framework routing UI
202+
*/
203+
public RouteCategory getSystemAudioCategory() {
204+
return sStatic.mSystemCategory;
205+
}
206+
200207
/**
201208
* Return the currently selected route for the given types
202209
*
@@ -739,6 +746,16 @@ public void setRemoteControlClient(RemoteControlClient rcc) {
739746
mRcc = rcc;
740747
}
741748

749+
/**
750+
* Retrieve the RemoteControlClient associated with this route, if one has been set.
751+
*
752+
* @return the RemoteControlClient associated with this route
753+
* @see #setRemoteControlClient(RemoteControlClient)
754+
*/
755+
public RemoteControlClient getRemoteControlClient() {
756+
return mRcc;
757+
}
758+
742759
/**
743760
* Set an icon that will be used to represent this route.
744761
* The system may use this icon in picker UIs or similar.

0 commit comments

Comments
 (0)