diff options
| -rw-r--r-- | api/17.txt | 4 | ||||
| -rw-r--r-- | api/current.txt | 4 | ||||
| -rw-r--r-- | core/java/android/app/Presentation.java | 89 | ||||
| -rw-r--r-- | core/java/android/hardware/display/DisplayManager.java | 72 | ||||
| -rw-r--r-- | core/java/android/view/Display.java | 103 | ||||
| -rw-r--r-- | core/java/android/view/DisplayInfo.java | 27 | ||||
| -rw-r--r-- | media/java/android/media/MediaRouter.java | 131 | ||||
| -rw-r--r-- | services/java/com/android/server/display/DisplayDeviceInfo.java | 20 | ||||
| -rw-r--r-- | services/java/com/android/server/display/HeadlessDisplayAdapter.java | 2 | ||||
| -rw-r--r-- | services/java/com/android/server/display/LocalDisplayAdapter.java | 3 | ||||
| -rw-r--r-- | services/java/com/android/server/display/LogicalDisplay.java | 2 | ||||
| -rw-r--r-- | services/java/com/android/server/display/OverlayDisplayAdapter.java | 2 | ||||
| -rw-r--r-- | services/java/com/android/server/display/WifiDisplayAdapter.java | 10 |
13 files changed, 438 insertions, 31 deletions
@@ -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(); diff --git a/api/current.txt b/api/current.txt index 85ae413..e26d8f0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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(); diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java index 20b27c5..3e8af60 100644 --- a/core/java/android/app/Presentation.java +++ b/core/java/android/app/Presentation.java @@ -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 diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 28e320b..0a7a2e7 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -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); } + return mTempDisplays.toArray(new Display[mTempDisplays.size()]); + } finally { + mTempDisplays.clear(); } - if (actualCount != expectedCount) { - Display[] oldDisplays = displays; - displays = new Display[actualCount]; - System.arraycopy(oldDisplays, 0, displays, 0, actualCount); + } + } + + 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) { diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 1cd3e05..758abb5 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -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 @@ -141,6 +143,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()} * or {@link android.hardware.display.DisplayManager#getDisplay} @@ -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); + } + } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index ead5ff4..f3841d5 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -39,6 +39,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. */ public String name; @@ -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) { diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index e08d494..2a5a16e 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -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; @@ -1119,6 +1191,38 @@ public class MediaRouter { } /** + * 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 */ public boolean isEnabled() { @@ -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) { + } } /** diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java index e76bf44..247d8a0 100644 --- a/services/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/java/com/android/server/display/DisplayDeviceInfo.java @@ -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 + "}"; } diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java index 919733d..7a104d7 100644 --- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java +++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java @@ -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; diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java index fa56b83..b37d57f 100644 --- a/services/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/java/com/android/server/display/LocalDisplayAdapter.java @@ -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; diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java index aa62aee..1583137 100644 --- a/services/java/com/android/server/display/LogicalDisplay.java +++ b/services/java/com/android/server/display/LogicalDisplay.java @@ -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; diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java index c35fd98..36e9f74 100644 --- a/services/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java @@ -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; diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index 2ea83ee..45fff30 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -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); } |
