Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.media; | |
| 6 | |
| 7 import android.annotation.SuppressLint; | |
| 8 import android.app.Dialog; | |
| 9 import android.content.Context; | |
| 10 import android.os.IBinder; | |
| 11 import android.view.Gravity; | |
| 12 import android.view.SurfaceHolder; | |
| 13 import android.view.Window; | |
| 14 import android.view.WindowManager; | |
| 15 | |
| 16 import org.chromium.base.Log; | |
| 17 | |
| 18 /** | |
| 19 * Core class for control of a single DialogSurface instance. Everything runs | |
| 20 * on a single thread, that's probably not the UI thread. | |
| 21 * | |
| 22 * Note that this does not implement IDialogSurface; we assume that, and the | |
| 23 * associated thread-hopping, is handled elsewhere. | |
| 24 */ | |
| 25 class DialogSurfaceCore { | |
| 26 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.
| |
| 27 | |
| 28 /** | |
| 29 * Call back into native with a message about our state. This can be called | |
| 30 * on any thread. It's okay if it refers to a native object that no longer | |
| 31 * exists; we can't really avoid it. The native side handles it. | |
| 32 * | |
| 33 * The callback cannot recursively call back into us; the IDialogSurface | |
| 34 * implementation must prevent that. | |
| 35 */ | |
| 36 private IDialogSurfaceCallback mClientCallback; | |
| 37 | |
| 38 // Callback operations. | |
| 39 // These must match dialog_surface.h . | |
| 40 private static final int OP_CREATED = 0; | |
| 41 private static final int OP_DESTROYED = 1; | |
| 42 | |
| 43 // When initialized via Init, we'll create mDialog. We'll clear it when | |
| 44 // we send SURFACE_DESTROYED to the client. In general, when this is null, | |
| 45 // either we haven't been initialized yet, or we've been torn down. It | |
| 46 // shouldn't be the case that anything calls methods after construction but | |
| 47 // before Init, though. | |
| 48 private Dialog mDialog; | |
| 49 | |
| 50 private Callbacks mDialogCallbacks; | |
| 51 private IBinder mToken; | |
| 52 | |
| 53 // Initial position and size. | |
| 54 private int mInitialX; | |
| 55 private int mInitialY; | |
| 56 private int mInitialWidth; | |
| 57 private int mInitialHeight; | |
| 58 | |
| 59 /** | |
| 60 * Construction may be called from a random thread, for simplicity. Call | |
| 61 * Init from the proper thread before doing anything else. | |
| 62 */ | |
| 63 public DialogSurfaceCore() {} | |
| 64 | |
| 65 /** | |
| 66 * Finish init on the proper thread. We'll use this thread for the Dialog | |
| 67 * Looper thread. | |
| 68 * @param context Context that we use. | |
| 69 * @param callback callback object to notify about state changes. | |
| 70 * @param x initial x position in chrome compositor (not screen) coords. | |
| 71 * @param y initial y position in chrome compositor (not screen) coords. | |
| 72 * @param width initial width. | |
| 73 * @param height initial height. | |
| 74 */ | |
| 75 public void initialize( | |
| 76 Context context, IDialogSurfaceCallback callback, int x, int y, int width, int height) { | |
| 77 mClientCallback = callback; | |
| 78 mInitialX = x; | |
| 79 mInitialY = y; | |
| 80 mInitialWidth = width; | |
| 81 mInitialHeight = height; | |
| 82 | |
| 83 // Create the dialog, but don't lay it out or show it yet. We'll do | |
| 84 // that when we get a window token. | |
| 85 mDialog = new Dialog(context, android.R.style.Theme_NoDisplay); | |
| 86 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); | |
| 87 mDialog.setCancelable(false); | |
| 88 } | |
| 89 | |
| 90 /** | |
| 91 * Release the underlying surface, and generally clean up, in response to | |
| 92 * the client releasing the IDialogSurface. | |
| 93 */ | |
| 94 public void release() { | |
| 95 // If we've not released the dialog yet, then do so. | |
| 96 if (mDialog != null) { | |
| 97 // TODO(liberato): is this safe if we haven't shown the dialog yet? | |
| 98 mDialog.dismiss(); | |
| 99 mDialog = null; | |
| 100 mDialogCallbacks = null; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 /** | |
| 105 * Layout the DialogSurface. If we don't have a token, then we ignore it, | |
| 106 * since a well-behaved client shouldn't call us before OP_CREATED anyway. | |
| 107 */ | |
| 108 @SuppressLint("RtlHardcoded") | |
| 109 public void layoutSurface(final int x, final int y, final int width, final i nt height) { | |
| 110 if (mDialog == null || mToken == null) return; | |
| 111 | |
| 112 // Rather than using getAttributes, we just create them from scratch. | |
| 113 // The default dialog attributes aren't what we want. | |
| 114 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.
| |
| 115 | |
| 116 // TODO(liberato): adjust for CompositorView screen location here if we | |
| 117 // want to support non-full screen use cases. | |
| 118 | |
| 119 layoutParams.x = x; | |
| 120 layoutParams.y = y; | |
| 121 layoutParams.width = width; | |
| 122 layoutParams.height = height; | |
| 123 layoutParams.token = mToken; | |
| 124 | |
| 125 // NOTE: we really do want LEFT here, since we're dealing in compositor | |
| 126 // coordinates. Those are always from the left. | |
| 127 layoutParams.gravity = Gravity.TOP | Gravity.LEFT; | |
| 128 | |
| 129 // Use a media surface, which is what SurfaceView uses by default. For | |
| 130 // debugging overlay drawing, consider using TYPE_APPLICATION_PANEL to | |
| 131 // move the dialog over the CompositorView. | |
| 132 layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; | |
| 133 | |
| 134 layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | |
| 135 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | |
| 136 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | |
| 137 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; | |
| 138 | |
| 139 // Don't set FLAG_SCALED. in addition to not being sure what it does | |
| 140 // (SV uses it), it also causes a crash in WindowManager when we hide | |
| 141 // (not dismiss), navigate, and/or exit the app without hide/dismiss. | |
| 142 // There's a missing null check in WindowManagerService.java@3170 | |
| 143 // on M MR2. To repro, change dimiss() to hide(), bring up a SV, and | |
| 144 // navigate away or press home. | |
| 145 | |
| 146 // Turn off the position animation, so that it doesn't animate from one | |
| 147 // position to the next. Ignore errors. | |
| 148 try { | |
| 149 int currentFlags = | |
| 150 (Integer) layoutParams.getClass().getField("privateFlags").g et(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
| |
| 151 layoutParams.getClass() | |
| 152 .getField("privateFlags") | |
| 153 .set(layoutParams, currentFlags | 0x00000040); | |
| 154 // It would be nice to just catch Exception, but findbugs doesn't | |
| 155 // allow it. If we cannot set the flag, then that's okay too. | |
| 156 } catch (NoSuchFieldException e) { | |
| 157 } catch (NullPointerException e) { | |
| 158 } catch (SecurityException e) { | |
| 159 } catch (IllegalAccessException e) { | |
| 160 } catch (IllegalArgumentException e) { | |
| 161 } catch (ExceptionInInitializerError e) { | |
| 162 } | |
| 163 | |
| 164 mDialog.getWindow().setAttributes(layoutParams); | |
| 165 } | |
| 166 | |
| 167 /** | |
| 168 * Callbacks for finding out about the Dialog's Surface. | |
| 169 * These happen on the looper thread. | |
| 170 */ | |
| 171 private class Callbacks implements SurfaceHolder.Callback2 { | |
| 172 @Override | |
| 173 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} | |
| 174 | |
| 175 @Override | |
| 176 public void surfaceCreated(SurfaceHolder holder) { | |
| 177 // Make sure that we haven't torn down the dialog yet. | |
| 178 if (mDialog == null) return; | |
| 179 | |
| 180 try { | |
| 181 mClientCallback.onCreated(holder.getSurface()); | |
| 182 } catch (Exception e) { | |
| 183 Log.e(TAG, "surfaceCreated: callback failed: " + e); | |
| 184 enterDestroyedState(); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 @Override | |
| 189 public void surfaceDestroyed(SurfaceHolder holder) { | |
| 190 if (mDialog == null) return; | |
| 191 | |
| 192 enterDestroyedState(); | |
| 193 } | |
| 194 | |
| 195 @Override | |
| 196 public void surfaceRedrawNeeded(SurfaceHolder holder) {} | |
| 197 } | |
| 198 | |
| 199 /** | |
| 200 * Notify the client about surface destruction, and clean up. Multiple | |
| 201 * calls to us do nothing. | |
| 202 */ | |
| 203 private void enterDestroyedState() { | |
| 204 if (mDialog == null) return; | |
| 205 | |
| 206 try { | |
| 207 mClientCallback.onDestroyed(); | |
| 208 } catch (Exception e) { | |
| 209 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.
| |
| 210 release(); | |
| 211 } | |
| 212 | |
| 213 // Dismiss the dialog, if needed | |
| 214 // TODO(liberato): is it safe to call dismiss without show? we don't | |
| 215 // know that we've shown the dialog yet. maybe if mToken? isShowing? | |
| 216 mDialog.dismiss(); | |
| 217 mDialog = null; | |
| 218 mDialogCallbacks = null; | |
| 219 mToken = null; | |
| 220 } | |
| 221 | |
| 222 public void onWindowToken(IBinder token) { | |
| 223 if (mDialog == null) return; | |
| 224 | |
| 225 if (token == null || (mToken != null && token != mToken)) { | |
| 226 // We've lost the token, if we had one, or we got a new one. | |
| 227 // Notify the client. | |
| 228 enterDestroyedState(); | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 if (mToken == token) { | |
| 233 // 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
| |
| 234 return; | |
| 235 } | |
| 236 | |
| 237 // We have a token, so layout the dialog. | |
| 238 mToken = token; | |
| 239 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
| |
| 240 mDialogCallbacks = new Callbacks(); | |
| 241 mDialog.getWindow().takeSurface(mDialogCallbacks); | |
| 242 mDialog.show(); | |
| 243 | |
| 244 // We don't notify the client here. We'll wait until the Android | |
| 245 // Surface is created. | |
| 246 } | |
| 247 } | |
| OLD | NEW |