From 9b08edff236fc68d836eccfaa1a5f028dc390cec Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 4 Jul 2014 09:32:40 +0900 Subject: [PATCH] do not merge. Implement onVideoAvailable/Unavailable in TIF. Use cases: - VIDEO_UNAVAILABLE_REASON_TUNE: to show spinner on channel change. - VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL: to show a message and spinner on weak TV signal. - VIDEO_UNAVAILABLE_REASON_BUFFERING: to show spinner on buffering. - VIDEO_UNAVAILABLE_REASON_UNKNOWN: to show spinner. Bug: 14126559 Change-Id: Ide2b68c74bd96d4ea1b9d85ab099a277253d05c7 (cherry picked from commit 1edfd8eb985ee41095c60f4aaf7029f56abff614) --- api/current.txt | 8 ++ .../java/android/media/tv/ITvInputClient.aidl | 2 + .../media/tv/ITvInputSessionCallback.aidl | 2 + .../java/android/media/tv/TvInputManager.java | 83 +++++++++++++++++++ .../java/android/media/tv/TvInputService.java | 54 +++++++++++- media/java/android/media/tv/TvView.java | 41 +++++++++ .../server/tv/TvInputManagerService.java | 34 ++++++++ 7 files changed, 223 insertions(+), 1 deletion(-) diff --git a/api/current.txt b/api/current.txt index c319ce8b8ccdc..8868c8565bf86 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16166,6 +16166,10 @@ package android.media.tv { public final class TvInputManager { method public boolean getAvailability(java.lang.String); method public java.util.List getTvInputList(); + field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3 + field public static final int VIDEO_UNAVAILABLE_REASON_TUNE = 1; // 0x1 + field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0 + field public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; // 0x2 } public static abstract class TvInputManager.TvInputListener { @@ -16185,6 +16189,8 @@ package android.media.tv { ctor public TvInputService.Session(); method public void dispatchChannelRetuned(android.net.Uri); method public void dispatchTrackInfoChanged(java.util.List); + method public void dispatchVideoAvailable(); + method public void dispatchVideoUnavailable(int); method public android.view.View onCreateOverlayView(); method public boolean onGenericMotionEvent(android.view.MotionEvent); method public boolean onKeyDown(int, android.view.KeyEvent); @@ -16263,6 +16269,8 @@ package android.media.tv { method public void onError(java.lang.String, int); method public void onTrackInfoChanged(java.lang.String, java.util.List); method public void onVideoSizeChanged(java.lang.String, int, int); + method public void onVideoAvailable(java.lang.String); + method public void onVideoUnavailable(java.lang.String, int); } } diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl index 27360eda478c7..cac8a14687e6e 100644 --- a/media/java/android/media/tv/ITvInputClient.aidl +++ b/media/java/android/media/tv/ITvInputClient.aidl @@ -35,4 +35,6 @@ oneway interface ITvInputClient { void onSessionEvent(in String name, in Bundle args, int seq); void onChannelRetuned(in Uri channelUri, int seq); void onTrackInfoChanged(in List tracks, int seq); + void onVideoAvailable(int seq); + void onVideoUnavailable(int reason, int seq); } diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl index 4d7205916b45d..e0036e1366b8d 100644 --- a/media/java/android/media/tv/ITvInputSessionCallback.aidl +++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl @@ -31,4 +31,6 @@ oneway interface ITvInputSessionCallback { void onSessionEvent(in String name, in Bundle args); void onChannelRetuned(in Uri channelUri); void onTrackInfoChanged(in List tracks); + void onVideoAvailable(); + void onVideoUnavailable(int reason); } diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index f1a63adc1cf64..867b0db6ab6c1 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -47,6 +47,24 @@ import java.util.Map; public final class TvInputManager { private static final String TAG = "TvInputManager"; + /** + * A generic reason. Video is not available due to an unspecified error. + */ + public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; + /** + * Video is not available because the TV input is tuning to another channel. + */ + public static final int VIDEO_UNAVAILABLE_REASON_TUNE = 1; + /** + * Video is not available due to the weak TV signal. + */ + public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; + /** + * Video is not available because the TV input stopped the playback temporarily to buffer more + * data. + */ + public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; + private final ITvInputManager mService; // A mapping from an input to the list of its TvInputListenerRecords. @@ -107,6 +125,29 @@ public final class TvInputManager { public void onTrackInfoChanged(Session session, List tracks) { } + /** + * This is called when the video is available, so the TV input starts the playback. + * + * @param session A {@link TvInputManager.Session} associated with this callback + */ + public void onVideoAvailable(Session session) { + } + + /** + * This is called when the video is not available, so the TV input stops the playback. + * + * @param session A {@link TvInputManager.Session} associated with this callback + * @param reason The reason why the TV input stopped the playback: + *
    + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNE} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} + *
