TIF: Address the feedback from the API review - 2/3
This change addresses the following comments from the council:
- Change the TvView API to be tune(String inputId, Uri channelUri) and
reset()
- @hide TvInputSession class, including createSession() and
SessionCallback
- Define a listener interface specific to TvView, allowing you to hide
the session details and keep the API simpler. Add
setTvInputListener().
Bug: 15345342
Change-Id: I931662841b41d862ebc4a700ccb245889a5f87a5
diff --git a/api/current.txt b/api/current.txt
index 2fe6cee..dc57431 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15951,23 +15951,10 @@
}
public final class TvInputManager {
- method public void createSession(java.lang.String, android.media.tv.TvInputManager.SessionCallback, android.os.Handler);
method public boolean getAvailability(java.lang.String);
method public java.util.List<android.media.tv.TvInputInfo> getTvInputList();
}
- public static final class TvInputManager.Session {
- method public void release();
- method public void setStreamVolume(float);
- method public void tune(android.net.Uri);
- }
-
- public static abstract class TvInputManager.SessionCallback {
- ctor public TvInputManager.SessionCallback();
- method public void onSessionCreated(android.media.tv.TvInputManager.Session);
- method public void onSessionReleased(android.media.tv.TvInputManager.Session);
- }
-
public static abstract class TvInputManager.TvInputListener {
ctor public TvInputManager.TvInputListener();
method public void onAvailabilityChanged(java.lang.String, boolean);
@@ -16002,18 +15989,26 @@
ctor public TvView(android.content.Context);
ctor public TvView(android.content.Context, android.util.AttributeSet);
ctor public TvView(android.content.Context, android.util.AttributeSet, int);
- method public void bindTvInput(java.lang.String, android.media.tv.TvInputManager.SessionCallback);
method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
+ method public void reset();
method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
method public void setStreamVolume(float);
- method public void unbindTvInput();
+ method public void setTvInputListener(android.media.tv.TvView.TvInputListener);
+ method public void tune(java.lang.String, android.net.Uri);
+ field public static final int ERROR_BUSY = 0; // 0x0
+ field public static final int ERROR_TV_INPUT_DISCONNECTED = 1; // 0x1
}
public static abstract interface TvView.OnUnhandledInputEventListener {
method public abstract boolean onUnhandledInputEvent(android.view.InputEvent);
}
+ public static abstract class TvView.TvInputListener {
+ ctor public TvView.TvInputListener();
+ method public void onError(java.lang.String, int);
+ }
+
}
package android.mtp {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 38d7c41..edfdd60 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -67,6 +67,7 @@
/**
* Interface used to receive the created session.
+ * @hide
*/
public abstract static class SessionCallback {
/**
@@ -467,6 +468,7 @@
* @param callback a callback used to receive the created session.
* @param handler a {@link Handler} that the session creation will be delivered to.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @hide
*/
public void createSession(String inputId, final SessionCallback callback,
Handler handler) {
@@ -491,7 +493,10 @@
}
}
- /** The Session provides the per-session functionality of TV inputs. */
+ /**
+ * The Session provides the per-session functionality of TV inputs.
+ * @hide
+ */
public static final class Session {
static final int DISPATCH_IN_PROGRESS = -1;
static final int DISPATCH_NOT_HANDLED = 0;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 0601195..409a33c 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -152,7 +152,7 @@
public abstract Session onCreateSession();
/**
- * Base class for derived classes to implement to provide {@link TvInputManager.Session}.
+ * Base class for derived classes to implement to provide a TV input session.
*/
public abstract class Session implements KeyEvent.Callback {
private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6c7abc5..c2459a6 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -21,6 +21,7 @@
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.media.tv.TvInputManager.SessionCallback;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
@@ -38,9 +39,21 @@
* View playing TV
*/
public class TvView extends SurfaceView {
+ private static final String TAG = "TvView";
// STOPSHIP: Turn debugging off.
private static final boolean DEBUG = true;
- private static final String TAG = "TvView";
+
+ /**
+ * Passed with {@link TvInputListener#onError(String, int)}. Indicates that the requested TV
+ * input is busy and unable to handle the request.
+ */
+ public static final int ERROR_BUSY = 0;
+
+ /**
+ * Passed with {@link TvInputListener#onError(String, int)}. Indicates that the underlying TV
+ * input has been disconnected.
+ */
+ public static final int ERROR_TV_INPUT_DISCONNECTED = 1;
private final Handler mHandler = new Handler();
private TvInputManager.Session mSession;
@@ -48,7 +61,8 @@
private boolean mOverlayViewCreated;
private Rect mOverlayViewFrame;
private final TvInputManager mTvInputManager;
- private SessionCallback mSessionCallback;
+ private MySessionCallback mSessionCallback;
+ private TvInputListener mListener;
private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@@ -113,44 +127,13 @@
}
/**
- * Binds a TV input to this view. {@link SessionCallback#onSessionCreated} will be
- * called to send the result of this binding with {@link TvInputManager.Session}.
- * If a TV input is already bound, the input will be unbound from this view and its session
- * will be released.
+ * Sets a listener for events in this TvView.
*
- * @param inputId the id of TV input which will be bound to this view.
- * @param callback called when TV input is bound. The callback sends
- * {@link TvInputManager.Session}
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @param listener The listener to be called with events. A value of {@code null} removes any
+ * existing listener.
*/
- public void bindTvInput(String inputId, SessionCallback callback) {
- if (TextUtils.isEmpty(inputId)) {
- throw new IllegalArgumentException("inputId cannot be null or an empty string");
- }
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- if (mSession != null) {
- release();
- }
- // When bindTvInput is called multiple times before the callback is called,
- // only the callback of the last bindTvInput call will be actually called back.
- // The previous callbacks will be ignored. For the logic, mSessionCallback
- // is newly assigned for every bindTvInput call and compared with
- // MySessionCreateCallback.this.
- mSessionCallback = new MySessionCallback(callback);
- mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
- }
-
- /**
- * Unbinds a TV input currently bound. Its corresponding {@link TvInputManager.Session}
- * is released.
- */
- public void unbindTvInput() {
- if (mSession != null) {
- release();
- }
- mSessionCallback = null;
+ public void setTvInputListener(TvInputListener listener) {
+ mListener = listener;
}
/**
@@ -167,6 +150,50 @@
}
/**
+ * Tunes to a given channel.
+ *
+ * @param inputId the id of TV input which will play the given channel.
+ * @param channelUri The URI of a channel.
+ */
+ public void tune(String inputId, Uri channelUri) {
+ if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
+ if (TextUtils.isEmpty(inputId)) {
+ throw new IllegalArgumentException("inputId cannot be null or an empty string");
+ }
+ if (mSessionCallback != null && mSessionCallback.mInputId.equals(inputId)) {
+ if (mSession != null) {
+ mSession.tune(channelUri);
+ } else {
+ // Session is not created yet. Replace the channel which will be set once the
+ // session is made.
+ mSessionCallback.mChannelUri = channelUri;
+ }
+ } else {
+ if (mSession != null) {
+ release();
+ }
+ // When createSession() is called multiple times before the callback is called,
+ // only the callback of the last createSession() call will be actually called back.
+ // The previous callbacks will be ignored. For the logic, mSessionCallback
+ // is newly assigned for every createSession request and compared with
+ // MySessionCreateCallback.this.
+ mSessionCallback = new MySessionCallback(inputId, channelUri);
+ mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+ }
+ }
+
+ /**
+ * Resets this TvView.
+ * <p>
+ * This method is primarily used to un-tune the current TvView.
+ */
+ public void reset() {
+ if (mSession != null) {
+ release();
+ }
+ }
+
+ /**
* Dispatches an unhandled input event to the next receiver.
* <p>
* Except system keys, TvView always consumes input events in the normal flow. This is called
@@ -290,6 +317,7 @@
removeSessionOverlayView();
mSession.release();
mSession = null;
+ mSessionCallback = null;
}
private void setSessionSurface(Surface surface) {
@@ -339,6 +367,71 @@
}
/**
+ * Interface used to receive various status updates on the {@link TvView}.
+ */
+ public abstract static class TvInputListener {
+
+ /**
+ * This is invoked when an error occurred while handling requested operation.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param errorCode The error code. For the details of error code, please see
+ * {@link TvView}.
+ */
+ public void onError(String inputId, int errorCode) {
+ }
+
+ /**
+ * This is invoked when the view is tuned to a specific channel and starts decoding video
+ * stream from there. It is also called later when the video format is changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param width The width of the video.
+ * @param height The height of the video.
+ * @param interlaced {@code true} if the video is interlaced, {@code false} if the video is
+ * progressive.
+ * @hide
+ */
+ public void onVideoStreamChanged(String inputId, int width, int height,
+ boolean interlaced) {
+ }
+
+ /**
+ * This is invoked when the view is tuned to a specific channel and starts decoding audio
+ * stream from there. It is also called later when the audio format is changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param channelCount The number of channels in the audio stream.
+ * @hide
+ */
+ public void onAudioStreamChanged(String inputId, int channelCount) {
+ }
+
+ /**
+ * This is invoked when the view is tuned to a specific channel and starts decoding data
+ * stream that includes subtitle information from the channel. It is also called later when
+ * the information disappears or appears.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param hasClosedCaption {@code true} if the stream contains closed caption, {@code false}
+ * otherwise.
+ * @hide
+ */
+ public void onClosedCaptionStreamChanged(String inputId, boolean hasClosedCaption) {
+ }
+
+ /**
+ * This is invoked when a custom event from the bound TV input is sent to this view.
+ *
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ public void onEvent(String inputId, String eventType, Bundle eventArgs) {
+ }
+ }
+
+ /**
* Interface definition for a callback to be invoked when the unhandled input event is received.
*/
public interface OnUnhandledInputEventListener {
@@ -356,10 +449,12 @@
}
private class MySessionCallback extends SessionCallback {
- final SessionCallback mExternalCallback;
+ final String mInputId;
+ Uri mChannelUri;
- MySessionCallback(SessionCallback externalCallback) {
- mExternalCallback = externalCallback;
+ MySessionCallback(String inputId, Uri channelUri) {
+ mInputId = inputId;
+ mChannelUri = channelUri;
}
@Override
@@ -380,17 +475,20 @@
setSessionSurface(mSurface);
}
createSessionOverlayView();
- }
- if (mExternalCallback != null) {
- mExternalCallback.onSessionCreated(session);
+ mSession.tune(mChannelUri);
+ } else {
+ if (mListener != null) {
+ mListener.onError(mInputId, ERROR_BUSY);
+ }
}
}
@Override
public void onSessionReleased(Session session) {
mSession = null;
- if (mExternalCallback != null) {
- mExternalCallback.onSessionReleased(session);
+ mSessionCallback = null;
+ if (mListener != null) {
+ mListener.onError(mInputId, ERROR_TV_INPUT_DISCONNECTED);
}
}
@@ -400,8 +498,8 @@
if (DEBUG) {
Log.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
}
- if (mExternalCallback != null) {
- mExternalCallback.onVideoStreamChanged(session, width, height, interlaced);
+ if (mListener != null) {
+ mListener.onVideoStreamChanged(mInputId, width, height, interlaced);
}
}
@@ -410,8 +508,8 @@
if (DEBUG) {
Log.d(TAG, "onAudioStreamChanged(" + channelCount + ")");
}
- if (mExternalCallback != null) {
- mExternalCallback.onAudioStreamChanged(session, channelCount);
+ if (mListener != null) {
+ mListener.onAudioStreamChanged(mInputId, channelCount);
}
}
@@ -420,16 +518,16 @@
if (DEBUG) {
Log.d(TAG, "onClosedCaptionStreamChanged(" + hasClosedCaption + ")");
}
- if (mExternalCallback != null) {
- mExternalCallback.onClosedCaptionStreamChanged(session, hasClosedCaption);
+ if (mListener != null) {
+ mListener.onClosedCaptionStreamChanged(mInputId, hasClosedCaption);
}
}
@Override
public void onSessionEvent(TvInputManager.Session session, String eventType,
Bundle eventArgs) {
- if (mExternalCallback != null) {
- mExternalCallback.onSessionEvent(session, eventType, eventArgs);
+ if (mListener != null) {
+ mListener.onEvent(mInputId, eventType, eventArgs);
}
}
}