| /* |
| * Copyright (C) 2013 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 com.android.media.remotedisplay; |
| |
| import android.app.PendingIntent; |
| import android.app.Service; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.media.IRemoteDisplayCallback; |
| import android.media.IRemoteDisplayProvider; |
| import android.media.RemoteDisplayState; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.provider.Settings; |
| import android.util.ArrayMap; |
| |
| import java.util.Collection; |
| |
| /** |
| * Base class for remote display providers implemented as unbundled services. |
| * <p> |
| * To implement your remote display provider service, create a subclass of |
| * {@link Service} and override the {@link Service#onBind Service.onBind()} method |
| * to return the provider's binder when the {@link #SERVICE_INTERFACE} is requested. |
| * </p> |
| * <pre> |
| * public class SampleRemoteDisplayProviderService extends Service { |
| * private SampleProvider mProvider; |
| * |
| * public IBinder onBind(Intent intent) { |
| * if (intent.getAction().equals(RemoteDisplayProvider.SERVICE_INTERFACE)) { |
| * if (mProvider == null) { |
| * mProvider = new SampleProvider(this); |
| * } |
| * return mProvider.getBinder(); |
| * } |
| * return null; |
| * } |
| * |
| * class SampleProvider extends RemoteDisplayProvider { |
| * public SampleProvider() { |
| * super(SampleRemoteDisplayProviderService.this); |
| * } |
| * |
| * // --- Implementation goes here --- |
| * } |
| * } |
| * </pre> |
| * <p> |
| * Declare your remote display provider service in your application manifest |
| * like this: |
| * </p> |
| * <pre> |
| * <application> |
| * <uses-library android:name="com.android.media.remotedisplay" /> |
| * |
| * <service android:name=".SampleRemoteDisplayProviderService" |
| * android:label="@string/sample_remote_display_provider_service" |
| * android:exported="true" |
| * android:permission="android.permission.BIND_REMOTE_DISPLAY"> |
| * <intent-filter> |
| * <action android:name="com.android.media.remotedisplay.RemoteDisplayProvider" /> |
| * </intent-filter> |
| * </service> |
| * </application> |
| * </pre> |
| * <p> |
| * This object is not thread safe. It is only intended to be accessed on the |
| * {@link Context#getMainLooper main looper thread} of an application. |
| * </p><p> |
| * IMPORTANT: This class is effectively a public API for unbundled applications, and |
| * must remain API stable. See README.txt in the root of this package for more information. |
| * </p> |
| */ |
| public abstract class RemoteDisplayProvider { |
| private static final int MSG_SET_CALLBACK = 1; |
| private static final int MSG_SET_DISCOVERY_MODE = 2; |
| private static final int MSG_CONNECT = 3; |
| private static final int MSG_DISCONNECT = 4; |
| private static final int MSG_SET_VOLUME = 5; |
| private static final int MSG_ADJUST_VOLUME = 6; |
| |
| private final Context mContext; |
| private final ProviderStub mStub; |
| private final ProviderHandler mHandler; |
| private final ArrayMap<String, RemoteDisplay> mDisplays = |
| new ArrayMap<String, RemoteDisplay>(); |
| private IRemoteDisplayCallback mCallback; |
| private int mDiscoveryMode = DISCOVERY_MODE_NONE; |
| |
| private PendingIntent mSettingsPendingIntent; |
| |
| /** |
| * The {@link Intent} that must be declared as handled by the service. |
| * Put this in your manifest. |
| */ |
| public static final String SERVICE_INTERFACE = RemoteDisplayState.SERVICE_INTERFACE; |
| |
| /** |
| * Discovery mode: Do not perform any discovery. |
| */ |
| public static final int DISCOVERY_MODE_NONE = RemoteDisplayState.DISCOVERY_MODE_NONE; |
| |
| /** |
| * Discovery mode: Passive or low-power periodic discovery. |
| * <p> |
| * This mode indicates that an application is interested in knowing whether there |
| * are any remote displays paired or available but doesn't need the latest or |
| * most detailed information. The provider may scan at a lower rate or rely on |
| * knowledge of previously paired devices. |
| * </p> |
| */ |
| public static final int DISCOVERY_MODE_PASSIVE = RemoteDisplayState.DISCOVERY_MODE_PASSIVE; |
| |
| /** |
| * Discovery mode: Active discovery. |
| * <p> |
| * This mode indicates that the user is actively trying to connect to a route |
| * and we should perform continuous scans. This mode may use significantly more |
| * power but is intended to be short-lived. |
| * </p> |
| */ |
| public static final int DISCOVERY_MODE_ACTIVE = RemoteDisplayState.DISCOVERY_MODE_ACTIVE; |
| |
| /** |
| * Creates a remote display provider. |
| * |
| * @param context The application context for the remote display provider. |
| */ |
| public RemoteDisplayProvider(Context context) { |
| mContext = context; |
| mStub = new ProviderStub(); |
| mHandler = new ProviderHandler(context.getMainLooper()); |
| } |
| |
| /** |
| * Gets the context of the remote display provider. |
| */ |
| public final Context getContext() { |
| return mContext; |
| } |
| |
| /** |
| * Gets the Binder associated with the provider. |
| * <p> |
| * This is intended to be used for the onBind() method of a service that implements |
| * a remote display provider service. |
| * </p> |
| * |
| * @return The IBinder instance associated with the provider. |
| */ |
| public IBinder getBinder() { |
| return mStub; |
| } |
| |
| /** |
| * Called when the current discovery mode changes. |
| * |
| * @param mode The new discovery mode. |
| */ |
| public void onDiscoveryModeChanged(int mode) { |
| } |
| |
| /** |
| * Called when the system would like to connect to a display. |
| * |
| * @param display The remote display. |
| */ |
| public void onConnect(RemoteDisplay display) { |
| } |
| |
| /** |
| * Called when the system would like to disconnect from a display. |
| * |
| * @param display The remote display. |
| */ |
| public void onDisconnect(RemoteDisplay display) { |
| } |
| |
| /** |
| * Called when the system would like to set the volume of a display. |
| * |
| * @param display The remote display. |
| * @param volume The desired volume. |
| */ |
| public void onSetVolume(RemoteDisplay display, int volume) { |
| } |
| |
| /** |
| * Called when the system would like to adjust the volume of a display. |
| * |
| * @param display The remote display. |
| * @param delta An increment to add to the current volume, such as +1 or -1. |
| */ |
| public void onAdjustVolume(RemoteDisplay display, int delta) { |
| } |
| |
| /** |
| * Gets the current discovery mode. |
| * |
| * @return The current discovery mode. |
| */ |
| public int getDiscoveryMode() { |
| return mDiscoveryMode; |
| } |
| |
| /** |
| * Gets the current collection of displays. |
| * |
| * @return The current collection of displays, which must not be modified. |
| */ |
| public Collection<RemoteDisplay> getDisplays() { |
| return mDisplays.values(); |
| } |
| |
| /** |
| * Adds the specified remote display and notifies the system. |
| * |
| * @param display The remote display that was added. |
| * @throws IllegalStateException if there is already a display with the same id. |
| */ |
| public void addDisplay(RemoteDisplay display) { |
| if (display == null || mDisplays.containsKey(display.getId())) { |
| throw new IllegalArgumentException("display"); |
| } |
| mDisplays.put(display.getId(), display); |
| publishState(); |
| } |
| |
| /** |
| * Updates information about the specified remote display and notifies the system. |
| * |
| * @param display The remote display that was added. |
| * @throws IllegalStateException if the display was n |
| */ |
| public void updateDisplay(RemoteDisplay display) { |
| if (display == null || mDisplays.get(display.getId()) != display) { |
| throw new IllegalArgumentException("display"); |
| } |
| publishState(); |
| } |
| |
| /** |
| * Removes the specified remote display and tells the system about it. |
| * |
| * @param display The remote display that was removed. |
| */ |
| public void removeDisplay(RemoteDisplay display) { |
| if (display == null || mDisplays.get(display.getId()) != display) { |
| throw new IllegalArgumentException("display"); |
| } |
| mDisplays.remove(display.getId()); |
| publishState(); |
| } |
| |
| /** |
| * Finds the remote display with the specified id, returns null if not found. |
| * |
| * @param id Id of the remote display. |
| * @return The display, or null if none. |
| */ |
| public RemoteDisplay findRemoteDisplay(String id) { |
| return mDisplays.get(id); |
| } |
| |
| /** |
| * Gets a pending intent to launch the remote display settings activity. |
| * |
| * @return A pending intent to launch the settings activity. |
| */ |
| public PendingIntent getSettingsPendingIntent() { |
| if (mSettingsPendingIntent == null) { |
| Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS); |
| settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
| | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED |
| | Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| mSettingsPendingIntent = PendingIntent.getActivity( |
| mContext, 0, settingsIntent, 0, null); |
| } |
| return mSettingsPendingIntent; |
| } |
| |
| void setCallback(IRemoteDisplayCallback callback) { |
| mCallback = callback; |
| publishState(); |
| } |
| |
| void setDiscoveryMode(int mode) { |
| if (mDiscoveryMode != mode) { |
| mDiscoveryMode = mode; |
| onDiscoveryModeChanged(mode); |
| } |
| } |
| |
| void publishState() { |
| if (mCallback != null) { |
| RemoteDisplayState state = new RemoteDisplayState(); |
| final int count = mDisplays.size(); |
| for (int i = 0; i < count; i++) { |
| final RemoteDisplay display = mDisplays.valueAt(i); |
| state.displays.add(display.getInfo()); |
| } |
| try { |
| mCallback.onStateChanged(state); |
| } catch (RemoteException ex) { |
| // system server died? |
| } |
| } |
| } |
| |
| final class ProviderStub extends IRemoteDisplayProvider.Stub { |
| @Override |
| public void setCallback(IRemoteDisplayCallback callback) { |
| mHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget(); |
| } |
| |
| @Override |
| public void setDiscoveryMode(int mode) { |
| mHandler.obtainMessage(MSG_SET_DISCOVERY_MODE, mode, 0).sendToTarget(); |
| } |
| |
| @Override |
| public void connect(String id) { |
| mHandler.obtainMessage(MSG_CONNECT, id).sendToTarget(); |
| } |
| |
| @Override |
| public void disconnect(String id) { |
| mHandler.obtainMessage(MSG_DISCONNECT, id).sendToTarget(); |
| } |
| |
| @Override |
| public void setVolume(String id, int volume) { |
| mHandler.obtainMessage(MSG_SET_VOLUME, volume, 0, id).sendToTarget(); |
| } |
| |
| @Override |
| public void adjustVolume(String id, int delta) { |
| mHandler.obtainMessage(MSG_ADJUST_VOLUME, delta, 0, id).sendToTarget(); |
| } |
| } |
| |
| final class ProviderHandler extends Handler { |
| public ProviderHandler(Looper looper) { |
| super(looper, null, true); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_SET_CALLBACK: { |
| setCallback((IRemoteDisplayCallback)msg.obj); |
| break; |
| } |
| case MSG_SET_DISCOVERY_MODE: { |
| setDiscoveryMode(msg.arg1); |
| break; |
| } |
| case MSG_CONNECT: { |
| RemoteDisplay display = findRemoteDisplay((String)msg.obj); |
| if (display != null) { |
| onConnect(display); |
| } |
| break; |
| } |
| case MSG_DISCONNECT: { |
| RemoteDisplay display = findRemoteDisplay((String)msg.obj); |
| if (display != null) { |
| onDisconnect(display); |
| } |
| break; |
| } |
| case MSG_SET_VOLUME: { |
| RemoteDisplay display = findRemoteDisplay((String)msg.obj); |
| if (display != null) { |
| onSetVolume(display, msg.arg1); |
| } |
| break; |
| } |
| case MSG_ADJUST_VOLUME: { |
| RemoteDisplay display = findRemoteDisplay((String)msg.obj); |
| if (display != null) { |
| onAdjustVolume(display, msg.arg1); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |