Index: chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java |
index 536d6516e5de63a960f45cdfcd1c46fb7f79b954..5a624d8de50902ae557299238d35bdbb7d0f82c7 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java |
@@ -56,6 +56,7 @@ import org.chromium.chrome.browser.tab.Tab; |
import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
import org.chromium.chrome.browser.util.IntentUtils; |
import org.chromium.chrome.browser.webapps.WebappActivity; |
+import org.chromium.ui.UiUtils; |
import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
@@ -115,6 +116,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
private static VrShellDelegate sInstance; |
private static VrBroadcastReceiver sVrBroadcastReceiver; |
private static boolean sRegisteredDaydreamHook = false; |
+ private static View sBlackOverlayView; |
private ChromeActivity mActivity; |
@@ -169,8 +171,6 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
// presentation experience. |
private boolean mVrBrowserUsed; |
- private View mOverlayView; |
- |
private final VSyncEstimator mVSyncEstimator; |
private static final class VrBroadcastReceiver extends BroadcastReceiver { |
@@ -197,7 +197,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
// resuming the Activity, see the comment about the custom animation below. |
// However, if we're already in VR (in one of the cases where we chose not to exit |
// VR before the DON flow), we don't need to add the overlay. |
- if (!sInstance.mInVr) sInstance.addOverlayView(); |
+ if (!sInstance.mInVr) addBlackOverlayViewForActivity(sInstance.mActivity); |
sInstance.mNeedsAnimationCancel = !sInstance.mInVr; |
// We start the Activity with a custom animation that keeps it hidden while starting |
@@ -241,8 +241,9 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
VrClassesWrapper wrapper = getVrClassesWrapper(); |
if (wrapper == null) return; |
nativeOnLibraryAvailable(); |
- if (sInstance != null && sInstance.mAutopresentWebVr) { |
- sInstance.enterVr(false); |
+ |
+ if (sInstance != null) { |
+ sInstance.cancelStartupAnimationIfNeeded(); |
} |
} |
@@ -787,8 +788,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
maybeSetPresentResult(true, donSuceeded); |
mVrShell.getContainer().setOnSystemUiVisibilityChangeListener(this); |
- removeOverlayView(); |
- |
+ removeBlackOverlayView(); |
if (!donSuceeded && !mAutopresentWebVr && isDaydreamCurrentViewer()) { |
// TODO(mthiesse): This is a VERY dirty hack. We need to know whether or not entering VR |
// will trigger the DON flow, so that we can wait for it to complete before we let the |
@@ -808,28 +808,86 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
} |
} |
+ private static void addBlackOverlayViewForActivity(ChromeActivity activity) { |
+ if (sBlackOverlayView != null) return; |
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( |
+ WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); |
+ sBlackOverlayView = new View(activity); |
+ sBlackOverlayView.setBackgroundColor(Color.BLACK); |
+ activity.getWindow().addContentView(sBlackOverlayView, params); |
+ } |
+ |
+ private static void removeBlackOverlayView() { |
+ if (sBlackOverlayView != null) UiUtils.removeViewFromParent(sBlackOverlayView); |
+ sBlackOverlayView = null; |
+ } |
+ |
+ private static boolean isTrustedDaydreamIntent(Intent intent) { |
+ return isVrIntent(intent) |
+ && IntentHandler.isIntentFromTrustedApp(intent, DAYDREAM_HOME_PACKAGE); |
+ } |
+ |
private void onAutopresentIntent() { |
// Autopresent intents are only expected from trusted first party apps while |
// we're not in vr. |
assert !mInVr; |
- mAutopresentWebVr = true; |
mDonSucceeded = true; |
+ mAutopresentWebVr = true; |
+ } |
+ |
+ private void onVrIntent() { |
+ // We assume that when we get a VR intent, we're in the headset. |
+ mNeedsAnimationCancel = true; |
} |
/** |
* This is called every time ChromeActivity gets a new intent. |
*/ |
- public static void onNewIntent(ChromeActivity activity, Intent intent) { |
- if (IntentUtils.safeGetBooleanExtra(intent, DAYDREAM_VR_EXTRA, false) |
+ public static void onNewIntentWithNative(ChromeActivity activity, Intent intent) { |
+ if (!isVrIntent(intent) || !activitySupportsVrBrowsing(activity)) return; |
+ VrShellDelegate instance = getInstance(activity); |
+ if (instance == null) return; |
+ instance.onVrIntent(); |
+ if (isTrustedDaydreamIntent(intent) |
&& ChromeFeatureList.isEnabled(ChromeFeatureList.WEBVR_AUTOPRESENT) |
- && activitySupportsAutopresentation(activity) |
- && IntentHandler.isIntentFromTrustedApp(intent, DAYDREAM_HOME_PACKAGE)) { |
- VrShellDelegate instance = getInstance(activity); |
- if (instance == null) return; |
+ && activitySupportsAutopresentation(activity)) { |
instance.onAutopresentIntent(); |
} |
} |
+ /** |
+ * This is called when ChromeTabbedActivity gets a new intent before native is initialized. |
+ */ |
+ public static void maybeHandleVrIntentPreNative(ChromeActivity activity, Intent intent) { |
+ if (isTrustedDaydreamIntent(intent)) { |
+ // We add a black overlay view so that we can show black while the VR UI is loading. |
+ // Note that this alone isn't sufficient to prevent 2D UI from showing when |
+ // auto-presenting WebVR. See comment about the custom animation in {@link |
+ // getVrIntentOptions}. |
+ addBlackOverlayViewForActivity(activity); |
+ } |
+ } |
+ |
+ /** |
+ * @return Whether or not the given intent is a VR-specific intent. |
+ */ |
+ public static boolean isVrIntent(Intent intent) { |
+ return IntentUtils.safeGetBooleanExtra(intent, DAYDREAM_VR_EXTRA, false); |
+ } |
+ |
+ /** |
+ * @return Options that a VR-specific Chrome activity should be launched with. |
+ */ |
+ public static Bundle getVrIntentOptions(Context context) { |
+ // These options are used to start the Activity with a custom animation to keep it hidden |
+ // for a few hundread milliseconds - enough time for us to draw the first black view. |
+ // The animation is sufficient to hide the 2D screenshot but not to the 2D UI while the |
+ // WebVR page is being loaded because the animation is somehow cancelled when we try to |
+ // enter VR (I don't know what's cancelling it). To hide the 2D UI, we resort to the black |
+ // overlay view added in {@link startWithVrIntentPreNative}. |
+ return ActivityOptions.makeCustomAnimation(context, R.anim.stay_hidden, 0).toBundle(); |
+ } |
+ |
@Override |
public void onSystemUiVisibilityChange(int visibility) { |
if (mInVr && !isWindowModeCorrectForVr()) { |
@@ -931,15 +989,18 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
return true; |
} |
+ private boolean cancelStartupAnimationIfNeeded() { |
+ if (!mNeedsAnimationCancel) return false; |
+ mCancellingEntryAnimation = true; |
+ Bundle options = ActivityOptions.makeCustomAnimation(mActivity, 0, 0).toBundle(); |
+ mActivity.startActivity(new Intent(mActivity, VrCancelAnimationActivity.class), options); |
+ mNeedsAnimationCancel = false; |
+ return true; |
+ } |
+ |
private void onResume() { |
- if (mNeedsAnimationCancel) { |
- mCancellingEntryAnimation = true; |
- Bundle options = ActivityOptions.makeCustomAnimation(mActivity, 0, 0).toBundle(); |
- mActivity.startActivity( |
- new Intent(mActivity, VrCancelAnimationActivity.class), options); |
- mNeedsAnimationCancel = false; |
- return; |
- } |
+ if (cancelStartupAnimationIfNeeded()) return; |
+ |
mPaused = false; |
maybeUpdateVrSupportLevel(); |
@@ -1140,7 +1201,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
// Ensure we can't asynchronously enter VR after trying to exit it. |
mEnterVrHandler.removeCallbacksAndMessages(null); |
mDonSucceeded = false; |
- removeOverlayView(); |
+ removeBlackOverlayView(); |
mVrClassesWrapper.setVrModeEnabled(mActivity, false); |
restoreWindowMode(); |
} |
@@ -1369,23 +1430,6 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener, |
mRestoreSystemUiVisibilityFlag = -1; |
} |
- private void addOverlayView() { |
- if (mOverlayView != null) return; |
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( |
- WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); |
- FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView(); |
- mOverlayView = new View(mActivity); |
- mOverlayView.setBackgroundColor(Color.BLACK); |
- decor.addView(mOverlayView, -1, params); |
- } |
- |
- private void removeOverlayView() { |
- if (mOverlayView == null) return; |
- FrameLayout decor = (FrameLayout) sInstance.mActivity.getWindow().getDecorView(); |
- decor.removeView(mOverlayView); |
- mOverlayView = null; |
- } |
- |
/** |
* Clean up VrShell, and associated native objects. |
*/ |