diff options
Diffstat (limited to 'media/java/android')
17 files changed, 410 insertions, 55 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 88756d7..c3d5d94 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1017,7 +1017,7 @@ public class AudioManager { public void setMasterMute(boolean state, int flags) { IAudioService service = getService(); try { - service.setMasterMute(state, flags, mICallBack); + service.setMasterMute(state, flags, mContext.getOpPackageName(), mICallBack); } catch (RemoteException e) { Log.e(TAG, "Dead object in setMasterMute", e); } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 74f39b7..2f782cc 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -1321,11 +1321,16 @@ public class AudioService extends IAudioService.Stub { } /** @see AudioManager#setMasterMute(boolean, int) */ - public void setMasterMute(boolean state, int flags, IBinder cb) { + public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) { if (mUseFixedVolume) { return; } + if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(), + callingPackage) != AppOpsManager.MODE_ALLOWED) { + return; + } + if (state != AudioSystem.getMasterMute()) { AudioSystem.setMasterMute(state); // Post a persist master volume msg diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index e59623b..ba3cfb6 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -62,7 +62,7 @@ interface IAudioService { boolean isStreamMute(int streamType); - void setMasterMute(boolean state, int flags, IBinder cb); + void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb); boolean isMasterMute(); diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index a4d491d8..1da0215 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -28,6 +28,8 @@ import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.WifiDisplay; import android.hardware.display.WifiDisplayStatus; +import android.media.session.MediaSession; +import android.media.session.RemoteVolumeProvider; import android.os.Handler; import android.os.IBinder; import android.os.Process; @@ -58,6 +60,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MediaRouter { private static final String TAG = "MediaRouter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean USE_SESSIONS = true; static class Static implements DisplayManager.DisplayListener { final Context mAppContext; @@ -1141,7 +1144,7 @@ public class MediaRouter { public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) { return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable); } - + /** * Create a new route category. Each route must belong to a category. * @@ -1980,6 +1983,7 @@ public class MediaRouter { */ public static class UserRouteInfo extends RouteInfo { RemoteControlClient mRcc; + SessionVolumeProvider mSvp; UserRouteInfo(RouteCategory category) { super(category); @@ -1996,7 +2000,7 @@ public class MediaRouter { mName = name; routeUpdated(); } - + /** * Set the user-visible name of this route. * <p> @@ -2100,7 +2104,11 @@ public class MediaRouter { public void setPlaybackType(int type) { if (mPlaybackType != type) { mPlaybackType = type; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type); + } } } @@ -2113,8 +2121,12 @@ public class MediaRouter { public void setVolumeHandling(int volumeHandling) { if (mVolumeHandling != volumeHandling) { mVolumeHandling = volumeHandling; - setPlaybackInfoOnRcc( - RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc( + RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling); + } } } @@ -2127,7 +2139,13 @@ public class MediaRouter { volume = Math.max(0, Math.min(volume, getVolumeMax())); if (mVolume != volume) { mVolume = volume; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); + if (USE_SESSIONS) { + if (mSvp != null) { + mSvp.notifyVolumeChanged(); + } + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); + } dispatchRouteVolumeChanged(this); if (mGroup != null) { mGroup.memberVolumeChanged(this); @@ -2166,7 +2184,11 @@ public class MediaRouter { public void setVolumeMax(int volumeMax) { if (mVolumeMax != volumeMax) { mVolumeMax = volumeMax; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax); + } } } @@ -2177,29 +2199,76 @@ public class MediaRouter { public void setPlaybackStream(int stream) { if (mPlaybackStream != stream) { mPlaybackStream = stream; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream); + } } } private void updatePlaybackInfoOnRcc() { - if ((mRcc != null) && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) { - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType); - // let AudioService know whom to call when remote volume needs to be updated - try { - sStatic.mAudioService.registerRemoteVolumeObserverForRcc( - mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */); - } catch (RemoteException e) { - Log.e(TAG, "Error registering remote volume observer", e); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + if ((mRcc != null) + && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) { + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType); + // let AudioService know whom to call when remote volume + // needs to be updated + try { + sStatic.mAudioService.registerRemoteVolumeObserverForRcc( + mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */); + } catch (RemoteException e) { + Log.e(TAG, "Error registering remote volume observer", e); + } + } + } + } + + private void configureSessionVolume() { + if (mRcc == null) { + if (DEBUG) { + Log.d(TAG, "No Rcc to configure volume for route " + mName); + } + return; + } + MediaSession session = mRcc.getMediaSession(); + if (session == null) { + if (DEBUG) { + Log.d(TAG, "Rcc has no session to configure volume"); + } + return; + } + if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) { + int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED; + switch (mVolumeHandling) { + case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE: + volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE; + break; + case RemoteControlClient.PLAYBACK_VOLUME_FIXED: + default: + break; } + // Only register a new listener if necessary + if (mSvp == null || mSvp.getVolumeControl() != volumeControl + || mSvp.getMaxVolume() != mVolumeMax) { + mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax); + session.setPlaybackToRemote(mSvp); + } + } else { + // We only know how to handle local and remote, fall back to local if not remote. + session.setPlaybackToLocal(mPlaybackStream); + mSvp = null; } } @@ -2208,6 +2277,42 @@ public class MediaRouter { mRcc.setPlaybackInformation(what, value); } } + + class SessionVolumeProvider extends RemoteVolumeProvider { + + public SessionVolumeProvider(int volumeControl, int maxVolume) { + super(volumeControl, maxVolume); + } + + @Override + public int onGetCurrentVolume() { + return mVcb == null ? 0 : mVcb.route.mVolume; + } + + @Override + public void onSetVolumeTo(final int volume) { + sStatic.mHandler.post(new Runnable() { + @Override + public void run() { + if (mVcb != null) { + mVcb.vcb.onVolumeSetRequest(mVcb.route, volume); + } + } + }); + } + + @Override + public void onAdjustVolumeBy(final int delta) { + sStatic.mHandler.post(new Runnable() { + @Override + public void run() { + if (mVcb != null) { + mVcb.vcb.onVolumeUpdateRequest(mVcb.route, delta); + } + } + }); + } + } } /** @@ -2504,17 +2609,17 @@ public class MediaRouter { public CharSequence getName() { return getName(sStatic.mResources); } - + /** * Return the properly localized/configuration dependent name of this RouteCategory. - * + * * @param context Context to resolve name resources * @return the name of this route category */ public CharSequence getName(Context context) { return getName(context.getResources()); } - + CharSequence getName(Resources res) { if (mNameResId != 0) { return res.getText(mNameResId); diff --git a/media/java/android/media/session/IActiveSessionsListener.aidl b/media/java/android/media/session/IActiveSessionsListener.aidl new file mode 100644 index 0000000..e5e24bc --- /dev/null +++ b/media/java/android/media/session/IActiveSessionsListener.aidl @@ -0,0 +1,26 @@ +/* Copyright (C) 2014 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.media.session; + +import android.media.session.MediaSessionToken; + +/** + * Listens for changes to the list of active sessions. + * @hide + */ +oneway interface IActiveSessionsListener { + void onActiveSessionsChanged(in List<MediaSessionToken> sessions); +}
\ No newline at end of file diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index 9ce0692..7c03907 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -19,6 +19,7 @@ import android.content.Intent; import android.media.MediaMetadata; import android.media.Rating; import android.media.session.ISessionControllerCallback; +import android.media.session.MediaSessionInfo; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.ResultReceiver; @@ -35,6 +36,7 @@ interface ISessionController { void unregisterCallbackListener(in ISessionControllerCallback cb); boolean isTransportControlEnabled(); void showRoutePicker(); + MediaSessionInfo getSessionInfo(); // These commands are for the TransportController void play(); diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 6d9888f..bd1fa85 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -16,6 +16,7 @@ package android.media.session; import android.content.ComponentName; +import android.media.session.IActiveSessionsListener; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.os.Bundle; @@ -30,4 +31,7 @@ interface ISessionManager { List<IBinder> getSessions(in ComponentName compName, int userId); void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock); void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags); + void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, + int userId); + void removeSessionsListener(in IActiveSessionsListener listener); }
\ No newline at end of file diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index caff1ad..57a0a54 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -246,6 +246,21 @@ public final class MediaController { } } + /** + * Get the info for the session this controller is connected to. + * + * @return The session info for the connected session. + * @hide + */ + public MediaSessionInfo getSessionInfo() { + try { + return mSessionBinder.getSessionInfo(); + } catch (RemoteException e) { + Log.e(TAG, "Error in getSessionInfo.", e); + } + return null; + } + /* * @hide */ diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 7972639..4ba1351 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -277,6 +277,7 @@ public final class MediaSession { throw new IllegalArgumentException("volumeProvider may not be null!"); } mVolumeProvider = volumeProvider; + volumeProvider.setSession(this); try { mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(), @@ -522,6 +523,24 @@ public final class MediaSession { } } + /** + * Notify the system that the remove volume changed. + * + * @param provider The provider that is handling volume changes. + * @hide + */ + void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) { + if (provider == null || provider != mVolumeProvider) { + Log.w(TAG, "Received update from stale volume provider"); + return; + } + try { + mBinder.setCurrentVolume(provider.onGetCurrentVolume()); + } catch (RemoteException e) { + Log.e(TAG, "Error in notifyVolumeChanged", e); + } + } + private void dispatchPlay() { postToTransportCallbacks(TransportMessageHandler.MSG_PLAY); } @@ -963,27 +982,26 @@ public final class MediaSession { @Override public void onRouteStateChange(int state) throws RemoteException { // TODO - } - /* - * (non-Javadoc) - * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int) - */ @Override public void onAdjustVolumeBy(int delta) throws RemoteException { - // TODO(epastern): Auto-generated method stub - + MediaSession session = mMediaSession.get(); + if (session != null) { + if (session.mVolumeProvider != null) { + session.mVolumeProvider.onAdjustVolumeBy(delta); + } + } } - /* - * (non-Javadoc) - * @see android.media.session.ISessionCallback#onSetVolumeTo(int) - */ @Override public void onSetVolumeTo(int value) throws RemoteException { - // TODO(epastern): Auto-generated method stub - + MediaSession session = mMediaSession.get(); + if (session != null) { + if (session.mVolumeProvider != null) { + session.mVolumeProvider.onSetVolumeTo(value); + } + } } } diff --git a/media/java/android/media/session/MediaSessionInfo.aidl b/media/java/android/media/session/MediaSessionInfo.aidl new file mode 100644 index 0000000..63dca9a --- /dev/null +++ b/media/java/android/media/session/MediaSessionInfo.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, 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.media.session; + +parcelable MediaSessionInfo; diff --git a/media/java/android/media/session/MediaSessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java index f701211..4dc1c09 100644 --- a/media/java/android/media/session/MediaSessionInfo.java +++ b/media/java/android/media/session/MediaSessionInfo.java @@ -26,18 +26,21 @@ import android.os.Parcelable; public final class MediaSessionInfo implements Parcelable { private final String mId; private final String mPackageName; + private final int mPid; /** * @hide */ - public MediaSessionInfo(String id, String packageName) { + public MediaSessionInfo(String id, String packageName, int pid) { mId = id; mPackageName = packageName; + mPid = pid; } private MediaSessionInfo(Parcel in) { mId = in.readString(); mPackageName = in.readString(); + mPid = in.readInt(); } /** @@ -58,9 +61,13 @@ public final class MediaSessionInfo implements Parcelable { return mId; } + public int getPid() { + return mPid; + } + @Override public String toString() { - return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + "}"; + return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + ", pid=" + mPid + "}"; } @Override @@ -72,6 +79,7 @@ public final class MediaSessionInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); dest.writeString(mPackageName); + dest.writeInt(mPid); } public static final Parcelable.Creator<MediaSessionInfo> CREATOR diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 9e8b0d3..291bfc8 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -142,6 +142,50 @@ public final class MediaSessionManager { } /** + * Add a listener to be notified when the list of active sessions + * changes.This requires the + * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by + * the calling app. You may also retrieve this list if your app is an + * enabled notification listener using the + * {@link NotificationListenerService} APIs, in which case you must pass the + * {@link ComponentName} of your enabled listener. + * + * @param sessionListener The listener to add. + * @param notificationListener The enabled notification listener component. + * May be null. + * @param userId The userId to listen for changes on. + * @hide + */ + public void addActiveSessionsListener(SessionListener sessionListener, + ComponentName notificationListener, int userId) { + if (sessionListener == null) { + throw new IllegalArgumentException("listener may not be null"); + } + try { + mService.addSessionsListener(sessionListener.mStub, notificationListener, userId); + } catch (RemoteException e) { + Log.e(TAG, "Error in addActiveSessionsListener.", e); + } + } + + /** + * Stop receiving active sessions updates on the specified listener. + * + * @param listener The listener to remove. + * @hide + */ + public void removeActiveSessionsListener(SessionListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener may not be null"); + } + try { + mService.removeSessionsListener(listener.mStub); + } catch (RemoteException e) { + Log.e(TAG, "Error in removeActiveSessionsListener.", e); + } + } + + /** * Send a media key event. The receiver will be selected automatically. * * @param keyEvent The KeyEvent to send. @@ -184,4 +228,35 @@ public final class MediaSessionManager { Log.e(TAG, "Failed to send adjust volume.", e); } } + + /** + * Listens for changes to the list of active sessions. This can be added + * using {@link #addActiveSessionsListener}. + * + * @hide + */ + public static abstract class SessionListener { + /** + * Called when the list of active sessions has changed. This can be due + * to a session being added or removed or the order of sessions + * changing. + * + * @param controllers The updated list of controllers for the user that + * changed. + */ + public abstract void onActiveSessionsChanged(List<MediaController> controllers); + + private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() { + @Override + public void onActiveSessionsChanged(List<MediaSessionToken> tokens) + throws RemoteException { + ArrayList<MediaController> controllers = new ArrayList<MediaController>(); + int size = tokens.size(); + for (int i = 0; i < size; i++) { + controllers.add(MediaController.fromToken(tokens.get(i))); + } + SessionListener.this.onActiveSessionsChanged(controllers); + } + }; + } } diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/MediaSessionToken.java index 86f5662..e599189 100644 --- a/media/java/android/media/session/MediaSessionToken.java +++ b/media/java/android/media/session/MediaSessionToken.java @@ -31,7 +31,7 @@ public final class MediaSessionToken implements Parcelable { /** * @hide */ - MediaSessionToken(ISessionController binder) { + public MediaSessionToken(ISessionController binder) { mBinder = binder; } diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/session/RemoteVolumeProvider.java index 47f672f..606b1d7 100644 --- a/media/java/android/media/session/RemoteVolumeProvider.java +++ b/media/java/android/media/session/RemoteVolumeProvider.java @@ -15,6 +15,9 @@ */ package android.media.session; +import android.os.RemoteException; +import android.util.Log; + /** * Handles requests to adjust or set the volume on a session. This is also used * to push volume updates back to the session after a request has been handled. @@ -22,6 +25,7 @@ package android.media.session; * {@link MediaSession#setPlaybackToRemote}. */ public abstract class RemoteVolumeProvider { + private static final String TAG = "RemoteVolumeProvider"; /** * The volume is fixed and can not be modified. Requests to change volume @@ -46,6 +50,8 @@ public abstract class RemoteVolumeProvider { private final int mControlType; private final int mMaxVolume; + private MediaSession mSession; + /** * Create a new volume provider for handling volume events. You must specify * the type of volume control and the maximum volume that can be used. @@ -88,7 +94,7 @@ public abstract class RemoteVolumeProvider { * Notify the system that the remote playback's volume has been changed. */ public final void notifyVolumeChanged() { - // TODO + mSession.notifyRemoteVolumeChanged(this); } /** @@ -107,4 +113,11 @@ public abstract class RemoteVolumeProvider { */ public void onAdjustVolumeBy(int delta) { } + + /** + * @hide + */ + void setSession(MediaSession session) { + mSession = session; + } }
\ No newline at end of file diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 5e650c2..52045d3 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -306,7 +306,7 @@ public final class TvContract { public static final int TYPE_ATSC_C = 0x00030200; /** The channel type for ATSC-M/H (mobile/handheld). */ - public static final int TYPE_ATSC_M_H = 0x00030200; + public static final int TYPE_ATSC_M_H = 0x00030300; /** The channel type for ISDB-T (terrestrial). */ public static final int TYPE_ISDB_T = 0x00040000; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 9525c08..868c5bf 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -46,6 +46,27 @@ public final class TvInputInfo implements Parcelable { private static final String TAG = "TvInputInfo"; /** + * TV input type: the TV input service is not handling input from hardware. For example, + * services showing streaming from the internet falls into this type. + */ + public static final int TYPE_VIRTUAL = 0; + + // Should be in sync with hardware/libhardware/include/hardware/tv_input.h + + /** + * TV input type: the TV input service is HDMI. (e.g. HDMI 1) + */ + public static final int TYPE_HDMI = 1; + /** + * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner) + */ + public static final int TYPE_TUNER = 2; + /** + * TV input type: the TV input service is stateless pass-through. (e.g. RGB, composite, etc.) + */ + public static final int TYPE_PASSTHROUGH = 3; + + /** * The name of the TV input service to provide to the setup activity and settings activity. */ public static final String EXTRA_SERVICE_NAME = "serviceName"; @@ -58,6 +79,7 @@ public final class TvInputInfo implements Parcelable { // Attributes from XML meta data. private String mSetupActivity; private String mSettingsActivity; + private int mType; /** * Create a new instance of the TvInputInfo class, @@ -105,6 +127,11 @@ public final class TvInputInfo implements Parcelable { Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for " + si.name); } + input.mType = sa.getInt( + com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL); + if (DEBUG) { + Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name); + } sa.recycle(); return input; @@ -179,6 +206,13 @@ public final class TvInputInfo implements Parcelable { } /** + * Returns the type of this TV input service. + */ + public int getType() { + return mType; + } + + /** * Loads the user-displayed label for this TV input service. * * @param pm Supplies a PackageManager used to load the TV input's resources. diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index c2459a6..2831d9e 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -33,12 +33,13 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.ViewGroup; import android.view.ViewRootImpl; /** * View playing TV */ -public class TvView extends SurfaceView { +public class TvView extends ViewGroup { private static final String TAG = "TvView"; // STOPSHIP: Turn debugging off. private static final boolean DEBUG = true; @@ -57,6 +58,7 @@ public class TvView extends SurfaceView { private final Handler mHandler = new Handler(); private TvInputManager.Session mSession; + private final SurfaceView mSurfaceView; private Surface mSurface; private boolean mOverlayViewCreated; private Rect mOverlayViewFrame; @@ -64,6 +66,8 @@ public class TvView extends SurfaceView { private MySessionCallback mSessionCallback; private TvInputListener mListener; private OnUnhandledInputEventListener mOnUnhandledInputEventListener; + private boolean mHasStreamVolume; + private float mStreamVolume; private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { @Override @@ -122,7 +126,14 @@ public class TvView extends SurfaceView { public TvView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - getHolder().addCallback(mSurfaceHolderCallback); + mSurfaceView = new SurfaceView(context, attrs, defStyleAttr) { + @Override + protected void updateWindow(boolean force, boolean redrawNeeded) { + super.updateWindow(force, redrawNeeded); + relayoutSessionOverlayView(); + }}; + mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback); + addView(mSurfaceView); mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); } @@ -143,7 +154,10 @@ public class TvView extends SurfaceView { */ public void setStreamVolume(float volume) { if (DEBUG) Log.d(TAG, "setStreamVolume(" + volume + ")"); + mHasStreamVolume = true; + mStreamVolume = volume; if (mSession == null) { + // Volume will be set once the connection has been made. return; } mSession.setStreamVolume(volume); @@ -305,11 +319,26 @@ public class TvView extends SurfaceView { super.onDetachedFromWindow(); } - /** @hide */ @Override - protected void updateWindow(boolean force, boolean redrawNeeded) { - super.updateWindow(force, redrawNeeded); - relayoutSessionOverlayView(); + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + mSurfaceView.layout(0, 0, right - left, bottom - top); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec); + int width = mSurfaceView.getMeasuredWidth(); + int height = mSurfaceView.getMeasuredHeight(); + int childState = mSurfaceView.getMeasuredState(); + setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState), + resolveSizeAndState(height, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + mSurfaceView.setVisibility(visibility); } private void release() { @@ -476,6 +505,9 @@ public class TvView extends SurfaceView { } createSessionOverlayView(); mSession.tune(mChannelUri); + if (mHasStreamVolume) { + mSession.setStreamVolume(mStreamVolume); + } } else { if (mListener != null) { mListener.onError(mInputId, ERROR_BUSY); |
