/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.display; import android.content.Context; import android.os.Handler; import android.util.SparseArray; import android.view.Display; import java.util.ArrayList; /** * Manages the properties of attached displays. * <p> * Get an instance of this class by calling * {@link android.content.Context#getSystemService(java.lang.String) * Context.getSystemService()} with the argument * {@link android.content.Context#DISPLAY_SERVICE}. * </p> */ public final class DisplayManager { private static final String TAG = "DisplayManager"; private static final boolean DEBUG = false; private final Context mContext; private final DisplayManagerGlobal mGlobal; 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> * The status is provided as a {@link WifiDisplayStatus} object in the * {@link #EXTRA_WIFI_DISPLAY_STATUS} extra. * </p><p> * This broadcast is only sent to registered receivers and can only be sent by the system. * </p> * @hide */ public static final String ACTION_WIFI_DISPLAY_STATUS_CHANGED = "android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED"; /** * Contains a {@link WifiDisplayStatus} object. * @hide */ 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; mGlobal = DisplayManagerGlobal.getInstance(); } /** * Gets information about a logical display. * * The display metrics may be adjusted to provide compatibility * for legacy applications. * * @param displayId The logical display id. * @return The display object, or null if there is no valid display with the given id. */ public Display getDisplay(int displayId) { synchronized (mLock) { return getOrCreateDisplayLocked(displayId, false /*assumeValid*/); } } /** * Gets all currently valid logical displays. * * @return An array containing all displays. */ public Display[] getDisplays() { 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) { 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(); } } } 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); } } } private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) { Display display = mDisplays.get(displayId); if (display == null) { display = mGlobal.getCompatibleDisplay(displayId, mContext.getCompatibilityInfo(displayId)); if (display != null) { mDisplays.put(displayId, display); } } else if (!assumeValid && !display.isValid()) { display = null; } return display; } /** * Registers an display listener to receive notifications about when * displays are added, removed or changed. * * @param listener The listener to register. * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. * * @see #unregisterDisplayListener */ public void registerDisplayListener(DisplayListener listener, Handler handler) { mGlobal.registerDisplayListener(listener, handler); } /** * Unregisters an input device listener. * * @param listener The listener to unregister. * * @see #registerDisplayListener */ public void unregisterDisplayListener(DisplayListener listener) { mGlobal.unregisterDisplayListener(listener); } /** * Initiates a fresh scan of availble Wifi displays. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. * @hide */ public void scanWifiDisplays() { mGlobal.scanWifiDisplays(); } /** * Connects to a Wifi display. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. * <p> * Automatically remembers the display after a successful connection, if not * already remembered. * </p><p> * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} to connect * to unknown displays. No permissions are required to connect to already known displays. * </p> * * @param deviceAddress The MAC address of the device to which we should connect. * @hide */ public void connectWifiDisplay(String deviceAddress) { mGlobal.connectWifiDisplay(deviceAddress); } /** * Disconnects from the current Wifi display. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. * @hide */ public void disconnectWifiDisplay() { mGlobal.disconnectWifiDisplay(); } /** * Renames a Wifi display. * <p> * The display must already be remembered for this call to succeed. In other words, * we must already have successfully connected to the display at least once and then * not forgotten it. * </p><p> * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. * </p> * * @param deviceAddress The MAC address of the device to rename. * @param alias The alias name by which to remember the device, or null * or empty if no alias should be used. * @hide */ public void renameWifiDisplay(String deviceAddress, String alias) { mGlobal.renameWifiDisplay(deviceAddress, alias); } /** * Forgets a previously remembered Wifi display. * <p> * Automatically disconnects from the display if currently connected to it. * </p><p> * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. * </p> * * @param deviceAddress The MAC address of the device to forget. * @hide */ public void forgetWifiDisplay(String deviceAddress) { mGlobal.forgetWifiDisplay(deviceAddress); } /** * Gets the current Wifi display status. * Watch for changes in the status by registering a broadcast receiver for * {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED}. * * @return The current Wifi display status. * @hide */ public WifiDisplayStatus getWifiDisplayStatus() { return mGlobal.getWifiDisplayStatus(); } /** * Listens for changes in available display devices. */ public interface DisplayListener { /** * Called whenever a logical display has been added to the system. * Use {@link DisplayManager#getDisplay} to get more information about * the display. * * @param displayId The id of the logical display that was added. */ void onDisplayAdded(int displayId); /** * Called whenever a logical display has been removed from the system. * * @param displayId The id of the logical display that was removed. */ void onDisplayRemoved(int displayId); /** * Called whenever the properties of a logical display have changed. * * @param displayId The id of the logical display that changed. */ void onDisplayChanged(int displayId); } }