Loading media/java/android/media/tv/ad/ITvAdManager.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.tv.ad; import android.graphics.Rect; import android.media.tv.ad.ITvAdClient; import android.media.tv.ad.ITvAdManagerCallback; import android.media.tv.ad.TvAdServiceInfo; Loading @@ -37,4 +38,9 @@ interface ITvAdManager { void registerCallback(in ITvAdManagerCallback callback, int userId); void unregisterCallback(in ITvAdManagerCallback callback, int userId); void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame, int userId); void relayoutMediaView(in IBinder sessionToken, in Rect frame, int userId); void removeMediaView(in IBinder sessionToken, int userId); } media/java/android/media/tv/ad/ITvAdSession.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.tv.ad; import android.graphics.Rect; import android.view.Surface; /** Loading @@ -27,4 +28,8 @@ oneway interface ITvAdSession { void startAdService(); void setSurface(in Surface surface); void dispatchSurfaceChanged(int format, int width, int height); void createMediaView(in IBinder windowToken, in Rect frame); void relayoutMediaView(in Rect frame); void removeMediaView(); } media/java/android/media/tv/ad/ITvAdSessionWrapper.java +36 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package android.media.tv.ad; import android.content.Context; import android.graphics.Rect; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; Loading @@ -43,6 +45,9 @@ public class ITvAdSessionWrapper private static final int DO_RELEASE = 1; private static final int DO_SET_SURFACE = 2; private static final int DO_DISPATCH_SURFACE_CHANGED = 3; private static final int DO_CREATE_MEDIA_VIEW = 4; private static final int DO_RELAYOUT_MEDIA_VIEW = 5; private static final int DO_REMOVE_MEDIA_VIEW = 6; private final HandlerCaller mCaller; private TvAdService.Session mSessionImpl; Loading @@ -61,6 +66,7 @@ public class ITvAdSessionWrapper @Override public void release() { mSessionImpl.scheduleMediaViewCleanup(); mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE)); } Loading Loading @@ -97,6 +103,20 @@ public class ITvAdSessionWrapper args.recycle(); break; } case DO_CREATE_MEDIA_VIEW: { SomeArgs args = (SomeArgs) msg.obj; mSessionImpl.createMediaView((IBinder) args.arg1, (Rect) args.arg2); args.recycle(); break; } case DO_RELAYOUT_MEDIA_VIEW: { mSessionImpl.relayoutMediaView((Rect) msg.obj); break; } case DO_REMOVE_MEDIA_VIEW: { mSessionImpl.removeMediaView(true); break; } default: { Log.w(TAG, "Unhandled message code: " + msg.what); break; Loading Loading @@ -129,6 +149,22 @@ public class ITvAdSessionWrapper mCaller.obtainMessageIIII(DO_DISPATCH_SURFACE_CHANGED, format, width, height, 0)); } @Override public void createMediaView(IBinder windowToken, Rect frame) { mCaller.executeOrSendMessage( mCaller.obtainMessageOO(DO_CREATE_MEDIA_VIEW, windowToken, frame)); } @Override public void relayoutMediaView(Rect frame) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_MEDIA_VIEW, frame)); } @Override public void removeMediaView() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_MEDIA_VIEW)); } private final class TvAdEventReceiver extends InputEventReceiver { TvAdEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); Loading media/java/android/media/tv/ad/TvAdManager.java +63 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; import android.graphics.Rect; import android.media.tv.TvInputManager; import android.media.tv.flags.Flags; import android.os.Handler; Loading @@ -35,6 +36,7 @@ import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; import android.view.Surface; import android.view.View; import com.android.internal.util.Preconditions; Loading Loading @@ -285,6 +287,67 @@ public class TvAdManager { } } /** * Creates a media view. Once the media view is created, {@link #relayoutMediaView} * should be called whenever the layout of its containing view is changed. * {@link #removeMediaView()} should be called to remove the media view. * Since a session can have only one media view, this method should be called only once * or it can be called again after calling {@link #removeMediaView()}. * * @param view A view for AD service. * @param frame A position of the media view. * @throws IllegalStateException if {@code view} is not attached to a window. */ void createMediaView(@NonNull View view, @NonNull Rect frame) { Preconditions.checkNotNull(view); Preconditions.checkNotNull(frame); if (view.getWindowToken() == null) { throw new IllegalStateException("view must be attached to a window"); } if (mToken == null) { Log.w(TAG, "The session has been already released"); return; } try { mService.createMediaView(mToken, view.getWindowToken(), frame, mUserId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Relayouts the current media view. * * @param frame A new position of the media view. */ void relayoutMediaView(@NonNull Rect frame) { Preconditions.checkNotNull(frame); if (mToken == null) { Log.w(TAG, "The session has been already released"); return; } try { mService.relayoutMediaView(mToken, frame, mUserId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Removes the current media view. */ void removeMediaView() { if (mToken == null) { Log.w(TAG, "The session has been already released"); return; } try { mService.removeMediaView(mToken, mUserId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies of any structural changes (format or size) of the surface passed in * {@link #setSurface}. Loading media/java/android/media/tv/ad/TvAdService.java +219 −0 Original line number Diff line number Diff line Loading @@ -20,19 +20,25 @@ import android.annotation.CallSuper; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; Loading @@ -42,6 +48,7 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; import com.android.internal.os.SomeArgs; Loading @@ -56,6 +63,8 @@ public abstract class TvAdService extends Service { private static final boolean DEBUG = false; private static final String TAG = "TvAdService"; private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000; /** * Name under which a TvAdService component publishes information about itself. This meta-data * must reference an XML resource containing an Loading Loading @@ -151,7 +160,14 @@ public abstract class TvAdService extends Service { private final Context mContext; final Handler mHandler; private final WindowManager mWindowManager; private WindowManager.LayoutParams mWindowParams; private Surface mSurface; private FrameLayout mMediaViewContainer; private View mMediaView; private MediaViewCleanUpTask mMediaViewCleanUpTask; private boolean mMediaViewEnabled; private IBinder mWindowToken; private Rect mMediaFrame; /** Loading @@ -165,6 +181,48 @@ public abstract class TvAdService extends Service { mHandler = new Handler(context.getMainLooper()); } /** * Enables or disables the media view. * * <p>By default, the media view is disabled. Must be called explicitly after the * session is created to enable the media view. * * <p>The TV AD service can disable its media view when needed. * * @param enable {@code true} if you want to enable the media view. {@code false} * otherwise. * @hide */ @CallSuper public void setMediaViewEnabled(final boolean enable) { mHandler.post(new Runnable() { @Override public void run() { if (enable == mMediaViewEnabled) { return; } mMediaViewEnabled = enable; if (enable) { if (mWindowToken != null) { createMediaView(mWindowToken, mMediaFrame); } } else { removeMediaView(false); } } }); } /** * Returns {@code true} if media view is enabled, {@code false} otherwise. * * @see #setMediaViewEnabled(boolean) * @hide */ public boolean isMediaViewEnabled() { return mMediaViewEnabled; } /** * Releases TvAdService session. */ Loading @@ -180,6 +238,9 @@ public abstract class TvAdService extends Service { mSessionCallback = null; mPendingActions.clear(); } // Removes the media view lastly so that any hanging on the main thread can be handled // in {@link #scheduleMediaViewCleanup}. removeMediaView(true); } /** Loading Loading @@ -306,6 +367,33 @@ public abstract class TvAdService extends Service { public void onSurfaceChanged(@PixelFormat.Format int format, int width, int height) { } /** * Called when the size of the media view is changed by the application. * * <p>This is always called at least once when the session is created regardless of whether * the media view is enabled or not. The media view container size is the same as the * containing {@link TvAdView}. Note that the size of the underlying surface can * be different if the surface was changed by calling {@link #layoutSurface}. * * @param width The width of the media view, in pixels. * @param height The height of the media view, in pixels. * @hide */ public void onMediaViewSizeChanged(@Px int width, @Px int height) { } /** * Called when the application requests to create a media view. Each session * implementation can override this method and return its own view. * * @return a view attached to the media window. {@code null} if no media view is created. * @hide */ @Nullable public View onCreateMediaView() { return null; } /** * Takes care of dispatching incoming input events and tells whether the event was handled. */ Loading Loading @@ -388,6 +476,137 @@ public abstract class TvAdService extends Service { } } } /** * Creates a media view. This calls {@link #onCreateMediaView} to get a view to attach * to the media window. * * @param windowToken A window token of the application. * @param frame A position of the media view. */ void createMediaView(IBinder windowToken, Rect frame) { if (mMediaViewContainer != null) { removeMediaView(false); } if (DEBUG) Log.d(TAG, "create media view(" + frame + ")"); mWindowToken = windowToken; mMediaFrame = frame; onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); if (!mMediaViewEnabled) { return; } mMediaView = onCreateMediaView(); if (mMediaView == null) { return; } if (mMediaViewCleanUpTask != null) { mMediaViewCleanUpTask.cancel(true); mMediaViewCleanUpTask = null; } // Creates a container view to check hanging on the media view detaching. // Adding/removing the media view to/from the container make the view attach/detach // logic run on the main thread. mMediaViewContainer = new FrameLayout(mContext.getApplicationContext()); mMediaViewContainer.addView(mMediaView); int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; // We make the overlay view non-focusable and non-touchable so that // the application that owns the window token can decide whether to consume or // dispatch the input events. int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; if (ActivityManager.isHighEndGfx()) { flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } mWindowParams = new WindowManager.LayoutParams( frame.right - frame.left, frame.bottom - frame.top, frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT); mWindowParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; mWindowParams.gravity = Gravity.START | Gravity.TOP; mWindowParams.token = windowToken; mWindowManager.addView(mMediaViewContainer, mWindowParams); } /** * Relayouts the current media view. * * @param frame A new position of the media view. */ void relayoutMediaView(Rect frame) { if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")"); if (mMediaFrame == null || mMediaFrame.width() != frame.width() || mMediaFrame.height() != frame.height()) { // Note: relayoutMediaView is called whenever TvAdView's layout is // changed regardless of setMediaViewEnabled. onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); } mMediaFrame = frame; if (!mMediaViewEnabled || mMediaViewContainer == null) { return; } mWindowParams.x = frame.left; mWindowParams.y = frame.top; mWindowParams.width = frame.right - frame.left; mWindowParams.height = frame.bottom - frame.top; mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams); } /** * Removes the current media view. */ void removeMediaView(boolean clearWindowToken) { if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")"); if (clearWindowToken) { mWindowToken = null; mMediaFrame = null; } if (mMediaViewContainer != null) { // Removes the media view from the view hierarchy in advance so that it can be // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is // hanging. mMediaViewContainer.removeView(mMediaView); mMediaView = null; mWindowManager.removeView(mMediaViewContainer); mMediaViewContainer = null; mWindowParams = null; } } /** * Schedules a task which checks whether the media view is detached and kills the process * if it is not. Note that this method is expected to be called in a non-main thread. */ void scheduleMediaViewCleanup() { View mediaViewParent = mMediaViewContainer; if (mediaViewParent != null) { mMediaViewCleanUpTask = new MediaViewCleanUpTask(); mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mediaViewParent); } } } private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> { @Override protected Void doInBackground(View... views) { View mediaViewParent = views[0]; try { Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS); } catch (InterruptedException e) { return null; } if (isCancelled()) { return null; } if (mediaViewParent.isAttachedToWindow()) { Log.e(TAG, "Time out on releasing media view. Killing " + mediaViewParent.getContext().getPackageName()); android.os.Process.killProcess(Process.myPid()); } return null; } } Loading Loading
media/java/android/media/tv/ad/ITvAdManager.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.tv.ad; import android.graphics.Rect; import android.media.tv.ad.ITvAdClient; import android.media.tv.ad.ITvAdManagerCallback; import android.media.tv.ad.TvAdServiceInfo; Loading @@ -37,4 +38,9 @@ interface ITvAdManager { void registerCallback(in ITvAdManagerCallback callback, int userId); void unregisterCallback(in ITvAdManagerCallback callback, int userId); void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame, int userId); void relayoutMediaView(in IBinder sessionToken, in Rect frame, int userId); void removeMediaView(in IBinder sessionToken, int userId); }
media/java/android/media/tv/ad/ITvAdSession.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.tv.ad; import android.graphics.Rect; import android.view.Surface; /** Loading @@ -27,4 +28,8 @@ oneway interface ITvAdSession { void startAdService(); void setSurface(in Surface surface); void dispatchSurfaceChanged(int format, int width, int height); void createMediaView(in IBinder windowToken, in Rect frame); void relayoutMediaView(in Rect frame); void removeMediaView(); }
media/java/android/media/tv/ad/ITvAdSessionWrapper.java +36 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package android.media.tv.ad; import android.content.Context; import android.graphics.Rect; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; Loading @@ -43,6 +45,9 @@ public class ITvAdSessionWrapper private static final int DO_RELEASE = 1; private static final int DO_SET_SURFACE = 2; private static final int DO_DISPATCH_SURFACE_CHANGED = 3; private static final int DO_CREATE_MEDIA_VIEW = 4; private static final int DO_RELAYOUT_MEDIA_VIEW = 5; private static final int DO_REMOVE_MEDIA_VIEW = 6; private final HandlerCaller mCaller; private TvAdService.Session mSessionImpl; Loading @@ -61,6 +66,7 @@ public class ITvAdSessionWrapper @Override public void release() { mSessionImpl.scheduleMediaViewCleanup(); mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE)); } Loading Loading @@ -97,6 +103,20 @@ public class ITvAdSessionWrapper args.recycle(); break; } case DO_CREATE_MEDIA_VIEW: { SomeArgs args = (SomeArgs) msg.obj; mSessionImpl.createMediaView((IBinder) args.arg1, (Rect) args.arg2); args.recycle(); break; } case DO_RELAYOUT_MEDIA_VIEW: { mSessionImpl.relayoutMediaView((Rect) msg.obj); break; } case DO_REMOVE_MEDIA_VIEW: { mSessionImpl.removeMediaView(true); break; } default: { Log.w(TAG, "Unhandled message code: " + msg.what); break; Loading Loading @@ -129,6 +149,22 @@ public class ITvAdSessionWrapper mCaller.obtainMessageIIII(DO_DISPATCH_SURFACE_CHANGED, format, width, height, 0)); } @Override public void createMediaView(IBinder windowToken, Rect frame) { mCaller.executeOrSendMessage( mCaller.obtainMessageOO(DO_CREATE_MEDIA_VIEW, windowToken, frame)); } @Override public void relayoutMediaView(Rect frame) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_MEDIA_VIEW, frame)); } @Override public void removeMediaView() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_MEDIA_VIEW)); } private final class TvAdEventReceiver extends InputEventReceiver { TvAdEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); Loading
media/java/android/media/tv/ad/TvAdManager.java +63 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; import android.graphics.Rect; import android.media.tv.TvInputManager; import android.media.tv.flags.Flags; import android.os.Handler; Loading @@ -35,6 +36,7 @@ import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; import android.view.Surface; import android.view.View; import com.android.internal.util.Preconditions; Loading Loading @@ -285,6 +287,67 @@ public class TvAdManager { } } /** * Creates a media view. Once the media view is created, {@link #relayoutMediaView} * should be called whenever the layout of its containing view is changed. * {@link #removeMediaView()} should be called to remove the media view. * Since a session can have only one media view, this method should be called only once * or it can be called again after calling {@link #removeMediaView()}. * * @param view A view for AD service. * @param frame A position of the media view. * @throws IllegalStateException if {@code view} is not attached to a window. */ void createMediaView(@NonNull View view, @NonNull Rect frame) { Preconditions.checkNotNull(view); Preconditions.checkNotNull(frame); if (view.getWindowToken() == null) { throw new IllegalStateException("view must be attached to a window"); } if (mToken == null) { Log.w(TAG, "The session has been already released"); return; } try { mService.createMediaView(mToken, view.getWindowToken(), frame, mUserId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Relayouts the current media view. * * @param frame A new position of the media view. */ void relayoutMediaView(@NonNull Rect frame) { Preconditions.checkNotNull(frame); if (mToken == null) { Log.w(TAG, "The session has been already released"); return; } try { mService.relayoutMediaView(mToken, frame, mUserId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Removes the current media view. */ void removeMediaView() { if (mToken == null) { Log.w(TAG, "The session has been already released"); return; } try { mService.removeMediaView(mToken, mUserId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies of any structural changes (format or size) of the surface passed in * {@link #setSurface}. Loading
media/java/android/media/tv/ad/TvAdService.java +219 −0 Original line number Diff line number Diff line Loading @@ -20,19 +20,25 @@ import android.annotation.CallSuper; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; Loading @@ -42,6 +48,7 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; import com.android.internal.os.SomeArgs; Loading @@ -56,6 +63,8 @@ public abstract class TvAdService extends Service { private static final boolean DEBUG = false; private static final String TAG = "TvAdService"; private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000; /** * Name under which a TvAdService component publishes information about itself. This meta-data * must reference an XML resource containing an Loading Loading @@ -151,7 +160,14 @@ public abstract class TvAdService extends Service { private final Context mContext; final Handler mHandler; private final WindowManager mWindowManager; private WindowManager.LayoutParams mWindowParams; private Surface mSurface; private FrameLayout mMediaViewContainer; private View mMediaView; private MediaViewCleanUpTask mMediaViewCleanUpTask; private boolean mMediaViewEnabled; private IBinder mWindowToken; private Rect mMediaFrame; /** Loading @@ -165,6 +181,48 @@ public abstract class TvAdService extends Service { mHandler = new Handler(context.getMainLooper()); } /** * Enables or disables the media view. * * <p>By default, the media view is disabled. Must be called explicitly after the * session is created to enable the media view. * * <p>The TV AD service can disable its media view when needed. * * @param enable {@code true} if you want to enable the media view. {@code false} * otherwise. * @hide */ @CallSuper public void setMediaViewEnabled(final boolean enable) { mHandler.post(new Runnable() { @Override public void run() { if (enable == mMediaViewEnabled) { return; } mMediaViewEnabled = enable; if (enable) { if (mWindowToken != null) { createMediaView(mWindowToken, mMediaFrame); } } else { removeMediaView(false); } } }); } /** * Returns {@code true} if media view is enabled, {@code false} otherwise. * * @see #setMediaViewEnabled(boolean) * @hide */ public boolean isMediaViewEnabled() { return mMediaViewEnabled; } /** * Releases TvAdService session. */ Loading @@ -180,6 +238,9 @@ public abstract class TvAdService extends Service { mSessionCallback = null; mPendingActions.clear(); } // Removes the media view lastly so that any hanging on the main thread can be handled // in {@link #scheduleMediaViewCleanup}. removeMediaView(true); } /** Loading Loading @@ -306,6 +367,33 @@ public abstract class TvAdService extends Service { public void onSurfaceChanged(@PixelFormat.Format int format, int width, int height) { } /** * Called when the size of the media view is changed by the application. * * <p>This is always called at least once when the session is created regardless of whether * the media view is enabled or not. The media view container size is the same as the * containing {@link TvAdView}. Note that the size of the underlying surface can * be different if the surface was changed by calling {@link #layoutSurface}. * * @param width The width of the media view, in pixels. * @param height The height of the media view, in pixels. * @hide */ public void onMediaViewSizeChanged(@Px int width, @Px int height) { } /** * Called when the application requests to create a media view. Each session * implementation can override this method and return its own view. * * @return a view attached to the media window. {@code null} if no media view is created. * @hide */ @Nullable public View onCreateMediaView() { return null; } /** * Takes care of dispatching incoming input events and tells whether the event was handled. */ Loading Loading @@ -388,6 +476,137 @@ public abstract class TvAdService extends Service { } } } /** * Creates a media view. This calls {@link #onCreateMediaView} to get a view to attach * to the media window. * * @param windowToken A window token of the application. * @param frame A position of the media view. */ void createMediaView(IBinder windowToken, Rect frame) { if (mMediaViewContainer != null) { removeMediaView(false); } if (DEBUG) Log.d(TAG, "create media view(" + frame + ")"); mWindowToken = windowToken; mMediaFrame = frame; onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); if (!mMediaViewEnabled) { return; } mMediaView = onCreateMediaView(); if (mMediaView == null) { return; } if (mMediaViewCleanUpTask != null) { mMediaViewCleanUpTask.cancel(true); mMediaViewCleanUpTask = null; } // Creates a container view to check hanging on the media view detaching. // Adding/removing the media view to/from the container make the view attach/detach // logic run on the main thread. mMediaViewContainer = new FrameLayout(mContext.getApplicationContext()); mMediaViewContainer.addView(mMediaView); int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; // We make the overlay view non-focusable and non-touchable so that // the application that owns the window token can decide whether to consume or // dispatch the input events. int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; if (ActivityManager.isHighEndGfx()) { flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } mWindowParams = new WindowManager.LayoutParams( frame.right - frame.left, frame.bottom - frame.top, frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT); mWindowParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; mWindowParams.gravity = Gravity.START | Gravity.TOP; mWindowParams.token = windowToken; mWindowManager.addView(mMediaViewContainer, mWindowParams); } /** * Relayouts the current media view. * * @param frame A new position of the media view. */ void relayoutMediaView(Rect frame) { if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")"); if (mMediaFrame == null || mMediaFrame.width() != frame.width() || mMediaFrame.height() != frame.height()) { // Note: relayoutMediaView is called whenever TvAdView's layout is // changed regardless of setMediaViewEnabled. onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); } mMediaFrame = frame; if (!mMediaViewEnabled || mMediaViewContainer == null) { return; } mWindowParams.x = frame.left; mWindowParams.y = frame.top; mWindowParams.width = frame.right - frame.left; mWindowParams.height = frame.bottom - frame.top; mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams); } /** * Removes the current media view. */ void removeMediaView(boolean clearWindowToken) { if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")"); if (clearWindowToken) { mWindowToken = null; mMediaFrame = null; } if (mMediaViewContainer != null) { // Removes the media view from the view hierarchy in advance so that it can be // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is // hanging. mMediaViewContainer.removeView(mMediaView); mMediaView = null; mWindowManager.removeView(mMediaViewContainer); mMediaViewContainer = null; mWindowParams = null; } } /** * Schedules a task which checks whether the media view is detached and kills the process * if it is not. Note that this method is expected to be called in a non-main thread. */ void scheduleMediaViewCleanup() { View mediaViewParent = mMediaViewContainer; if (mediaViewParent != null) { mMediaViewCleanUpTask = new MediaViewCleanUpTask(); mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mediaViewParent); } } } private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> { @Override protected Void doInBackground(View... views) { View mediaViewParent = views[0]; try { Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS); } catch (InterruptedException e) { return null; } if (isCancelled()) { return null; } if (mediaViewParent.isAttachedToWindow()) { Log.e(TAG, "Time out on releasing media view. Killing " + mediaViewParent.getContext().getPackageName()); android.os.Process.killProcess(Process.myPid()); } return null; } } Loading