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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f436cf20242aeff1a0f9b53a88267999cb0a0d7
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
@@ -0,0 +1,384 @@
+// Copyright 2014 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.chrome.browser.fullscreen;
+
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
+
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.view.Window;
+import android.view.WindowManager;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.widget.TextBubble;
+import org.chromium.content.browser.ContentViewCore;
+
+/**
+ * Handles updating the UI based on requests to the HTML Fullscreen API.
+ */
+public class FullscreenHtmlApiHandler {
+ private static final int MSG_ID_HIDE_NOTIFICATION_BUBBLE = 1;
+ private static final int MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS = 2;
+ private static final int MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG = 3;
+
+ private static final int MAX_NOTIFICATION_DIMENSION_DP = 600;
+
+ private static final long NOTIFICATION_INITIAL_SHOW_DURATION_MS = 3500;
+ private static final long NOTIFICATION_SHOW_DURATION_MS = 2500;
+ // The time we allow the Android notification bar to be shown when it is requested temporarily
+ // by the Android system (this value is additive on top of the show duration imposed by
+ // Android).
+ private static final long ANDROID_CONTROLS_SHOW_DURATION_MS = 200;
+ // Delay to allow a frame to render between getting the fullscreen layout update and clearing
+ // the SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flag.
+ private static final long CLEAR_LAYOUT_FULLSCREEN_DELAY_MS = 20;
+
+ private static final int NOTIFICATION_BUBBLE_ALPHA = 252; // 255 * 0.99
+
+ private static boolean sFullscreenNotificationShown;
+
+ private final Window mWindow;
+ private final Handler mHandler;
+ private final FullscreenHtmlApiDelegate mDelegate;
+ private final int mNotificationMaxDimension;
+
+ private final boolean mPersistentFullscreenSupported;
+
+ private ContentViewCore mContentViewCoreInFullscreen;
+ private boolean mIsPersistentMode;
+
+ private TextBubble mNotificationBubble;
+ private OnLayoutChangeListener mFullscreenOnLayoutChangeListener;
+
+ /**
+ * Delegate that allows embedders to react to fullscreen API requests.
+ */
+ public interface FullscreenHtmlApiDelegate {
+ /**
+ * @return The Y offset to be applied to the fullscreen notification.
+ */
+ int getNotificationOffsetY();
+
+ /**
+ * @return The view that the fullscreen notification will be pinned to.
+ */
+ View getNotificationAnchorView();
+
+ /**
+ * Notifies the delegate that entering fullscreen has been requested and allows them
+ * to hide their controls.
+ * <p>
+ * Once the delegate has hidden the their controls, it must call
+ * {@link FullscreenHtmlApiHandler#enterFullscreen(ContentViewCore)}.
+ */
+ void onEnterFullscreen();
+
+ /**
+ * Cancels a pending enter fullscreen request if present.
+ * @return Whether the request was cancelled.
+ */
+ boolean cancelPendingEnterFullscreen();
+
+ /**
+ * Notifies the delegate that the window UI has fully exited fullscreen and gives
+ * the embedder a chance to update their controls.
+ *
+ * @param contentViewCore The ContentViewCore whose fullscreen is being exited.
+ */
+ void onFullscreenExited(ContentViewCore contentViewCore);
+
+ /**
+ * @return Whether the notification bubble should be shown. For fullscreen video in
+ * overlay mode, the notification bubble should be disabled.
+ */
+ boolean shouldShowNotificationBubble();
+ }
+
+ /**
+ * Constructs the handler that will manage the UI transitions from the HTML fullscreen API.
+ *
+ * @param window The window containing the view going to fullscreen.
+ * @param delegate The delegate that allows embedders to handle fullscreen transitions.
+ * @param persistentFullscreenSupported
+ */
+ public FullscreenHtmlApiHandler(Window window, FullscreenHtmlApiDelegate delegate,
+ boolean persistentFullscreenSupported) {
+ mWindow = window;
+ mDelegate = delegate;
+ mPersistentFullscreenSupported = persistentFullscreenSupported;
+
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg == null) return;
+ switch (msg.what) {
+ case MSG_ID_HIDE_NOTIFICATION_BUBBLE:
+ hideNotificationBubble();
+ break;
+ case MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS: {
+ assert mIsPersistentMode : "Calling after we exited fullscreen";
+ final ContentViewCore contentViewCore = mContentViewCoreInFullscreen;
+ if (contentViewCore == null) return;
+ final View contentView = contentViewCore.getContainerView();
+ int systemUiVisibility = contentView.getSystemUiVisibility();
+ if ((systemUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN)
+ == SYSTEM_UI_FLAG_FULLSCREEN) {
+ return;
+ }
+ systemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ systemUiVisibility |= SYSTEM_UI_FLAG_LOW_PROFILE;
+ contentView.setSystemUiVisibility(systemUiVisibility);
+
+ // Trigger a update to clear the SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flag
+ // once the view has been laid out after this system UI update. Without
+ // clearing this flag, the keyboard appearing will not trigger a relayout
+ // of the contents, which prevents updating the overdraw amount to the
+ // renderer.
+ contentView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight,
+ int oldBottom) {
+ sendEmptyMessageDelayed(MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG,
+ CLEAR_LAYOUT_FULLSCREEN_DELAY_MS);
+ contentView.removeOnLayoutChangeListener(this);
+ }
+ });
+ break;
+ }
+ case MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG: {
+ // Change this assert to simply ignoring the message to work around
+ // http://crbug/365638
+ // TODO(aberent): Fix bug
+ // assert mIsPersistentMode : "Calling after we exited fullscreen";
+ if (!mIsPersistentMode) return;
+ if (mContentViewCoreInFullscreen == null) return;
+ final View view = mContentViewCoreInFullscreen.getContainerView();
+ int systemUiVisibility = view.getSystemUiVisibility();
+ if ((systemUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0) {
+ return;
+ }
+ systemUiVisibility &= ~SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ view.setSystemUiVisibility(systemUiVisibility);
+ break;
+ }
+ default:
+ assert false : "Unexpected message for ID: " + msg.what;
+ break;
+ }
+ }
+ };
+
+ Resources resources = mWindow.getContext().getResources();
+ float density = resources.getDisplayMetrics().density;
+ mNotificationMaxDimension = (int) (density * MAX_NOTIFICATION_DIMENSION_DP);
+ }
+
+ /**
+ * Enters or exits persistent fullscreen mode. In this mode, the top controls will be
+ * permanently hidden until this mode is exited.
+ *
+ * @param enabled Whether to enable persistent fullscreen mode.
+ */
+ public void setPersistentFullscreenMode(boolean enabled) {
+ if (!mPersistentFullscreenSupported) return;
+
+ if (mIsPersistentMode == enabled) return;
+
+ mIsPersistentMode = enabled;
+
+ if (mIsPersistentMode) {
+ mDelegate.onEnterFullscreen();
+ } else {
+ if (mContentViewCoreInFullscreen != null) {
+ exitFullscreen(mContentViewCoreInFullscreen);
+ } else {
+ if (!mDelegate.cancelPendingEnterFullscreen()) {
+ assert false : "No content view previously set to fullscreen.";
+ }
+ }
+ mContentViewCoreInFullscreen = null;
+ }
+ }
+
+ /**
+ * @return Whether the application is in persistent fullscreen mode.
+ * @see #setPersistentFullscreenMode(boolean)
+ */
+ public boolean getPersistentFullscreenMode() {
+ return mIsPersistentMode;
+ }
+
+ private void exitFullscreen(final ContentViewCore contentViewCore) {
+ final View contentView = contentViewCore.getContainerView();
+ hideNotificationBubble();
+ mHandler.removeMessages(MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS);
+ mHandler.removeMessages(MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG);
+
+ int systemUiVisibility = contentView.getSystemUiVisibility();
+ systemUiVisibility &= ~SYSTEM_UI_FLAG_LOW_PROFILE;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ systemUiVisibility &= ~SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ systemUiVisibility &= ~SYSTEM_UI_FLAG_FULLSCREEN;
+ } else {
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+ contentView.setSystemUiVisibility(systemUiVisibility);
+ if (mFullscreenOnLayoutChangeListener != null) {
+ contentView.removeOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
+ }
+ mFullscreenOnLayoutChangeListener = new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if ((bottom - top) < (oldBottom - oldTop)) {
+ mDelegate.onFullscreenExited(contentViewCore);
+ contentView.removeOnLayoutChangeListener(this);
+ }
+ }
+ };
+ contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
+ contentViewCore.getWebContents().exitFullscreen();
+ }
+
+ /**
+ * Handles hiding the system UI components to allow the content to take up the full screen.
+ * @param contentViewCore The contentViewCore that is entering fullscreen.
+ */
+ public void enterFullscreen(final ContentViewCore contentViewCore) {
+ final View contentView = contentViewCore.getContainerView();
+ int systemUiVisibility = contentView.getSystemUiVisibility();
+ systemUiVisibility |= SYSTEM_UI_FLAG_LOW_PROFILE;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ if ((systemUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ == SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) {
+ systemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ } else {
+ systemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ }
+ } else {
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+ }
+ if (mFullscreenOnLayoutChangeListener != null) {
+ contentView.removeOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
+ }
+ mFullscreenOnLayoutChangeListener = new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ // On certain sites playing embedded video (http://crbug.com/293782), setting the
+ // SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN does not always trigger a view-level layout
+ // with an updated height. To work around this, do not check for an increased
+ // height and always just trigger the next step of the fullscreen initialization.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ // Posting the message to set the fullscreen flag because setting it
+ // directly in the onLayoutChange would have no effect.
+ mHandler.sendEmptyMessage(MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS);
+ }
+
+ if ((bottom - top) <= (oldBottom - oldTop)) return;
+ if (mDelegate.shouldShowNotificationBubble()) {
+ showNotificationBubble(mWindow.getContext().getResources().getString(
+ R.string.fullscreen_api_notification));
+ }
+ contentView.removeOnLayoutChangeListener(this);
+ }
+ };
+ contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
+ contentView.setSystemUiVisibility(systemUiVisibility);
+ mContentViewCoreInFullscreen = contentViewCore;
+ }
+
+ /**
+ * Creates (if necessary) and returns a notification bubble.
+ */
+ private TextBubble getOrCreateNotificationBubble() {
+ if (mNotificationBubble == null) {
+ Bundle b = new Bundle();
+ b.putBoolean(TextBubble.BACKGROUND_INTRINSIC_PADDING, true);
+ b.putBoolean(TextBubble.CENTER, true);
+ b.putBoolean(TextBubble.UP_DOWN, true);
+ b.putInt(TextBubble.TEXT_STYLE_ID, android.R.style.TextAppearance_DeviceDefault_Medium);
+ b.putInt(TextBubble.ANIM_STYLE_ID, R.style.FullscreenNotificationBubble);
+ mNotificationBubble = new TextBubble(mWindow.getContext(), b);
+ mNotificationBubble.getBubbleTextView().setGravity(Gravity.CENTER_HORIZONTAL);
+ mNotificationBubble.getBackground().setAlpha(NOTIFICATION_BUBBLE_ALPHA);
+ mNotificationBubble.setTouchable(false);
+ }
+ return mNotificationBubble;
+ }
+
+ private void showNotificationBubble(String text) {
+ getOrCreateNotificationBubble().showTextBubble(text, mDelegate.getNotificationAnchorView(),
+ mNotificationMaxDimension, mNotificationMaxDimension);
+ updateBubblePosition();
+
+ mHandler.removeMessages(MSG_ID_HIDE_NOTIFICATION_BUBBLE);
+
+ long showDuration = NOTIFICATION_INITIAL_SHOW_DURATION_MS;
+ if (sFullscreenNotificationShown) {
+ showDuration = NOTIFICATION_SHOW_DURATION_MS;
+ }
+ sFullscreenNotificationShown = true;
+
+ mHandler.sendEmptyMessageDelayed(MSG_ID_HIDE_NOTIFICATION_BUBBLE, showDuration);
+ }
+
+ /**
+ * Updates the position of the notification bubble based on the current offset.
+ */
+ public void updateBubblePosition() {
+ if (mNotificationBubble != null && mNotificationBubble.isShowing()) {
+ mNotificationBubble.setOffsetY(mDelegate.getNotificationOffsetY());
+ }
+ }
+
+ /**
+ * Hides the notification bubble.
+ */
+ public void hideNotificationBubble() {
+ if (mNotificationBubble != null && mNotificationBubble.isShowing()) {
+ mNotificationBubble.dismiss();
+ }
+ }
+
+ /**
+ * Notified when the system UI visibility for the current ContentView has changed.
+ * @param visibility The updated UI visibility.
+ * @see View#getSystemUiVisibility()
+ */
+ public void onContentViewSystemUiVisibilityChange(int visibility) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return;
+
+ if (mContentViewCoreInFullscreen == null || !mIsPersistentMode) return;
+ mHandler.sendEmptyMessageDelayed(
+ MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS, ANDROID_CONTROLS_SHOW_DURATION_MS);
+ }
+
+ /**
+ * Ensure the proper system UI flags are set after the window regains focus.
+ * @see android.app.Activity#onWindowFocusChanged(boolean)
+ */
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ if (!hasWindowFocus) hideNotificationBubble();
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return;
+
+ mHandler.removeMessages(MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS);
+ mHandler.removeMessages(MSG_ID_CLEAR_LAYOUT_FULLSCREEN_FLAG);
+ if (mContentViewCoreInFullscreen == null || !mIsPersistentMode || !hasWindowFocus) return;
+ mHandler.sendEmptyMessageDelayed(
+ MSG_ID_SET_FULLSCREEN_SYSTEM_UI_FLAGS, ANDROID_CONTROLS_SHOW_DURATION_MS);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698