Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 92130f64 authored by Jeff Brown's avatar Jeff Brown
Browse files

Add MediaRouter API to get presentation display.

This new API makes it possible for an application to ask on
which Display it should show a Presentation based on the currently
selected media route.

Also added a new API on DisplayManager to query displays that
support a certain category of uses.

Improved the documentation of the Presentation class to explain
how to choose an appropriate Display for presentation.

Bug: 7409073
Change-Id: Iab451215e570ae55f3718fc228303143c800fe51
parent 148d4131
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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();
+4 −0
Original line number Diff line number Diff line
@@ -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();
+88 −1
Original line number Diff line number Diff line
@@ -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) $&#123;
 *     Display presentationDisplay = route.getPresentationDisplay();
 *     if (presentationDisplay != null) $&#123;
 *         Presentation presentation = new MyPresentation(context, presentationDisplay);
 *         presentation.show();
 *     $&#125;
 * $&#125;
 * }
 * <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) $&#123;
 *     // 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();
 * $&#125;
 * }
 * <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
+59 −13
Original line number Diff line number Diff line
@@ -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) {
+95 −8
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
Loading