+ */ + public void onVideoUnavailable(Session session, int reason) { + } + /** * This is called when a custom event has been sent from this session. * @@ -168,6 +209,24 @@ public final class TvInputManager { }); } + public void postVideoAvailable() { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onVideoAvailable(mSession); + } + }); + } + + public void postVideoUnavailable(final int reason) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onVideoUnavailable(mSession, reason); + } + }); + } + public void postSessionEvent(final String eventType, final Bundle eventArgs) { mHandler.post(new Runnable() { @Override @@ -279,6 +338,30 @@ public final class TvInputManager { } } + @Override + public void onVideoAvailable(int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postVideoAvailable(); + } + } + + @Override + public void onVideoUnavailable(int reason, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postVideoUnavailable(reason); + } + } + @Override public void onSessionEvent(String eventType, Bundle eventArgs, int seq) { synchronized (mSessionCallbackRecordMap) { diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index ba12e2e224590..a994f5472271d 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -266,6 +266,56 @@ public abstract class TvInputService extends Service { }); } + /** + * Informs the application that video is available and the playback of the TV stream has + * been started. + */ + public void dispatchVideoAvailable() { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "dispatchVideoAvailable"); + mSessionCallback.onVideoAvailable(); + } catch (RemoteException e) { + Log.w(TAG, "error in dispatchVideoAvailable"); + } + } + }); + } + + /** + * Informs the application that video is not available, so the TV input cannot continue + * playing the TV stream. + * + * @param reason The reason why the TV input stopped the playback: + *
    + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNE} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} + *
+ */ + public void dispatchVideoUnavailable(final int reason) { + if (reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN + && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNE + && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL + && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING) { + throw new IllegalArgumentException("Unknown reason: " + reason); + } + mHandler.post(new Runnable() { + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "dispatchVideoUnavailable"); + mSessionCallback.onVideoUnavailable(reason); + } catch (RemoteException e) { + Log.w(TAG, "error in dispatchVideoUnavailable"); + } + } + }); + } + /** * Called when the session is released. */ @@ -289,7 +339,9 @@ public abstract class TvInputService extends Service { public abstract void onSetStreamVolume(float volume); /** - * Tunes to a given channel. + * Tunes to a given channel. When the video is available, {@link #dispatchVideoAvailable()} + * should be called. Also, {@link #dispatchVideoUnavailable(int)} should be called when the + * TV input cannot continue playing the given channel. * * @param channelUri The URI of the channel. * @return {@code true} the tuning was successful, {@code false} otherwise. diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 7eb27b6c6cd0c..4ac1ba422a7a6 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -524,6 +524,29 @@ public class TvView extends ViewGroup { public void onTrackInfoChanged(String inputId, List tracks) { } + /** + * This is called when the video is available, so the TV input starts the playback. + * + * @param inputId The ID of the TV input bound to this view. + */ + public void onVideoAvailable(String inputId) { + } + + /** + * This is called when the video is not available, so the TV input stops the playback. + * + * @param inputId The ID of the TV input bound to this view. + * @param reason The reason why the TV input stopped the playback: + *
    + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNE} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} + *
+ */ + public void onVideoUnavailable(String inputId, int reason) { + } + /** * This is invoked when a custom event from the bound TV input is sent to this view. * @@ -625,6 +648,24 @@ public class TvView extends ViewGroup { } } + public void onVideoAvailable(Session session) { + if (DEBUG) { + Log.d(TAG, "onVideoAvailable()"); + } + if (mListener != null) { + mListener.onVideoAvailable(mInputId); + } + } + + public void onVideoUnavailable(Session session, int reason) { + if (DEBUG) { + Log.d(TAG, "onVideoUnavailable(" + reason + ")"); + } + if (mListener != null) { + mListener.onVideoUnavailable(mInputId, reason); + } + } + @Override public void onSessionEvent(TvInputManager.Session session, String eventType, Bundle eventArgs) { diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index fac78a9be3e25..5e95af4368718 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -471,6 +471,40 @@ public final class TvInputManagerService extends SystemService { } } + @Override + public void onVideoAvailable() { + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "onVideoAvailable()"); + } + if (sessionState.mSession == null || sessionState.mClient == null) { + return; + } + try { + sessionState.mClient.onVideoAvailable(sessionState.mSeq); + } catch (RemoteException e) { + Slog.e(TAG, "error in onVideoAvailable"); + } + } + } + + @Override + public void onVideoUnavailable(int reason) { + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "onVideoUnavailable(" + reason + ")"); + } + if (sessionState.mSession == null || sessionState.mClient == null) { + return; + } + try { + sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq); + } catch (RemoteException e) { + Slog.e(TAG, "error in onVideoUnavailable"); + } + } + } + @Override public void onSessionEvent(String eventType, Bundle eventArgs) { synchronized (mLock) {