Merge "Add wireless display selection support to MediaRouter." into jb-mr1-dev

This commit is contained in:
Adam Powell
2012-09-28 12:07:50 -07:00
committed by Android (Google) Code Review
7 changed files with 351 additions and 50 deletions

View File

@@ -11750,6 +11750,7 @@ package android.media {
method public void removeUserRoute(android.media.MediaRouter.UserRouteInfo);
method public void selectRoute(int, android.media.MediaRouter.RouteInfo);
field public static final int ROUTE_TYPE_LIVE_AUDIO = 1; // 0x1
field public static final int ROUTE_TYPE_LIVE_VIDEO = 2; // 0x2
field public static final int ROUTE_TYPE_USER = 8388608; // 0x800000
}
@@ -11798,6 +11799,7 @@ package android.media {
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
method public boolean isEnabled();
method public void requestSetVolume(int);
method public void requestUpdateVolume(int);
method public void setTag(java.lang.Object);

View File

@@ -221,21 +221,28 @@ public class MediaRouteButton extends View {
void updateRouteCount() {
final int N = mRouter.getRouteCount();
int count = 0;
boolean hasVideoRoutes = false;
for (int i = 0; i < N; i++) {
final RouteInfo route = mRouter.getRouteAt(i);
if ((route.getSupportedTypes() & mRouteTypes) != 0) {
final int routeTypes = route.getSupportedTypes();
if ((routeTypes & mRouteTypes) != 0) {
if (route instanceof RouteGroup) {
count += ((RouteGroup) route).getRouteCount();
} else {
count++;
}
if ((routeTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0) {
hasVideoRoutes = true;
}
}
}
setEnabled(count != 0);
// Only allow toggling if we have more than just user routes
mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0;
// Only allow toggling if we have more than just user routes.
// Don't toggle if we support video routes, we may have to let the dialog scan.
mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0 &&
!hasVideoRoutes;
}
@Override

View File

@@ -25,7 +25,7 @@ import android.app.MediaRouteActionProvider;
import android.app.MediaRouteButton;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteCategory;
import android.media.MediaRouter.RouteGroup;
@@ -70,6 +70,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
};
MediaRouter mRouter;
DisplayManager mDisplayService;
private int mRouteTypes;
private LayoutInflater mInflater;
@@ -97,6 +98,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
public void onAttach(Activity activity) {
super.onAttach(activity);
mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mDisplayService = (DisplayManager) activity.getSystemService(Context.DISPLAY_SERVICE);
}
@Override
@@ -119,6 +121,15 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
public void setRouteTypes(int types) {
mRouteTypes = types;
if ((mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0 && mDisplayService == null) {
final Context activity = getActivity();
if (activity != null) {
mDisplayService = (DisplayManager) activity.getSystemService(
Context.DISPLAY_SERVICE);
}
} else {
mDisplayService = null;
}
}
void updateVolume() {
@@ -194,6 +205,9 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
@Override
public void onResume() {
super.onResume();
if (mDisplayService != null) {
mDisplayService.scanWifiDisplays();
}
}
private static class ViewHolder {
@@ -253,7 +267,9 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
final RouteCategory cat = mRouter.getCategoryAt(i);
routes = cat.getRoutes(mCatRouteList);
mItems.add(cat);
if (!cat.isSystem()) {
mItems.add(cat);
}
if (cat == mCategoryEditingGroups) {
addGroupEditingCategoryRoutes(routes);
@@ -370,6 +386,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
public boolean isEnabled(int position) {
switch (getItemViewType(position)) {
case VIEW_ROUTE:
return ((RouteInfo) mItems.get(position)).isEnabled();
case VIEW_GROUPING_ROUTE:
case VIEW_GROUPING_DONE:
return true;
@@ -434,6 +451,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
}
convertView.setActivated(position == mSelectedItemPosition);
convertView.setEnabled(isEnabled(position));
return convertView;
}

View File

@@ -24,7 +24,8 @@
android:layout_height="56dp"
android:scaleType="center"
android:id="@+id/icon"
android:visibility="gone" />
android:visibility="gone"
android:duplicateParentState="true" />
<LinearLayout android:layout_width="0dp"
android:layout_height="match_parent"
@@ -32,21 +33,24 @@
android:orientation="vertical"
android:gravity="start|center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:duplicateParentState="true">
<TextView android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceMedium" />
android:textAppearance="?android:attr/textAppearanceMedium"
android:duplicateParentState="true" />
<TextView android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall" />
android:textAppearance="?android:attr/textAppearanceSmall"
android:duplicateParentState="true" />
</LinearLayout>
<ImageButton
@@ -56,6 +60,7 @@
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_media_group_expand"
android:scaleType="center"
android:visibility="gone" />
android:visibility="gone"
android:duplicateParentState="true" />
</LinearLayout>

View File

@@ -3783,6 +3783,18 @@
<!-- Content description of a MediaRouteButton for accessibility support -->
<string name="media_route_button_content_description">Media output</string>
<!-- Status message for remote routes attempting to scan/determine availability -->
<string name="media_route_status_scanning">Scanning...</string>
<!-- Status message for a remote route attempting to connect -->
<string name="media_route_status_connecting">Connecting...</string>
<!-- Status message for a remote route that is confirmed to be available for connection -->
<string name="media_route_status_available">Available</string>
<!-- Status message for remote routes that are not available for connection right now -->
<string name="media_route_status_not_available">Not available</string>
<!-- Display manager service -->
<!-- Name of the built-in display. [CHAR LIMIT=50] -->

View File

@@ -826,6 +826,10 @@
<java-symbol type="string" name="default_audio_route_name_hdmi" />
<java-symbol type="string" name="default_audio_route_category_name" />
<java-symbol type="string" name="safe_media_volume_warning" />
<java-symbol type="string" name="media_route_status_scanning" />
<java-symbol type="string" name="media_route_status_connecting" />
<java-symbol type="string" name="media_route_status_available" />
<java-symbol type="string" name="media_route_status_not_available" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />

View File

@@ -22,12 +22,17 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
import java.util.ArrayList;
import java.util.HashMap;
@@ -51,6 +56,7 @@ public class MediaRouter {
static class Static {
final Resources mResources;
final IAudioService mAudioService;
final DisplayManager mDisplayService;
final Handler mHandler;
final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
new CopyOnWriteArrayList<CallbackInfo>();
@@ -60,18 +66,20 @@ public class MediaRouter {
final RouteCategory mSystemCategory;
final AudioRoutesInfo mCurRoutesInfo = new AudioRoutesInfo();
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
RouteInfo mDefaultAudio;
RouteInfo mDefaultAudioVideo;
RouteInfo mBluetoothA2dpRoute;
RouteInfo mSelectedRoute;
final IAudioRoutesObserver.Stub mRoutesObserver = new IAudioRoutesObserver.Stub() {
WifiDisplayStatus mLastKnownWifiDisplayStatus;
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
mHandler.post(new Runnable() {
@Override public void run() {
updateRoutes(newRoutes);
updateAudioRoutes(newRoutes);
}
});
}
@@ -84,34 +92,42 @@ public class MediaRouter {
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
mAudioService = IAudioService.Stub.asInterface(b);
mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
mSystemCategory = new RouteCategory(
com.android.internal.R.string.default_audio_route_category_name,
ROUTE_TYPE_LIVE_AUDIO, false);
ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
mSystemCategory.mIsSystem = true;
}
// Called after sStatic is initialized
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);
mDefaultAudioVideo = new RouteInfo(mSystemCategory);
mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
addRoute(mDefaultAudioVideo);
appContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
AudioRoutesInfo newRoutes = null;
AudioRoutesInfo newAudioRoutes = null;
try {
newRoutes = mAudioService.startWatchingRoutes(mRoutesObserver);
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
} catch (RemoteException e) {
}
if (newRoutes != null) {
updateRoutes(newRoutes);
if (newAudioRoutes != null) {
updateAudioRoutes(newAudioRoutes);
}
updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),
new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
}
void updateRoutes(AudioRoutesInfo newRoutes) {
if (newRoutes.mMainType != mCurRoutesInfo.mMainType) {
mCurRoutesInfo.mMainType = newRoutes.mMainType;
void updateAudioRoutes(AudioRoutesInfo newRoutes) {
if (newRoutes.mMainType != mCurAudioRoutesInfo.mMainType) {
mCurAudioRoutesInfo.mMainType = newRoutes.mMainType;
int name;
if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
|| (newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
@@ -123,8 +139,8 @@ public class MediaRouter {
} else {
name = com.android.internal.R.string.default_audio_route_name;
}
sStatic.mDefaultAudio.mNameResId = name;
dispatchRouteChanged(sStatic.mDefaultAudio);
sStatic.mDefaultAudioVideo.mNameResId = name;
dispatchRouteChanged(sStatic.mDefaultAudioVideo);
}
boolean a2dpEnabled;
@@ -135,17 +151,17 @@ public class MediaRouter {
a2dpEnabled = false;
}
if (!TextUtils.equals(newRoutes.mBluetoothName, mCurRoutesInfo.mBluetoothName)) {
mCurRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
if (mCurRoutesInfo.mBluetoothName != null) {
if (!TextUtils.equals(newRoutes.mBluetoothName, mCurAudioRoutesInfo.mBluetoothName)) {
mCurAudioRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
if (mCurAudioRoutesInfo.mBluetoothName != null) {
if (sStatic.mBluetoothA2dpRoute == null) {
final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
info.mName = mCurRoutesInfo.mBluetoothName;
info.mName = mCurAudioRoutesInfo.mBluetoothName;
info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
sStatic.mBluetoothA2dpRoute = info;
addRoute(sStatic.mBluetoothA2dpRoute);
} else {
sStatic.mBluetoothA2dpRoute.mName = mCurRoutesInfo.mBluetoothName;
sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.mBluetoothName;
dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
}
} else if (sStatic.mBluetoothA2dpRoute != null) {
@@ -155,11 +171,11 @@ public class MediaRouter {
}
if (mBluetoothA2dpRoute != null) {
if (mCurRoutesInfo.mMainType != AudioRoutesInfo.MAIN_SPEAKER &&
if (mCurAudioRoutesInfo.mMainType != AudioRoutesInfo.MAIN_SPEAKER &&
mSelectedRoute == mBluetoothA2dpRoute) {
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudio);
} else if (mCurRoutesInfo.mMainType == AudioRoutesInfo.MAIN_SPEAKER &&
mSelectedRoute == mDefaultAudio && a2dpEnabled) {
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo);
} else if (mCurAudioRoutesInfo.mMainType == AudioRoutesInfo.MAIN_SPEAKER &&
mSelectedRoute == mDefaultAudioVideo && a2dpEnabled) {
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
}
}
@@ -180,6 +196,20 @@ public class MediaRouter {
*/
public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
/**
* Route type flag for live video.
*
* <p>A device that supports live video routing will allow a mirrored version
* of the device's primary display or a customized
* {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
*
* <p>Once initiated, display mirroring is transparent to the application.
* While remote routing is active the application may use a
* {@link android.app.Presentation Presentation} to replace the mirrored view
* on the external display with different content.</p>
*/
public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
/**
* Route type flag for application-specific usage.
*
@@ -219,7 +249,7 @@ public class MediaRouter {
* @hide for use by framework routing UI
*/
public RouteInfo getSystemAudioRoute() {
return sStatic.mDefaultAudio;
return sStatic.mDefaultAudioVideo;
}
/**
@@ -296,7 +326,8 @@ public class MediaRouter {
}
static void selectRouteStatic(int types, RouteInfo route) {
if (sStatic.mSelectedRoute == route) return;
final RouteInfo oldRoute = sStatic.mSelectedRoute;
if (oldRoute == route) return;
if ((route.getSupportedTypes() & types) == 0) {
Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
typesToString(route.getSupportedTypes()) + " into route types " +
@@ -306,7 +337,7 @@ public class MediaRouter {
final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
(route == btRoute || route == sStatic.mDefaultAudio)) {
(route == btRoute || route == sStatic.mDefaultAudioVideo)) {
try {
sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
} catch (RemoteException e) {
@@ -314,10 +345,21 @@ public class MediaRouter {
}
}
if (sStatic.mSelectedRoute != null) {
final WifiDisplay activeDisplay =
sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
final boolean newRouteHasAddress = route != null && route.mDeviceAddress != null;
if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
} else if (activeDisplay != null && !newRouteHasAddress) {
sStatic.mDisplayService.disconnectWifiDisplay();
}
}
if (oldRoute != null) {
// TODO filter types properly
dispatchRouteUnselected(types & sStatic.mSelectedRoute.getSupportedTypes(),
sStatic.mSelectedRoute);
dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
}
sStatic.mSelectedRoute = route;
if (route != null) {
@@ -326,6 +368,22 @@ public class MediaRouter {
}
}
/**
* Compare the device address of a display and a route.
* Nulls/no device address will match another null/no address.
*/
static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
if (display == null && !routeHasAddress) {
return true;
}
if (display != null && routeHasAddress) {
return display.getDeviceAddress().equals(info.mDeviceAddress);
}
return false;
}
/**
* Add an app-specified route for media to the MediaRouter.
* App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
@@ -419,7 +477,7 @@ public class MediaRouter {
if (info == sStatic.mSelectedRoute) {
// Removing the currently selected route? Select the default before we remove it.
// TODO: Be smarter about the route types here; this selects for all valid.
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudioVideo);
}
if (!found) {
sStatic.mCategories.remove(removingCat);
@@ -444,7 +502,8 @@ public class MediaRouter {
if (info == sStatic.mSelectedRoute) {
// Removing the currently selected route? Select the default before we remove it.
// TODO: Be smarter about the route types here; this selects for all valid.
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO | ROUTE_TYPE_USER,
sStatic.mDefaultAudioVideo);
}
if (!found) {
sStatic.mCategories.remove(removingCat);
@@ -611,20 +670,151 @@ public class MediaRouter {
if (selectedRoute == null) return;
if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
selectedRoute == sStatic.mDefaultAudio) {
selectedRoute == sStatic.mDefaultAudioVideo) {
dispatchRouteVolumeChanged(selectedRoute);
} else if (sStatic.mBluetoothA2dpRoute != null) {
try {
dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudio);
sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
} catch (RemoteException e) {
Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
}
} else {
dispatchRouteVolumeChanged(sStatic.mDefaultAudio);
dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
}
}
static void updateWifiDisplayStatus(WifiDisplayStatus newStatus) {
final WifiDisplayStatus oldStatus = sStatic.mLastKnownWifiDisplayStatus;
// TODO Naive implementation. Make this smarter later.
boolean needScan = false;
WifiDisplay[] oldDisplays = oldStatus != null ?
oldStatus.getRememberedDisplays() : new WifiDisplay[0];
WifiDisplay[] newDisplays = newStatus.getRememberedDisplays();
WifiDisplay[] availableDisplays = newStatus.getAvailableDisplays();
for (int i = 0; i < newDisplays.length; i++) {
final WifiDisplay d = newDisplays[i];
final WifiDisplay oldRemembered = findMatchingDisplay(d, oldDisplays);
if (oldRemembered == null) {
addRoute(makeWifiDisplayRoute(d));
needScan = true;
} else {
final boolean available = findMatchingDisplay(d, availableDisplays) != null;
final RouteInfo route = findWifiDisplayRoute(d);
updateWifiDisplayRoute(route, d, available, newStatus);
}
}
for (int i = 0; i < oldDisplays.length; i++) {
final WifiDisplay d = oldDisplays[i];
final WifiDisplay newDisplay = findMatchingDisplay(d, newDisplays);
if (newDisplay == null) {
removeRoute(findWifiDisplayRoute(d));
}
}
if (needScan) {
sStatic.mDisplayService.scanWifiDisplays();
}
sStatic.mLastKnownWifiDisplayStatus = newStatus;
}
static RouteInfo makeWifiDisplayRoute(WifiDisplay display) {
final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
newRoute.mDeviceAddress = display.getDeviceAddress();
newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
newRoute.mStatus = sStatic.mResources.getText(
com.android.internal.R.string.media_route_status_connecting);
newRoute.mEnabled = false;
newRoute.mName = makeWifiDisplayName(display);
return newRoute;
}
static String makeWifiDisplayName(WifiDisplay display) {
String name = display.getDeviceAlias();
if (TextUtils.isEmpty(name)) {
name = display.getDeviceName();
}
return name;
}
private static void updateWifiDisplayRoute(RouteInfo route, WifiDisplay display,
boolean available, WifiDisplayStatus wifiDisplayStatus) {
final boolean isScanning =
wifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING;
boolean changed = false;
int newStatus = RouteInfo.STATUS_NONE;
if (available) {
newStatus = isScanning ? RouteInfo.STATUS_SCANNING : RouteInfo.STATUS_AVAILABLE;
} else {
newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
}
if (display.equals(wifiDisplayStatus.getActiveDisplay())) {
final int activeState = wifiDisplayStatus.getActiveDisplayState();
switch (activeState) {
case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
newStatus = RouteInfo.STATUS_NONE;
break;
case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
newStatus = RouteInfo.STATUS_CONNECTING;
break;
case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
Log.e(TAG, "Active display is not connected!");
break;
}
}
final String newName = makeWifiDisplayName(display);
if (route.getName().equals(newName)) {
route.mName = newName;
changed = true;
}
changed |= route.mEnabled != available;
route.mEnabled = available;
changed |= route.setStatusCode(newStatus);
if (changed) {
dispatchRouteChanged(route);
}
if (!available && route == sStatic.mSelectedRoute) {
// Oops, no longer available. Reselect the default.
final RouteInfo defaultRoute = sStatic.mDefaultAudioVideo;
selectRouteStatic(defaultRoute.getSupportedTypes(), defaultRoute);
}
}
private static WifiDisplay findMatchingDisplay(WifiDisplay address, WifiDisplay[] displays) {
for (int i = 0; i < displays.length; i++) {
final WifiDisplay d = displays[i];
if (d.equals(address)) {
return d;
}
}
return null;
}
private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
final int count = sStatic.mRoutes.size();
for (int i = 0; i < count; i++) {
final RouteInfo info = sStatic.mRoutes.get(i);
if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
return info;
}
}
return null;
}
/**
* Information about a media route.
*/
@@ -644,6 +834,18 @@ public class MediaRouter {
int mPlaybackStream = AudioManager.STREAM_MUSIC;
VolumeCallbackInfo mVcb;
String mDeviceAddress;
boolean mEnabled = true;
// A predetermined connection status that can override mStatus
private int mStatusCode;
static final int STATUS_NONE = 0;
static final int STATUS_SCANNING = 1;
static final int STATUS_CONNECTING = 2;
static final int STATUS_AVAILABLE = 3;
static final int STATUS_NOT_AVAILABLE = 4;
private Object mTag;
/**
@@ -710,6 +912,34 @@ public class MediaRouter {
return mStatus;
}
/**
* Set this route's status by predetermined status code. If the caller
* should dispatch a route changed event this call will return true;
*/
boolean setStatusCode(int statusCode) {
if (statusCode != mStatusCode) {
mStatusCode = statusCode;
int resId = 0;
switch (statusCode) {
case STATUS_SCANNING:
resId = com.android.internal.R.string.media_route_status_scanning;
break;
case STATUS_CONNECTING:
resId = com.android.internal.R.string.media_route_status_connecting;
break;
case STATUS_AVAILABLE:
resId = com.android.internal.R.string.media_route_status_available;
break;
case STATUS_NOT_AVAILABLE:
resId = com.android.internal.R.string.media_route_status_not_available;
break;
}
mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
return true;
}
return false;
}
/**
* @return A media type flag set describing which types this route supports.
*/
@@ -866,6 +1096,13 @@ public class MediaRouter {
return mVolumeHandling;
}
/**
* @return true if this route is enabled and may be selected
*/
public boolean isEnabled() {
return mEnabled;
}
void setStatusInt(CharSequence status) {
if (!status.equals(mStatus)) {
mStatus = status;
@@ -881,7 +1118,6 @@ public class MediaRouter {
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);
@@ -1400,6 +1636,7 @@ public class MediaRouter {
int mNameResId;
int mTypes;
final boolean mGroupable;
boolean mIsSystem;
RouteCategory(CharSequence name, int types, boolean groupable) {
mName = name;
@@ -1486,6 +1723,14 @@ public class MediaRouter {
return mGroupable;
}
/**
* @return true if this is the category reserved for system routes.
* @hide
*/
public boolean isSystem() {
return mIsSystem;
}
public String toString() {
return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
" groupable=" + mGroupable + " }";
@@ -1671,7 +1916,6 @@ public class MediaRouter {
}
static class VolumeChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
@@ -1689,6 +1933,15 @@ public class MediaRouter {
}
}
}
}
static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
}
}
}
}