* commit '14012e1d1b3c96048955e22d2bb73b9c63189a54': Add MediaRouter API to get presentation display.
This commit is contained in:
@@ -10093,8 +10093,10 @@ package android.hardware.display {
|
||||
public final class DisplayManager {
|
||||
method public android.view.Display getDisplay(int);
|
||||
method public android.view.Display[] getDisplays();
|
||||
method public android.view.Display[] getDisplays(java.lang.String);
|
||||
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
|
||||
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
|
||||
field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
|
||||
}
|
||||
|
||||
public static abstract interface DisplayManager.DisplayListener {
|
||||
@@ -11768,6 +11770,7 @@ package android.media {
|
||||
method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
|
||||
method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
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);
|
||||
@@ -11802,6 +11805,7 @@ package android.media {
|
||||
method public java.lang.CharSequence getName(android.content.Context);
|
||||
method public int getPlaybackStream();
|
||||
method public int getPlaybackType();
|
||||
method public android.view.Display getPresentationDisplay();
|
||||
method public java.lang.CharSequence getStatus();
|
||||
method public int getSupportedTypes();
|
||||
method public java.lang.Object getTag();
|
||||
|
||||
@@ -10093,8 +10093,10 @@ package android.hardware.display {
|
||||
public final class DisplayManager {
|
||||
method public android.view.Display getDisplay(int);
|
||||
method public android.view.Display[] getDisplays();
|
||||
method public android.view.Display[] getDisplays(java.lang.String);
|
||||
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
|
||||
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
|
||||
field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
|
||||
}
|
||||
|
||||
public static abstract interface DisplayManager.DisplayListener {
|
||||
@@ -11768,6 +11770,7 @@ package android.media {
|
||||
method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
|
||||
method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
|
||||
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);
|
||||
@@ -11802,6 +11805,7 @@ package android.media {
|
||||
method public java.lang.CharSequence getName(android.content.Context);
|
||||
method public int getPlaybackStream();
|
||||
method public int getPlaybackType();
|
||||
method public android.view.Display getPresentationDisplay();
|
||||
method public java.lang.CharSequence getStatus();
|
||||
method public int getSupportedTypes();
|
||||
method public java.lang.Object getTag();
|
||||
|
||||
@@ -50,6 +50,93 @@ import android.util.TypedValue;
|
||||
* whenever the activity itself is paused or resumed.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Choosing a presentation display</h3>
|
||||
* <p>
|
||||
* Before showing a {@link Presentation} it's important to choose the {@link Display}
|
||||
* on which it will appear. Choosing a presentation display is sometimes difficult
|
||||
* because there may be multiple displays attached. Rather than trying to guess
|
||||
* which display is best, an application should let the system choose a suitable
|
||||
* presentation display.
|
||||
* </p><p>
|
||||
* There are two main ways to choose a {@link Display}.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Using the media router to choose a presentation display</h4>
|
||||
* <p>
|
||||
* The easiest way to choose a presentation display is to use the
|
||||
* {@link android.media.MediaRouter MediaRouter} API. The media router service keeps
|
||||
* track of which audio and video routes are available on the system.
|
||||
* The media router sends notifications whenever routes are selected or unselected
|
||||
* or when the preferred presentation display of a route changes.
|
||||
* So an application can simply watch for these notifications and show or dismiss
|
||||
* a presentation on the preferred presentation display automatically.
|
||||
* </p><p>
|
||||
* The preferred presentation display is the display that the media router recommends
|
||||
* that the application should use if it wants to show content on the secondary display.
|
||||
* Sometimes there may not be a preferred presentation display in which
|
||||
* case the application should show its content locally without using a presentation.
|
||||
* </p><p>
|
||||
* Here's how to use the media router to create and show a presentation on the preferred
|
||||
* presentation display using {@link android.media.MediaRouter.RouteInfo#getPresentationDisplay()}.
|
||||
* </p>
|
||||
* {@samplecode
|
||||
* MediaRouter mediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
|
||||
* MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
|
||||
* if (route != null) ${
|
||||
* Display presentationDisplay = route.getPresentationDisplay();
|
||||
* if (presentationDisplay != null) ${
|
||||
* Presentation presentation = new MyPresentation(context, presentationDisplay);
|
||||
* presentation.show();
|
||||
* $}
|
||||
* $}
|
||||
* }
|
||||
* <p>
|
||||
* The following sample code from <code>ApiDemos</code> demonstrates how to use the media
|
||||
* router to automatically switch between showing content in the main activity and showing
|
||||
* the content in a presentation when a presentation display is available.
|
||||
* </p>
|
||||
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java
|
||||
* activity}
|
||||
*
|
||||
* <h4>Using the display manager to choose a presentation display</h4>
|
||||
* <p>
|
||||
* Another way to choose a presentation display is to use the {@link DisplayManager} API
|
||||
* directly. The display manager service provides functions to enumerate and describe all
|
||||
* displays that are attached to the system including displays that may be used
|
||||
* for presentations.
|
||||
* </p><p>
|
||||
* The display manager keeps track of all displays in the system. However, not all
|
||||
* displays are appropriate for showing presentations. For example, if an activity
|
||||
* attempted to show a presentation on the main display it might obscure its own content
|
||||
* (it's like opening a dialog on top of your activity).
|
||||
* </p><p>
|
||||
* Here's how to identify suitable displays for showing presentations using
|
||||
* {@link DisplayManager#getDisplays(String)} and the
|
||||
* {@link DisplayManager#DISPLAY_CATEGORY_PRESENTATION} category.
|
||||
* </p>
|
||||
* {@samplecode
|
||||
* DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
||||
* Display[] presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
|
||||
* if (presentationDisplays.length > 0) ${
|
||||
* // If there is more than one suitable presentation display, then we could consider
|
||||
* // giving the user a choice. For this example, we simply choose the first display
|
||||
* // which is the one the system recommends as the preferred presentation display.
|
||||
* Display display = presentationDisplays[0];
|
||||
* Presentation presentation = new MyPresentation(context, presentationDisplay);
|
||||
* presentation.show();
|
||||
* $}
|
||||
* }
|
||||
* <p>
|
||||
* The following sample code from <code>ApiDemos</code> demonstrates how to use the display
|
||||
* manager to enumerate displays and show content on multiple presentation displays
|
||||
* simultaneously.
|
||||
* </p>
|
||||
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java
|
||||
* activity}
|
||||
*
|
||||
* @see android.media.MediaRouter#ROUTE_TYPE_LIVE_VIDEO for information on about live
|
||||
* video routes and how to obtain the preferred presentation display for the
|
||||
* current media route.
|
||||
* @see DisplayManager for information on how to enumerate displays and receive
|
||||
* notifications when displays are added or removed.
|
||||
*/
|
||||
@@ -121,7 +208,7 @@ public class Presentation extends Dialog {
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
mDisplayManager.registerDisplayListener(mDisplayListener, null);
|
||||
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
|
||||
|
||||
// Since we were not watching for display changes until just now, there is a
|
||||
// chance that the display metrics have changed. If so, we will need to
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.os.Handler;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Manages the properties of attached displays.
|
||||
* <p>
|
||||
@@ -40,6 +42,8 @@ public final class DisplayManager {
|
||||
private final Object mLock = new Object();
|
||||
private final SparseArray<Display> mDisplays = new SparseArray<Display>();
|
||||
|
||||
private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
|
||||
|
||||
/**
|
||||
* Broadcast receiver that indicates when the Wifi display status changes.
|
||||
* <p>
|
||||
@@ -60,6 +64,20 @@ public final class DisplayManager {
|
||||
public static final String EXTRA_WIFI_DISPLAY_STATUS =
|
||||
"android.hardware.display.extra.WIFI_DISPLAY_STATUS";
|
||||
|
||||
/**
|
||||
* Display category: Presentation displays.
|
||||
* <p>
|
||||
* This category can be used to identify secondary displays that are suitable for
|
||||
* use as presentation displays.
|
||||
* </p>
|
||||
*
|
||||
* @see android.app.Presentation for information about presenting content
|
||||
* on secondary displays.
|
||||
* @see #getDisplays(String)
|
||||
*/
|
||||
public static final String DISPLAY_CATEGORY_PRESENTATION =
|
||||
"android.hardware.display.category.PRESENTATION";
|
||||
|
||||
/** @hide */
|
||||
public DisplayManager(Context context) {
|
||||
mContext = context;
|
||||
@@ -87,24 +105,52 @@ public final class DisplayManager {
|
||||
* @return An array containing all displays.
|
||||
*/
|
||||
public Display[] getDisplays() {
|
||||
int[] displayIds = mGlobal.getDisplayIds();
|
||||
int expectedCount = displayIds.length;
|
||||
Display[] displays = new Display[expectedCount];
|
||||
return getDisplays(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all currently valid logical displays of the specified category.
|
||||
* <p>
|
||||
* When there are multiple displays in a category the returned displays are sorted
|
||||
* of preference. For example, if the requested category is
|
||||
* {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays
|
||||
* then the displays are sorted so that the first display in the returned array
|
||||
* is the most preferred presentation display. The application may simply
|
||||
* use the first display or allow the user to choose.
|
||||
* </p>
|
||||
*
|
||||
* @param category The requested display category or null to return all displays.
|
||||
* @return An array containing all displays sorted by order of preference.
|
||||
*
|
||||
* @see #DISPLAY_CATEGORY_PRESENTATION
|
||||
*/
|
||||
public Display[] getDisplays(String category) {
|
||||
final int[] displayIds = mGlobal.getDisplayIds();
|
||||
synchronized (mLock) {
|
||||
int actualCount = 0;
|
||||
for (int i = 0; i < expectedCount; i++) {
|
||||
Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
|
||||
if (display != null) {
|
||||
displays[actualCount++] = display;
|
||||
try {
|
||||
if (category == null) {
|
||||
addMatchingDisplaysLocked(mTempDisplays, displayIds, -1);
|
||||
} else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
|
||||
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
|
||||
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
|
||||
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
|
||||
}
|
||||
}
|
||||
if (actualCount != expectedCount) {
|
||||
Display[] oldDisplays = displays;
|
||||
displays = new Display[actualCount];
|
||||
System.arraycopy(oldDisplays, 0, displays, 0, actualCount);
|
||||
return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
|
||||
} finally {
|
||||
mTempDisplays.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addMatchingDisplaysLocked(
|
||||
ArrayList<Display> displays, int[] displayIds, int matchType) {
|
||||
for (int i = 0; i < displayIds.length; i++) {
|
||||
Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
|
||||
if (display != null
|
||||
&& (matchType < 0 || display.getType() == matchType)) {
|
||||
displays.add(display);
|
||||
}
|
||||
}
|
||||
return displays;
|
||||
}
|
||||
|
||||
private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
|
||||
|
||||
@@ -54,7 +54,9 @@ public final class Display {
|
||||
private final DisplayManagerGlobal mGlobal;
|
||||
private final int mDisplayId;
|
||||
private final int mLayerStack;
|
||||
private final String mName;
|
||||
private final int mFlags;
|
||||
private final int mType;
|
||||
private final String mAddress;
|
||||
private final CompatibilityInfoHolder mCompatibilityInfo;
|
||||
|
||||
private DisplayInfo mDisplayInfo; // never null
|
||||
@@ -140,6 +142,36 @@ public final class Display {
|
||||
*/
|
||||
public static final int FLAG_SECURE = 1 << 1;
|
||||
|
||||
/**
|
||||
* Display type: Unknown display type.
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* Display type: Built-in display.
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_BUILT_IN = 1;
|
||||
|
||||
/**
|
||||
* Display type: HDMI display.
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_HDMI = 2;
|
||||
|
||||
/**
|
||||
* Display type: WiFi display.
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_WIFI = 3;
|
||||
|
||||
/**
|
||||
* Display type: Overlay display.
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_OVERLAY = 4;
|
||||
|
||||
/**
|
||||
* Internal method to create a display.
|
||||
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
|
||||
@@ -154,10 +186,14 @@ public final class Display {
|
||||
mGlobal = global;
|
||||
mDisplayId = displayId;
|
||||
mDisplayInfo = displayInfo;
|
||||
mLayerStack = displayInfo.layerStack; // can never change as long as the display is valid
|
||||
mName = displayInfo.name; // cannot change as long as the display is valid
|
||||
mCompatibilityInfo = compatibilityInfo;
|
||||
mIsValid = true;
|
||||
|
||||
// Cache properties that cannot change as long as the display is valid.
|
||||
mLayerStack = displayInfo.layerStack;
|
||||
mFlags = displayInfo.flags;
|
||||
mType = displayInfo.type;
|
||||
mAddress = displayInfo.address;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,10 +264,34 @@ public final class Display {
|
||||
* @see #FLAG_SECURE
|
||||
*/
|
||||
public int getFlags() {
|
||||
synchronized (this) {
|
||||
updateDisplayInfoLocked();
|
||||
return mDisplayInfo.flags;
|
||||
}
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display type.
|
||||
*
|
||||
* @return The display type.
|
||||
*
|
||||
* @see #TYPE_UNKNOWN
|
||||
* @see #TYPE_BUILT_IN
|
||||
* @see #TYPE_HDMI
|
||||
* @see #TYPE_WIFI
|
||||
* @see #TYPE_OVERLAY
|
||||
* @hide
|
||||
*/
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display address, or null if none.
|
||||
* Interpretation varies by display type.
|
||||
*
|
||||
* @return The display address.
|
||||
* @hide
|
||||
*/
|
||||
public String getAddress() {
|
||||
return mAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,10 +306,17 @@ public final class Display {
|
||||
|
||||
/**
|
||||
* Gets the name of the display.
|
||||
* <p>
|
||||
* Note that some displays may be renamed by the user.
|
||||
* </p>
|
||||
*
|
||||
* @return The display's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return mName;
|
||||
synchronized (this) {
|
||||
updateDisplayInfoLocked();
|
||||
return mDisplayInfo.name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -527,5 +594,25 @@ public final class Display {
|
||||
+ ", " + mTempMetrics + ", isValid=" + mIsValid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static String typeToString(int type) {
|
||||
switch (type) {
|
||||
case TYPE_UNKNOWN:
|
||||
return "UNKNOWN";
|
||||
case TYPE_BUILT_IN:
|
||||
return "BUILT_IN";
|
||||
case TYPE_HDMI:
|
||||
return "HDMI";
|
||||
case TYPE_WIFI:
|
||||
return "WIFI";
|
||||
case TYPE_OVERLAY:
|
||||
return "OVERLAY";
|
||||
default:
|
||||
return Integer.toString(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,17 @@ public final class DisplayInfo implements Parcelable {
|
||||
*/
|
||||
public int flags;
|
||||
|
||||
/**
|
||||
* Display type.
|
||||
*/
|
||||
public int type;
|
||||
|
||||
/**
|
||||
* Display address, or null if none.
|
||||
* Interpretation varies by display type.
|
||||
*/
|
||||
public String address;
|
||||
|
||||
/**
|
||||
* The human-readable name of the display.
|
||||
*/
|
||||
@@ -143,10 +154,12 @@ public final class DisplayInfo implements Parcelable {
|
||||
public float physicalYDpi;
|
||||
|
||||
public static final Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
|
||||
@Override
|
||||
public DisplayInfo createFromParcel(Parcel source) {
|
||||
return new DisplayInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DisplayInfo[] newArray(int size) {
|
||||
return new DisplayInfo[size];
|
||||
}
|
||||
@@ -171,6 +184,9 @@ public final class DisplayInfo implements Parcelable {
|
||||
public boolean equals(DisplayInfo other) {
|
||||
return other != null
|
||||
&& layerStack == other.layerStack
|
||||
&& flags == other.flags
|
||||
&& type == other.type
|
||||
&& Objects.equal(address, other.address)
|
||||
&& Objects.equal(name, other.name)
|
||||
&& appWidth == other.appWidth
|
||||
&& appHeight == other.appHeight
|
||||
@@ -195,6 +211,8 @@ public final class DisplayInfo implements Parcelable {
|
||||
public void copyFrom(DisplayInfo other) {
|
||||
layerStack = other.layerStack;
|
||||
flags = other.flags;
|
||||
type = other.type;
|
||||
address = other.address;
|
||||
name = other.name;
|
||||
appWidth = other.appWidth;
|
||||
appHeight = other.appHeight;
|
||||
@@ -214,6 +232,8 @@ public final class DisplayInfo implements Parcelable {
|
||||
public void readFromParcel(Parcel source) {
|
||||
layerStack = source.readInt();
|
||||
flags = source.readInt();
|
||||
type = source.readInt();
|
||||
address = source.readString();
|
||||
name = source.readString();
|
||||
appWidth = source.readInt();
|
||||
appHeight = source.readInt();
|
||||
@@ -234,6 +254,8 @@ public final class DisplayInfo implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(layerStack);
|
||||
dest.writeInt(this.flags);
|
||||
dest.writeInt(type);
|
||||
dest.writeString(address);
|
||||
dest.writeString(name);
|
||||
dest.writeInt(appWidth);
|
||||
dest.writeInt(appHeight);
|
||||
@@ -294,7 +316,10 @@ public final class DisplayInfo implements Parcelable {
|
||||
+ ", rotation " + rotation
|
||||
+ ", density " + logicalDensityDpi
|
||||
+ ", " + physicalXDpi + " x " + physicalYDpi + " dpi"
|
||||
+ ", layerStack " + layerStack + flagsToString(flags) + "}";
|
||||
+ ", layerStack " + layerStack
|
||||
+ ", type " + Display.typeToString(type)
|
||||
+ ", address " + address
|
||||
+ flagsToString(flags) + "}";
|
||||
}
|
||||
|
||||
private static String flagsToString(int flags) {
|
||||
|
||||
@@ -32,7 +32,6 @@ 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;
|
||||
@@ -53,7 +52,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
public class MediaRouter {
|
||||
private static final String TAG = "MediaRouter";
|
||||
|
||||
static class Static {
|
||||
static class Static implements DisplayManager.DisplayListener {
|
||||
final Resources mResources;
|
||||
final IAudioService mAudioService;
|
||||
final DisplayManager mDisplayService;
|
||||
@@ -105,6 +104,8 @@ public class MediaRouter {
|
||||
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;
|
||||
mDefaultAudioVideo.mPresentationDisplay = choosePresentationDisplayForRoute(
|
||||
mDefaultAudioVideo, getAllPresentationDisplays());
|
||||
addRouteStatic(mDefaultAudioVideo);
|
||||
|
||||
// This will select the active wifi display route if there is one.
|
||||
@@ -115,6 +116,8 @@ public class MediaRouter {
|
||||
appContext.registerReceiver(new VolumeChangeReceiver(),
|
||||
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
|
||||
|
||||
mDisplayService.registerDisplayListener(this, mHandler);
|
||||
|
||||
AudioRoutesInfo newAudioRoutes = null;
|
||||
try {
|
||||
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
|
||||
@@ -191,6 +194,39 @@ public class MediaRouter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
updatePresentationDisplays(displayId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
updatePresentationDisplays(displayId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
updatePresentationDisplays(displayId);
|
||||
}
|
||||
|
||||
public Display[] getAllPresentationDisplays() {
|
||||
return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
|
||||
}
|
||||
|
||||
private void updatePresentationDisplays(int changedDisplayId) {
|
||||
final Display[] displays = getAllPresentationDisplays();
|
||||
final int count = mRoutes.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final RouteInfo info = mRoutes.get(i);
|
||||
Display display = choosePresentationDisplayForRoute(info, displays);
|
||||
if (display != info.mPresentationDisplay
|
||||
|| (display != null && display.getDisplayId() == changedDisplayId)) {
|
||||
info.mPresentationDisplay = display;
|
||||
dispatchRoutePresentationDisplayChanged(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Static sStatic;
|
||||
@@ -218,6 +254,9 @@ public class MediaRouter {
|
||||
* 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>
|
||||
*
|
||||
* @see RouteInfo#getPresentationDisplay()
|
||||
* @see android.app.Presentation
|
||||
*/
|
||||
public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
|
||||
|
||||
@@ -674,6 +713,14 @@ public class MediaRouter {
|
||||
}
|
||||
}
|
||||
|
||||
static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
|
||||
for (CallbackInfo cbi : sStatic.mCallbacks) {
|
||||
if ((cbi.type & info.mSupportedTypes) != 0) {
|
||||
cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void systemVolumeChanged(int newValue) {
|
||||
final RouteInfo selectedRoute = sStatic.mSelectedRoute;
|
||||
if (selectedRoute == null) return;
|
||||
@@ -755,6 +802,9 @@ public class MediaRouter {
|
||||
newRoute.mEnabled = available;
|
||||
|
||||
newRoute.mName = display.getFriendlyDisplayName();
|
||||
|
||||
newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
|
||||
sStatic.getAllPresentationDisplays());
|
||||
return newRoute;
|
||||
}
|
||||
|
||||
@@ -830,6 +880,27 @@ public class MediaRouter {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Display choosePresentationDisplayForRoute(RouteInfo route, Display[] displays) {
|
||||
if ((route.mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
|
||||
if (route.mDeviceAddress != null) {
|
||||
// Find the indicated Wifi display by its address.
|
||||
for (Display display : displays) {
|
||||
if (display.getType() == Display.TYPE_WIFI
|
||||
&& route.mDeviceAddress.equals(display.getAddress())) {
|
||||
return display;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (route == sStatic.mDefaultAudioVideo && displays.length > 0) {
|
||||
// Choose the first presentation display from the list.
|
||||
return displays[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about a media route.
|
||||
*/
|
||||
@@ -848,6 +919,7 @@ public class MediaRouter {
|
||||
int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
|
||||
int mPlaybackStream = AudioManager.STREAM_MUSIC;
|
||||
VolumeCallbackInfo mVcb;
|
||||
Display mPresentationDisplay;
|
||||
|
||||
String mDeviceAddress;
|
||||
boolean mEnabled = true;
|
||||
@@ -1118,6 +1190,38 @@ public class MediaRouter {
|
||||
return mVolumeHandling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Display} that should be used by the application to show
|
||||
* a {@link android.app.Presentation} on an external display when this route is selected.
|
||||
* Depending on the route, this may only be valid if the route is currently
|
||||
* selected.
|
||||
* <p>
|
||||
* The preferred presentation display may change independently of the route
|
||||
* being selected or unselected. For example, the presentation display
|
||||
* of the default system route may change when an external HDMI display is connected
|
||||
* or disconnected even though the route itself has not changed.
|
||||
* </p><p>
|
||||
* This method may return null if there is no external display associated with
|
||||
* the route or if the display is not ready to show UI yet.
|
||||
* </p><p>
|
||||
* The application should listen for changes to the presentation display
|
||||
* using the {@link Callback#onRoutePresentationDisplayChanged} callback and
|
||||
* show or dismiss its {@link android.app.Presentation} accordingly when the display
|
||||
* becomes available or is removed.
|
||||
* </p><p>
|
||||
* This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
|
||||
* </p>
|
||||
*
|
||||
* @return The preferred presentation display to use when this route is
|
||||
* selected or null if none.
|
||||
*
|
||||
* @see #ROUTE_TYPE_LIVE_VIDEO
|
||||
* @see android.app.Presentation
|
||||
*/
|
||||
public Display getPresentationDisplay() {
|
||||
return mPresentationDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this route is enabled and may be selected
|
||||
*/
|
||||
@@ -1159,9 +1263,11 @@ public class MediaRouter {
|
||||
@Override
|
||||
public String toString() {
|
||||
String supportedTypes = typesToString(getSupportedTypes());
|
||||
return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() +
|
||||
" category=" + getCategory() +
|
||||
" supportedTypes=" + supportedTypes + "}";
|
||||
return getClass().getSimpleName() + "{ name=" + getName() +
|
||||
", status=" + getStatus() +
|
||||
", category=" + getCategory() +
|
||||
", supportedTypes=" + supportedTypes +
|
||||
", presentationDisplay=" + mPresentationDisplay + "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1856,6 +1962,21 @@ public class MediaRouter {
|
||||
* @param info The route with altered volume
|
||||
*/
|
||||
public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
|
||||
|
||||
/**
|
||||
* Called when a route's presentation display changes.
|
||||
* <p>
|
||||
* This method is called whenever the route's presentation display becomes
|
||||
* available, is removes or has changes to some of its properties (such as its size).
|
||||
* </p>
|
||||
*
|
||||
* @param router the MediaRouter reporting the event
|
||||
* @param info The route whose presentation display changed
|
||||
*
|
||||
* @see RouteInfo#getPresentationDisplay()
|
||||
*/
|
||||
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.display;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
|
||||
import libcore.util.Objects;
|
||||
@@ -138,6 +139,17 @@ final class DisplayDeviceInfo {
|
||||
*/
|
||||
public int rotation = Surface.ROTATION_0;
|
||||
|
||||
/**
|
||||
* Display type.
|
||||
*/
|
||||
public int type;
|
||||
|
||||
/**
|
||||
* Display address, or null if none.
|
||||
* Interpretation varies by display type.
|
||||
*/
|
||||
public String address;
|
||||
|
||||
public void setAssumedDensityForExternalDisplay(int width, int height) {
|
||||
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
|
||||
// Technically, these values should be smaller than the apparent density
|
||||
@@ -162,7 +174,9 @@ final class DisplayDeviceInfo {
|
||||
&& yDpi == other.yDpi
|
||||
&& flags == other.flags
|
||||
&& touch == other.touch
|
||||
&& rotation == other.rotation;
|
||||
&& rotation == other.rotation
|
||||
&& type == other.type
|
||||
&& Objects.equal(address, other.address);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,6 +195,8 @@ final class DisplayDeviceInfo {
|
||||
flags = other.flags;
|
||||
touch = other.touch;
|
||||
rotation = other.rotation;
|
||||
type = other.type;
|
||||
address = other.address;
|
||||
}
|
||||
|
||||
// For debugging purposes
|
||||
@@ -191,6 +207,8 @@ final class DisplayDeviceInfo {
|
||||
+ "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
|
||||
+ ", touch " + touchToString(touch) + flagsToString(flags)
|
||||
+ ", rotation " + rotation
|
||||
+ ", type " + Display.typeToString(type)
|
||||
+ ", address " + address
|
||||
+ "}";
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.display;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
|
||||
/**
|
||||
* Provides a fake default display for headless systems.
|
||||
@@ -63,6 +64,7 @@ final class HeadlessDisplayAdapter extends DisplayAdapter {
|
||||
mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
|
||||
| DisplayDeviceInfo.FLAG_SECURE
|
||||
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
|
||||
mInfo.type = Display.TYPE_BUILT_IN;
|
||||
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
|
||||
}
|
||||
return mInfo;
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayEventReceiver;
|
||||
import android.view.Surface;
|
||||
import android.view.Surface.PhysicalDisplayInfo;
|
||||
@@ -139,11 +140,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|
||||
com.android.internal.R.string.display_manager_built_in_display_name);
|
||||
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
|
||||
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
|
||||
mInfo.type = Display.TYPE_BUILT_IN;
|
||||
mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
|
||||
mInfo.xDpi = mPhys.xDpi;
|
||||
mInfo.yDpi = mPhys.yDpi;
|
||||
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
|
||||
} else {
|
||||
mInfo.type = Display.TYPE_HDMI;
|
||||
mInfo.name = getContext().getResources().getString(
|
||||
com.android.internal.R.string.display_manager_hdmi_display_name);
|
||||
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
|
||||
|
||||
@@ -189,6 +189,8 @@ final class LogicalDisplay {
|
||||
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SECURE) != 0) {
|
||||
mBaseDisplayInfo.flags |= Display.FLAG_SECURE;
|
||||
}
|
||||
mBaseDisplayInfo.type = deviceInfo.type;
|
||||
mBaseDisplayInfo.address = deviceInfo.address;
|
||||
mBaseDisplayInfo.name = deviceInfo.name;
|
||||
mBaseDisplayInfo.appWidth = deviceInfo.width;
|
||||
mBaseDisplayInfo.appHeight = deviceInfo.height;
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.os.IBinder;
|
||||
import android.provider.Settings;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Slog;
|
||||
import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import android.view.Surface;
|
||||
|
||||
@@ -240,6 +241,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
|
||||
mInfo.xDpi = mDensityDpi;
|
||||
mInfo.yDpi = mDensityDpi;
|
||||
mInfo.flags = 0;
|
||||
mInfo.type = Display.TYPE_OVERLAY;
|
||||
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
|
||||
}
|
||||
return mInfo;
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.os.Message;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -293,9 +294,10 @@ final class WifiDisplayAdapter extends DisplayAdapter {
|
||||
float refreshRate = 60.0f; // TODO: get this for real
|
||||
|
||||
String name = display.getFriendlyDisplayName();
|
||||
String address = display.getDeviceAddress();
|
||||
IBinder displayToken = Surface.createDisplay(name, secure);
|
||||
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
|
||||
refreshRate, deviceFlags, surface);
|
||||
refreshRate, deviceFlags, address, surface);
|
||||
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
|
||||
|
||||
scheduleUpdateNotificationLocked();
|
||||
@@ -515,12 +517,13 @@ final class WifiDisplayAdapter extends DisplayAdapter {
|
||||
private final int mHeight;
|
||||
private final float mRefreshRate;
|
||||
private final int mFlags;
|
||||
private final String mAddress;
|
||||
|
||||
private Surface mSurface;
|
||||
private DisplayDeviceInfo mInfo;
|
||||
|
||||
public WifiDisplayDevice(IBinder displayToken, String name,
|
||||
int width, int height, float refreshRate, int flags,
|
||||
int width, int height, float refreshRate, int flags, String address,
|
||||
Surface surface) {
|
||||
super(WifiDisplayAdapter.this, displayToken);
|
||||
mName = name;
|
||||
@@ -528,6 +531,7 @@ final class WifiDisplayAdapter extends DisplayAdapter {
|
||||
mHeight = height;
|
||||
mRefreshRate = refreshRate;
|
||||
mFlags = flags;
|
||||
mAddress = address;
|
||||
mSurface = surface;
|
||||
}
|
||||
|
||||
@@ -555,6 +559,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
|
||||
mInfo.height = mHeight;
|
||||
mInfo.refreshRate = mRefreshRate;
|
||||
mInfo.flags = mFlags;
|
||||
mInfo.type = Display.TYPE_WIFI;
|
||||
mInfo.address = mAddress;
|
||||
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
|
||||
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user