Chromium Code Reviews| Index: media/base/android/java/src/org/chromium/media/DialogSurfaceCore.java |
| diff --git a/media/base/android/java/src/org/chromium/media/DialogSurfaceCore.java b/media/base/android/java/src/org/chromium/media/DialogSurfaceCore.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..79db2458c2c75b4019b7410b588e4fdab06ed70c |
| --- /dev/null |
| +++ b/media/base/android/java/src/org/chromium/media/DialogSurfaceCore.java |
| @@ -0,0 +1,247 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.media; |
| + |
| +import android.annotation.SuppressLint; |
| +import android.app.Dialog; |
| +import android.content.Context; |
| +import android.os.IBinder; |
| +import android.view.Gravity; |
| +import android.view.SurfaceHolder; |
| +import android.view.Window; |
| +import android.view.WindowManager; |
| + |
| +import org.chromium.base.Log; |
| + |
| +/** |
| + * Core class for control of a single DialogSurface instance. Everything runs |
| + * on a single thread, that's probably not the UI thread. |
| + * |
| + * Note that this does not implement IDialogSurface; we assume that, and the |
| + * associated thread-hopping, is handled elsewhere. |
| + */ |
| +class DialogSurfaceCore { |
| + private static final String TAG = "cr_media"; |
|
boliu
2017/01/04 01:48:44
generally, this is just the name of the class, the
liberato (no reviews please)
2017/01/11 22:17:57
not sure. i copied the pattern from MediaCodecBri
boliu
2017/01/12 20:24:19
fwiw, it's dynamic: https://cs.chromium.org/chromi
liberato (no reviews please)
2017/02/03 21:28:32
Acknowledged.
|
| + |
| + /** |
| + * Call back into native with a message about our state. This can be called |
| + * on any thread. It's okay if it refers to a native object that no longer |
| + * exists; we can't really avoid it. The native side handles it. |
| + * |
| + * The callback cannot recursively call back into us; the IDialogSurface |
| + * implementation must prevent that. |
| + */ |
| + private IDialogSurfaceCallback mClientCallback; |
| + |
| + // Callback operations. |
| + // These must match dialog_surface.h . |
| + private static final int OP_CREATED = 0; |
| + private static final int OP_DESTROYED = 1; |
| + |
| + // When initialized via Init, we'll create mDialog. We'll clear it when |
| + // we send SURFACE_DESTROYED to the client. In general, when this is null, |
| + // either we haven't been initialized yet, or we've been torn down. It |
| + // shouldn't be the case that anything calls methods after construction but |
| + // before Init, though. |
| + private Dialog mDialog; |
| + |
| + private Callbacks mDialogCallbacks; |
| + private IBinder mToken; |
| + |
| + // Initial position and size. |
| + private int mInitialX; |
| + private int mInitialY; |
| + private int mInitialWidth; |
| + private int mInitialHeight; |
| + |
| + /** |
| + * Construction may be called from a random thread, for simplicity. Call |
| + * Init from the proper thread before doing anything else. |
| + */ |
| + public DialogSurfaceCore() {} |
| + |
| + /** |
| + * Finish init on the proper thread. We'll use this thread for the Dialog |
| + * Looper thread. |
| + * @param context Context that we use. |
| + * @param callback callback object to notify about state changes. |
| + * @param x initial x position in chrome compositor (not screen) coords. |
| + * @param y initial y position in chrome compositor (not screen) coords. |
| + * @param width initial width. |
| + * @param height initial height. |
| + */ |
| + public void initialize( |
| + Context context, IDialogSurfaceCallback callback, int x, int y, int width, int height) { |
| + mClientCallback = callback; |
| + mInitialX = x; |
| + mInitialY = y; |
| + mInitialWidth = width; |
| + mInitialHeight = height; |
| + |
| + // Create the dialog, but don't lay it out or show it yet. We'll do |
| + // that when we get a window token. |
| + mDialog = new Dialog(context, android.R.style.Theme_NoDisplay); |
| + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); |
| + mDialog.setCancelable(false); |
| + } |
| + |
| + /** |
| + * Release the underlying surface, and generally clean up, in response to |
| + * the client releasing the IDialogSurface. |
| + */ |
| + public void release() { |
| + // If we've not released the dialog yet, then do so. |
| + if (mDialog != null) { |
| + // TODO(liberato): is this safe if we haven't shown the dialog yet? |
| + mDialog.dismiss(); |
| + mDialog = null; |
| + mDialogCallbacks = null; |
| + } |
| + } |
| + |
| + /** |
| + * Layout the DialogSurface. If we don't have a token, then we ignore it, |
| + * since a well-behaved client shouldn't call us before OP_CREATED anyway. |
| + */ |
| + @SuppressLint("RtlHardcoded") |
| + public void layoutSurface(final int x, final int y, final int width, final int height) { |
| + if (mDialog == null || mToken == null) return; |
| + |
| + // Rather than using getAttributes, we just create them from scratch. |
| + // The default dialog attributes aren't what we want. |
| + WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); |
|
boliu
2017/01/04 01:48:44
ditto about allocating java objects in calls that
liberato (no reviews please)
2017/01/11 22:17:57
Done.
|
| + |
| + // TODO(liberato): adjust for CompositorView screen location here if we |
| + // want to support non-full screen use cases. |
| + |
| + layoutParams.x = x; |
| + layoutParams.y = y; |
| + layoutParams.width = width; |
| + layoutParams.height = height; |
| + layoutParams.token = mToken; |
| + |
| + // NOTE: we really do want LEFT here, since we're dealing in compositor |
| + // coordinates. Those are always from the left. |
| + layoutParams.gravity = Gravity.TOP | Gravity.LEFT; |
| + |
| + // Use a media surface, which is what SurfaceView uses by default. For |
| + // debugging overlay drawing, consider using TYPE_APPLICATION_PANEL to |
| + // move the dialog over the CompositorView. |
| + layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; |
| + |
| + layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
| + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
| + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; |
| + |
| + // Don't set FLAG_SCALED. in addition to not being sure what it does |
| + // (SV uses it), it also causes a crash in WindowManager when we hide |
| + // (not dismiss), navigate, and/or exit the app without hide/dismiss. |
| + // There's a missing null check in WindowManagerService.java@3170 |
| + // on M MR2. To repro, change dimiss() to hide(), bring up a SV, and |
| + // navigate away or press home. |
| + |
| + // Turn off the position animation, so that it doesn't animate from one |
| + // position to the next. Ignore errors. |
| + try { |
| + int currentFlags = |
| + (Integer) layoutParams.getClass().getField("privateFlags").get(layoutParams); |
|
boliu
2017/01/04 01:48:44
err, reflection? hard coded values?
liberato (no reviews please)
2017/01/11 22:17:57
PRIVATE_FLAG_NO_MOVE_ANIMATION -- there's no other
boliu
2017/01/12 20:24:19
The bar for using private APIs is very high, ie it
liberato (no reviews please)
2017/02/03 21:28:32
i'd really like to do this.
we can chat offline a
|
| + layoutParams.getClass() |
| + .getField("privateFlags") |
| + .set(layoutParams, currentFlags | 0x00000040); |
| + // It would be nice to just catch Exception, but findbugs doesn't |
| + // allow it. If we cannot set the flag, then that's okay too. |
| + } catch (NoSuchFieldException e) { |
| + } catch (NullPointerException e) { |
| + } catch (SecurityException e) { |
| + } catch (IllegalAccessException e) { |
| + } catch (IllegalArgumentException e) { |
| + } catch (ExceptionInInitializerError e) { |
| + } |
| + |
| + mDialog.getWindow().setAttributes(layoutParams); |
| + } |
| + |
| + /** |
| + * Callbacks for finding out about the Dialog's Surface. |
| + * These happen on the looper thread. |
| + */ |
| + private class Callbacks implements SurfaceHolder.Callback2 { |
| + @Override |
| + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} |
| + |
| + @Override |
| + public void surfaceCreated(SurfaceHolder holder) { |
| + // Make sure that we haven't torn down the dialog yet. |
| + if (mDialog == null) return; |
| + |
| + try { |
| + mClientCallback.onCreated(holder.getSurface()); |
| + } catch (Exception e) { |
| + Log.e(TAG, "surfaceCreated: callback failed: " + e); |
| + enterDestroyedState(); |
| + } |
| + } |
| + |
| + @Override |
| + public void surfaceDestroyed(SurfaceHolder holder) { |
| + if (mDialog == null) return; |
| + |
| + enterDestroyedState(); |
| + } |
| + |
| + @Override |
| + public void surfaceRedrawNeeded(SurfaceHolder holder) {} |
| + } |
| + |
| + /** |
| + * Notify the client about surface destruction, and clean up. Multiple |
| + * calls to us do nothing. |
| + */ |
| + private void enterDestroyedState() { |
| + if (mDialog == null) return; |
| + |
| + try { |
| + mClientCallback.onDestroyed(); |
| + } catch (Exception e) { |
| + Log.e(TAG, "enterDestroyedState: callback failed: " + e); |
|
boliu
2017/01/04 01:48:44
all logs probably should be .d so they get strippe
liberato (no reviews please)
2017/01/11 22:17:57
Done.
|
| + release(); |
| + } |
| + |
| + // Dismiss the dialog, if needed |
| + // TODO(liberato): is it safe to call dismiss without show? we don't |
| + // know that we've shown the dialog yet. maybe if mToken? isShowing? |
| + mDialog.dismiss(); |
| + mDialog = null; |
| + mDialogCallbacks = null; |
| + mToken = null; |
| + } |
| + |
| + public void onWindowToken(IBinder token) { |
| + if (mDialog == null) return; |
| + |
| + if (token == null || (mToken != null && token != mToken)) { |
| + // We've lost the token, if we had one, or we got a new one. |
| + // Notify the client. |
| + enterDestroyedState(); |
| + return; |
| + } |
| + |
| + if (mToken == token) { |
| + // Same token, do nothing. |
|
boliu
2017/01/04 01:48:44
haven't read the window token stuff yet.. but is t
liberato (no reviews please)
2017/01/11 22:17:57
i don't think that it should, but it seemed harmle
boliu
2017/01/12 20:24:19
but this is silencing a potential problem in the f
|
| + return; |
| + } |
| + |
| + // We have a token, so layout the dialog. |
| + mToken = token; |
| + layoutSurface(mInitialX, mInitialY, mInitialWidth, mInitialHeight); |
|
boliu
2017/01/04 01:48:44
what if an layout happens before window token is r
liberato (no reviews please)
2017/01/11 22:17:57
it's okay, since the application shouldn't be posi
|
| + mDialogCallbacks = new Callbacks(); |
| + mDialog.getWindow().takeSurface(mDialogCallbacks); |
| + mDialog.show(); |
| + |
| + // We don't notify the client here. We'll wait until the Android |
| + // Surface is created. |
| + } |
| +} |