OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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.content.browser.androidoverlay; | |
6 | |
7 import android.annotation.SuppressLint; | |
8 import android.app.Dialog; | |
9 import android.os.IBinder; | |
10 import android.view.Gravity; | |
11 import android.view.Surface; | |
12 import android.view.SurfaceHolder; | |
13 import android.view.WindowManager; | |
14 | |
15 import org.chromium.gfx.mojom.Rect; | |
16 import org.chromium.media.mojom.AndroidOverlayConfig; | |
17 | |
18 /** | |
19 * Core class for control of a single Dialog-based AndroidOverlay instance. Eve rything runs on the | |
20 * overlay-ui thread, which is not the Browser UI thread. | |
21 * | |
22 * Note that this does not implement AndroidOverlay; we assume that, and the ass ociated thread- | |
23 * hopping, is handled elsewhere (DialogOverlayImpl). | |
24 */ | |
25 class DialogOverlayCore { | |
26 private static final String TAG = "DSCore"; | |
27 | |
28 // Host interface, since we're on the wrong thread to talk to mojo, or anyth ing else, really. | |
29 public interface Host { | |
30 // Notify the host that we have a surface. | |
31 void onSurfaceReady(Surface surface); | |
32 | |
33 // Notify the host that we have failed to get a surface or the surface w as destroyed. | |
34 void onOverlayDestroyed(); | |
35 | |
36 // Wait until the host has been told to clean up. We are allowed to let surfaceDestroyed | |
37 // proceed once this happens. | |
38 void waitForCleanup(); | |
39 } | |
40 | |
41 Host mHost; | |
boliu
2017/03/30 22:42:02
private?
liberato (no reviews please)
2017/04/04 17:49:29
Done.
| |
42 | |
43 // When initialized via Init, we'll create mDialog. We'll clear it when we send | |
44 // onOverlayDestroyed to the host. In general, when this is null, either we haven't been | |
45 // initialized yet, or we've been torn down. It shouldn't be the case that anything calls | |
46 // methods after construction but before |initialize()|, though. | |
47 private Dialog mDialog; | |
48 | |
49 private Callbacks mDialogCallbacks; | |
50 | |
51 // Most recent layout parameters. | |
52 private WindowManager.LayoutParams mLayoutParams; | |
53 | |
54 /** | |
55 * Construction may be called from a random thread, for simplicity. Call in itialize from the | |
56 * proper thread before doing anything else. | |
57 */ | |
58 public DialogOverlayCore() {} | |
59 | |
60 /** | |
61 * Finish init on the proper thread. We'll use this thread for the Dialog L ooper thread. | |
62 * @param dialog the dialog, which uses our current thread as the UI thread. | |
63 * @param config initial config. | |
64 * @param host host interface, for sending messages that (probably) need to thread hop. | |
65 */ | |
66 public void initialize(Dialog dialog, AndroidOverlayConfig config, Host host ) { | |
67 mHost = host; | |
68 mLayoutParams = createLayoutParams(config.secure); | |
69 layoutSurface(config.rect); | |
70 | |
71 // Set this after calling layoutSurface(), so that it doesn't try to use it. We haven't | |
72 // shown |dialog| yet! | |
73 mDialog = dialog; | |
74 } | |
75 | |
76 /** | |
77 * Release the underlying surface, and generally clean up, in response to | |
78 * the client releasing the AndroidOverlay. | |
79 */ | |
80 public void release() { | |
81 // If we've not released the dialog yet, then do so. | |
82 if (mDialog != null) { | |
83 if (mDialog.isShowing()) mDialog.dismiss(); | |
84 mDialog = null; | |
85 mDialogCallbacks = null; | |
86 } | |
87 | |
88 mLayoutParams.token = null; | |
89 | |
90 // We don't bother to notify |mHost| that we've been destroyed; it told us. | |
91 mHost = null; | |
92 } | |
93 | |
94 /** | |
95 * Layout the AndroidOverlay. If we don't have a token, then we ignore it, since a well-behaved | |
96 * client shouldn't call us before getting the surface anyway. | |
97 */ | |
98 public void layoutSurface(final Rect rect) { | |
99 if (mDialog == null || mLayoutParams.token == null) return; | |
100 | |
101 // TODO(liberato): adjust for CompositorView screen location here if we want to support | |
102 // non-full screen use cases. | |
103 mLayoutParams.x = rect.x; | |
104 mLayoutParams.y = rect.y; | |
105 mLayoutParams.width = rect.width; | |
106 mLayoutParams.height = rect.height; | |
107 | |
108 mDialog.getWindow().setAttributes(mLayoutParams); | |
109 } | |
110 | |
111 /** | |
112 * Callbacks for finding out about the Dialog's Surface. | |
113 * These happen on the looper thread. | |
114 */ | |
115 private class Callbacks implements SurfaceHolder.Callback2 { | |
116 @Override | |
117 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} | |
118 | |
119 @Override | |
120 public void surfaceCreated(SurfaceHolder holder) { | |
121 // Make sure that we haven't torn down the dialog yet. | |
122 if (mDialog == null) return; | |
123 | |
124 if (mHost != null) mHost.onSurfaceReady(holder.getSurface()); | |
125 } | |
126 | |
127 @Override | |
128 public void surfaceDestroyed(SurfaceHolder holder) { | |
129 if (mDialog == null || mHost == null) return; | |
130 | |
131 // Notify the host that we've been destroyed, and wait for it to cle an up. | |
132 mHost.onOverlayDestroyed(); | |
133 mHost.waitForCleanup(); | |
134 mHost = null; | |
135 } | |
136 | |
137 @Override | |
138 public void surfaceRedrawNeeded(SurfaceHolder holder) {} | |
139 } | |
140 | |
141 public void onWindowToken(IBinder token) { | |
142 if (mDialog == null || mHost == null) return; | |
143 | |
144 if (token == null || (mLayoutParams.token != null && token != mLayoutPar ams.token)) { | |
145 // We've lost the token, if we had one, or we got a new one. | |
146 // Notify the client. | |
147 mHost.onOverlayDestroyed(); | |
148 mHost = null; | |
149 if (mDialog.isShowing()) mDialog.dismiss(); | |
150 return; | |
151 } | |
152 | |
153 if (mLayoutParams.token == token) { | |
154 // Same token, do nothing. | |
155 return; | |
156 } | |
157 | |
158 // We have a token, so layout the dialog. | |
159 mLayoutParams.token = token; | |
160 mDialog.getWindow().setAttributes(mLayoutParams); | |
161 mDialogCallbacks = new Callbacks(); | |
162 mDialog.getWindow().takeSurface(mDialogCallbacks); | |
163 mDialog.show(); | |
164 | |
165 // We don't notify the client here. We'll wait until the Android Surfac e is created. | |
166 } | |
167 | |
168 @SuppressLint("RtlHardcoded") | |
169 private WindowManager.LayoutParams createLayoutParams(boolean secure) { | |
170 // Rather than using getAttributes, we just create them from scratch. | |
171 // The default dialog attributes aren't what we want. | |
172 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams (); | |
173 | |
174 // NOTE: we really do want LEFT here, since we're dealing in compositor | |
175 // coordinates. Those are always from the left. | |
176 layoutParams.gravity = Gravity.TOP | Gravity.LEFT; | |
177 | |
178 // Use a media surface, which is what SurfaceView uses by default. For | |
179 // debugging overlay drawing, consider using TYPE_APPLICATION_PANEL to | |
180 // move the dialog over the CompositorView. | |
181 layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; | |
182 | |
183 layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | |
184 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | |
185 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | |
186 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; | |
187 | |
188 if (secure) { | |
189 layoutParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; | |
190 } | |
191 | |
192 // Don't set FLAG_SCALED. in addition to not being sure what it does | |
193 // (SV uses it), it also causes a crash in WindowManager when we hide | |
194 // (not dismiss), navigate, and/or exit the app without hide/dismiss. | |
195 // There's a missing null check in WindowManagerService.java@3170 | |
196 // on M MR2. To repro, change dimiss() to hide(), bring up a SV, and | |
197 // navigate away or press home. | |
198 | |
199 // Turn off the position animation, so that it doesn't animate from one | |
200 // position to the next. Ignore errors. | |
201 // 0x40 is PRIVATE_FLAG_NO_MOVE_ANIMATION. | |
202 try { | |
203 int currentFlags = | |
204 (Integer) layoutParams.getClass().getField("privateFlags").g et(layoutParams); | |
205 layoutParams.getClass() | |
206 .getField("privateFlags") | |
207 .set(layoutParams, currentFlags | 0x00000040); | |
208 // It would be nice to just catch Exception, but findbugs doesn't | |
209 // allow it. If we cannot set the flag, then that's okay too. | |
210 } catch (NoSuchFieldException e) { | |
211 } catch (NullPointerException e) { | |
212 } catch (SecurityException e) { | |
213 } catch (IllegalAccessException e) { | |
214 } catch (IllegalArgumentException e) { | |
215 } catch (ExceptionInInitializerError e) { | |
216 } | |
217 | |
218 return layoutParams; | |
219 } | |
220 } | |
OLD | NEW |