Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(772)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java

Issue 810853003: Upstream FullscreenManager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update comments Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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.chrome.browser.fullscreen;
6
7 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
8 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
9 import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
10
11 import android.content.res.Resources;
12 import android.os.Build;
13 import android.os.Bundle;
14 import android.os.Handler;
15 import android.os.Message;
16 import android.view.Gravity;
17 import android.view.View;
18 import android.view.View.OnLayoutChangeListener;
19 import android.view.Window;
20 import android.view.WindowManager;
21
22 import org.chromium.chrome.R;
23 import org.chromium.chrome.browser.widget.TextBubble;
24 import org.chromium.content.browser.ContentViewCore;
25
26 /**
27 * Handles updating the UI based on requests to the HTML Fullscreen API.
28 */
29 public class FullscreenHtmlApiHandler {
30 private static final int MSG_ID_HIDE_NOTIFICATION_BUBBLE = 1;
31 private static final int MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS = 2;
32 private static final int MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG = 3;
33
34 private static final int MAX_NOTIFICATION_DIMENSION_DP = 600;
35
36 private static final long NOTIFICATION_INITIAL_SHOW_DURATION_MS = 3500;
37 private static final long NOTIFICATION_SHOW_DURATION_MS = 2500;
38 // The time we allow the Android notification bar to be shown when it is req uested temporarily
39 // by the Android system (this value is additive on top of the show duration imposed by
40 // Android).
41 private static final long ANDROID_CONTROLS_SHOW_DURATION_MS = 200;
42 // Delay to allow a frame to render between getting the fullscreen layout up date and clearing
43 // the SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flag.
44 private static final long CLEAR_LAYOUT_FULLSCREEN_DELAY_MS = 20;
45
46 private static final int NOTIFICATION_BUBBLE_ALPHA = 252; // 255 * 0.99
47
48 private static boolean sFullscreenNotificationShown;
49
50 private final Window mWindow;
51 private final Handler mHandler;
52 private final FullscreenHtmlApiDelegate mDelegate;
53 private final int mNotificationMaxDimension;
54
55 private final boolean mPersistentFullscreenSupported;
56
57 private ContentViewCore mContentViewCoreInFullscreen;
58 private boolean mIsPersistentMode;
59
60 private TextBubble mNotificationBubble;
61 private OnLayoutChangeListener mFullscreenOnLayoutChangeListener;
62
63 /**
64 * Delegate that allows embedders to react to fullscreen API requests.
65 */
66 public interface FullscreenHtmlApiDelegate {
67 /**
68 * @return The Y offset to be applied to the fullscreen notification.
69 */
70 int getNotificationOffsetY();
71
72 /**
73 * @return The view that the fullscreen notification will be pinned to.
74 */
75 View getNotificationAnchorView();
76
77 /**
78 * Notifies the delegate that entering fullscreen has been requested and allows them
79 * to hide their controls.
80 * <p>
81 * Once the delegate has hidden the their controls, it must call
82 * {@link FullscreenHtmlApiHandler#enterFullscreen(ContentViewCore)}.
83 */
84 void onEnterFullscreen();
85
86 /**
87 * Cancels a pending enter fullscreen request if present.
88 * @return Whether the request was cancelled.
89 */
90 boolean cancelPendingEnterFullscreen();
91
92 /**
93 * Notifies the delegate that the window UI has fully exited fullscreen and gives
94 * the embedder a chance to update their controls.
95 *
96 * @param contentViewCore The ContentViewCore whose fullscreen is being exited.
97 */
98 void onFullscreenExited(ContentViewCore contentViewCore);
99
100 /**
101 * @return Whether the notification bubble should be shown. For fullscre en video in
102 * overlay mode, the notification bubble should be disabled.
103 */
104 boolean shouldShowNotificationBubble();
105 }
106
107 /**
108 * Constructs the handler that will manage the UI transitions from the HTML fullscreen API.
109 *
110 * @param window The window containing the view going to fullscreen.
111 * @param delegate The delegate that allows embedders to handle fullscreen t ransitions.
112 * @param persistentFullscreenSupported
113 */
114 public FullscreenHtmlApiHandler(Window window, FullscreenHtmlApiDelegate del egate,
115 boolean persistentFullscreenSupported) {
116 mWindow = window;
117 mDelegate = delegate;
118 mPersistentFullscreenSupported = persistentFullscreenSupported;
119
120 mHandler = new Handler() {
121 @Override
122 public void handleMessage(Message msg) {
123 if (msg == null) return;
124 switch (msg.what) {
125 case MSG_ID_HIDE_NOTIFICATION_BUBBLE:
126 hideNotificationBubble();
127 break;
128 case MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS: {
129 assert mIsPersistentMode : "Calling after we exited full screen";
130 final ContentViewCore contentViewCore = mContentViewCore InFullscreen;
131 if (contentViewCore == null) return;
132 final View contentView = contentViewCore.getContainerVie w();
133 int systemUiVisibility = contentView.getSystemUiVisibili ty();
134 if ((systemUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN)
135 == SYSTEM_UI_FLAG_FULLSCREEN) {
136 return;
137 }
138 systemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
139 systemUiVisibility |= SYSTEM_UI_FLAG_LOW_PROFILE;
140 contentView.setSystemUiVisibility(systemUiVisibility);
141
142 // Trigger a update to clear the SYSTEM_UI_FLAG_LAYOUT_F ULLSCREEN flag
143 // once the view has been laid out after this system UI update. Without
144 // clearing this flag, the keyboard appearing will not t rigger a relayout
145 // of the contents, which prevents updating the overdraw amount to the
146 // renderer.
147 contentView.addOnLayoutChangeListener(new OnLayoutChange Listener() {
148 @Override
149 public void onLayoutChange(View v, int left, int top , int right,
150 int bottom, int oldLeft, int oldTop, int old Right,
151 int oldBottom) {
152 sendEmptyMessageDelayed(MSG_ID_CLEAR_LAYOUT_FULL SCREEN_FLAG,
153 CLEAR_LAYOUT_FULLSCREEN_DELAY_MS);
154 contentView.removeOnLayoutChangeListener(this);
155 }
156 });
157 break;
158 }
159 case MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG: {
160 // Change this assert to simply ignoring the message to work around
161 // http://crbug/365638
162 // TODO(aberent): Fix bug
163 // assert mIsPersistentMode : "Calling after we exited f ullscreen";
164 if (!mIsPersistentMode) return;
165 if (mContentViewCoreInFullscreen == null) return;
166 final View view = mContentViewCoreInFullscreen.getContai nerView();
167 int systemUiVisibility = view.getSystemUiVisibility();
168 if ((systemUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCRE EN) == 0) {
169 return;
170 }
171 systemUiVisibility &= ~SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
172 view.setSystemUiVisibility(systemUiVisibility);
173 break;
174 }
175 default:
176 assert false : "Unexpected message for ID: " + msg.what;
177 break;
178 }
179 }
180 };
181
182 Resources resources = mWindow.getContext().getResources();
183 float density = resources.getDisplayMetrics().density;
184 mNotificationMaxDimension = (int) (density * MAX_NOTIFICATION_DIMENSION_ DP);
185 }
186
187 /**
188 * Enters or exits persistent fullscreen mode. In this mode, the top contro ls will be
189 * permanently hidden until this mode is exited.
190 *
191 * @param enabled Whether to enable persistent fullscreen mode.
192 */
193 public void setPersistentFullscreenMode(boolean enabled) {
194 if (!mPersistentFullscreenSupported) return;
195
196 if (mIsPersistentMode == enabled) return;
197
198 mIsPersistentMode = enabled;
199
200 if (mIsPersistentMode) {
201 mDelegate.onEnterFullscreen();
202 } else {
203 if (mContentViewCoreInFullscreen != null) {
204 exitFullscreen(mContentViewCoreInFullscreen);
205 } else {
206 if (!mDelegate.cancelPendingEnterFullscreen()) {
207 assert false : "No content view previously set to fullscreen .";
208 }
209 }
210 mContentViewCoreInFullscreen = null;
211 }
212 }
213
214 /**
215 * @return Whether the application is in persistent fullscreen mode.
216 * @see #setPersistentFullscreenMode(boolean)
217 */
218 public boolean getPersistentFullscreenMode() {
219 return mIsPersistentMode;
220 }
221
222 private void exitFullscreen(final ContentViewCore contentViewCore) {
223 final View contentView = contentViewCore.getContainerView();
224 hideNotificationBubble();
225 mHandler.removeMessages(MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS);
226 mHandler.removeMessages(MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG);
227
228 int systemUiVisibility = contentView.getSystemUiVisibility();
229 systemUiVisibility &= ~SYSTEM_UI_FLAG_LOW_PROFILE;
230 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
231 systemUiVisibility &= ~SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
232 systemUiVisibility &= ~SYSTEM_UI_FLAG_FULLSCREEN;
233 } else {
234 mWindow.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREE N);
235 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
236 }
237 contentView.setSystemUiVisibility(systemUiVisibility);
238 if (mFullscreenOnLayoutChangeListener != null) {
239 contentView.removeOnLayoutChangeListener(mFullscreenOnLayoutChangeLi stener);
240 }
241 mFullscreenOnLayoutChangeListener = new OnLayoutChangeListener() {
242 @Override
243 public void onLayoutChange(View v, int left, int top, int right, int bottom,
244 int oldLeft, int oldTop, int oldRight, int oldBottom) {
245 if ((bottom - top) < (oldBottom - oldTop)) {
246 mDelegate.onFullscreenExited(contentViewCore);
247 contentView.removeOnLayoutChangeListener(this);
248 }
249 }
250 };
251 contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener) ;
252 contentViewCore.getWebContents().exitFullscreen();
253 }
254
255 /**
256 * Handles hiding the system UI components to allow the content to take up t he full screen.
257 * @param contentViewCore The contentViewCore that is entering fullscreen.
258 */
259 public void enterFullscreen(final ContentViewCore contentViewCore) {
260 final View contentView = contentViewCore.getContainerView();
261 int systemUiVisibility = contentView.getSystemUiVisibility();
262 systemUiVisibility |= SYSTEM_UI_FLAG_LOW_PROFILE;
263 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
264 if ((systemUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
265 == SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) {
266 systemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
267 } else {
268 systemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
269 }
270 } else {
271 mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
272 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCR EEN);
273 }
274 if (mFullscreenOnLayoutChangeListener != null) {
275 contentView.removeOnLayoutChangeListener(mFullscreenOnLayoutChangeLi stener);
276 }
277 mFullscreenOnLayoutChangeListener = new OnLayoutChangeListener() {
278 @Override
279 public void onLayoutChange(View v, int left, int top, int right, int bottom,
280 int oldLeft, int oldTop, int oldRight, int oldBottom) {
281 // On certain sites playing embedded video (http://crbug.com/293 782), setting the
282 // SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN does not always trigger a vi ew-level layout
283 // with an updated height. To work around this, do not check fo r an increased
284 // height and always just trigger the next step of the fullscree n initialization.
285 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
286 // Posting the message to set the fullscreen flag because se tting it
287 // directly in the onLayoutChange would have no effect.
288 mHandler.sendEmptyMessage(MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FL AGS);
289 }
290
291 if ((bottom - top) <= (oldBottom - oldTop)) return;
292 if (mDelegate.shouldShowNotificationBubble()) {
293 showNotificationBubble(mWindow.getContext().getResources().g etString(
294 R.string.fullscreen_api_notification));
295 }
296 contentView.removeOnLayoutChangeListener(this);
297 }
298 };
299 contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener) ;
300 contentView.setSystemUiVisibility(systemUiVisibility);
301 mContentViewCoreInFullscreen = contentViewCore;
302 }
303
304 /**
305 * Creates (if necessary) and returns a notification bubble.
306 */
307 private TextBubble getOrCreateNotificationBubble() {
308 if (mNotificationBubble == null) {
309 Bundle b = new Bundle();
310 b.putBoolean(TextBubble.BACKGROUND_INTRINSIC_PADDING, true);
311 b.putBoolean(TextBubble.CENTER, true);
312 b.putBoolean(TextBubble.UP_DOWN, true);
313 b.putInt(TextBubble.TEXT_STYLE_ID, android.R.style.TextAppearance_De viceDefault_Medium);
314 b.putInt(TextBubble.ANIM_STYLE_ID, R.style.FullscreenNotificationBub ble);
315 mNotificationBubble = new TextBubble(mWindow.getContext(), b);
316 mNotificationBubble.getBubbleTextView().setGravity(Gravity.CENTER_HO RIZONTAL);
317 mNotificationBubble.getBackground().setAlpha(NOTIFICATION_BUBBLE_ALP HA);
318 mNotificationBubble.setTouchable(false);
319 }
320 return mNotificationBubble;
321 }
322
323 private void showNotificationBubble(String text) {
324 getOrCreateNotificationBubble().showTextBubble(text, mDelegate.getNotifi cationAnchorView(),
325 mNotificationMaxDimension, mNotificationMaxDimension);
326 updateBubblePosition();
327
328 mHandler.removeMessages(MSG_ID_HIDE_NOTIFICATION_BUBBLE);
329
330 long showDuration = NOTIFICATION_INITIAL_SHOW_DURATION_MS;
331 if (sFullscreenNotificationShown) {
332 showDuration = NOTIFICATION_SHOW_DURATION_MS;
333 }
334 sFullscreenNotificationShown = true;
335
336 mHandler.sendEmptyMessageDelayed(MSG_ID_HIDE_NOTIFICATION_BUBBLE, showDu ration);
337 }
338
339 /**
340 * Updates the position of the notification bubble based on the current offs et.
341 */
342 public void updateBubblePosition() {
343 if (mNotificationBubble != null && mNotificationBubble.isShowing()) {
344 mNotificationBubble.setOffsetY(mDelegate.getNotificationOffsetY());
345 }
346 }
347
348 /**
349 * Hides the notification bubble.
350 */
351 public void hideNotificationBubble() {
352 if (mNotificationBubble != null && mNotificationBubble.isShowing()) {
353 mNotificationBubble.dismiss();
354 }
355 }
356
357 /**
358 * Notified when the system UI visibility for the current ContentView has ch anged.
359 * @param visibility The updated UI visibility.
360 * @see View#getSystemUiVisibility()
361 */
362 public void onContentViewSystemUiVisibilityChange(int visibility) {
363 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return;
364
365 if (mContentViewCoreInFullscreen == null || !mIsPersistentMode) return;
366 mHandler.sendEmptyMessageDelayed(
367 MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS, ANDROID_CONTROLS_SHOW_DUR ATION_MS);
368 }
369
370 /**
371 * Ensure the proper system UI flags are set after the window regains focus.
372 * @see android.app.Activity#onWindowFocusChanged(boolean)
373 */
374 public void onWindowFocusChanged(boolean hasWindowFocus) {
375 if (!hasWindowFocus) hideNotificationBubble();
376 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return;
377
378 mHandler.removeMessages(MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS);
379 mHandler.removeMessages(MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG);
380 if (mContentViewCoreInFullscreen == null || !mIsPersistentMode || !hasWi ndowFocus) return;
381 mHandler.sendEmptyMessageDelayed(
382 MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS, ANDROID_CONTROLS_SHOW_DUR ATION_MS);
383 }
384 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698