| 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 8e804dcd83aa88994ab992cdffb867303184c487..d82a395885b36e2afe2366d2a601587fb1d56199 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
|
| @@ -5,10 +5,13 @@
|
| package org.chromium.chrome.browser.vr_shell;
|
|
|
| import android.app.Activity;
|
| +import android.app.ActivityManager;
|
| import android.app.PendingIntent;
|
| +import android.content.BroadcastReceiver;
|
| import android.content.ComponentName;
|
| import android.content.Context;
|
| import android.content.Intent;
|
| +import android.content.IntentFilter;
|
| import android.content.pm.ActivityInfo;
|
| import android.content.res.Configuration;
|
| import android.net.Uri;
|
| @@ -36,6 +39,7 @@ import org.chromium.chrome.R;
|
| import org.chromium.chrome.browser.ChromeActivity;
|
| import org.chromium.chrome.browser.ChromeFeatureList;
|
| import org.chromium.chrome.browser.ChromeTabbedActivity;
|
| +import org.chromium.chrome.browser.customtabs.CustomTabActivity;
|
| import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
|
| import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder;
|
| import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
|
| @@ -44,6 +48,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelector;
|
|
|
| import java.lang.annotation.Retention;
|
| import java.lang.annotation.RetentionPolicy;
|
| +import java.lang.ref.WeakReference;
|
| import java.lang.reflect.Constructor;
|
| import java.lang.reflect.InvocationTargetException;
|
|
|
| @@ -57,30 +62,32 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| // Pseudo-random number to avoid request id collisions.
|
| public static final int EXIT_VR_RESULT = 721251;
|
|
|
| - public static final int ENTER_VR_NOT_NECESSARY = 0;
|
| - public static final int ENTER_VR_CANCELLED = 1;
|
| - public static final int ENTER_VR_REQUESTED = 2;
|
| - public static final int ENTER_VR_SUCCEEDED = 3;
|
| + private static final int ENTER_VR_NOT_NECESSARY = 0;
|
| + private static final int ENTER_VR_CANCELLED = 1;
|
| + private static final int ENTER_VR_REQUESTED = 2;
|
| + private static final int ENTER_VR_SUCCEEDED = 3;
|
|
|
| @Retention(RetentionPolicy.SOURCE)
|
| @IntDef({ENTER_VR_NOT_NECESSARY, ENTER_VR_CANCELLED, ENTER_VR_REQUESTED, ENTER_VR_SUCCEEDED})
|
| - public @interface EnterVRResult {}
|
| + private @interface EnterVRResult {}
|
|
|
| - public static final int VR_NOT_AVAILABLE = 0;
|
| - public static final int VR_CARDBOARD = 1;
|
| - public static final int VR_DAYDREAM = 2; // Supports both Cardboard and Daydream viewer.
|
| + private static final int VR_NOT_AVAILABLE = 0;
|
| + private static final int VR_CARDBOARD = 1;
|
| + private static final int VR_DAYDREAM = 2; // Supports both Cardboard and Daydream viewer.
|
|
|
| @Retention(RetentionPolicy.SOURCE)
|
| @IntDef({VR_NOT_AVAILABLE, VR_CARDBOARD, VR_DAYDREAM})
|
| - public @interface VrSupportLevel {}
|
| + private @interface VrSupportLevel {}
|
|
|
| // TODO(bshe): These should be replaced by string provided by NDK. Currently, it only available
|
| // in SDK and we don't want add dependency to SDK just to get these strings.
|
| private static final String DAYDREAM_CATEGORY = "com.google.intent.category.DAYDREAM";
|
| private static final String CARDBOARD_CATEGORY = "com.google.intent.category.CARDBOARD";
|
|
|
| - private static final String VR_ACTIVITY_ALIAS =
|
| - "org.chromium.chrome.browser.VRChromeTabbedActivity";
|
| + private static final String VR_PACKAGE = "org.chromium.chrome.browser.vr_shell";
|
| + private static final String VR_ACTIVITY_ALIAS = VR_PACKAGE + ".VrProxyActivity";
|
| + /* package */ static final String VR_ENTRY_RESULT_ACTION = VR_PACKAGE + ".VrEntryResult";
|
| + /* package */ static final String TARGET_EXTRA = "target";
|
|
|
| private static final long REENTER_VR_TIMEOUT_MS = 1000;
|
|
|
| @@ -93,6 +100,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| "market://details?id=" + VrCoreVersionChecker.VR_CORE_PACKAGE_ID;
|
|
|
| private static VrShellDelegate sInstance;
|
| + private static VrBroadcastReceiver sVrBroadcastReceiver;
|
|
|
| private ChromeActivity mActivity;
|
|
|
| @@ -107,7 +115,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| private TabModelSelector mTabModelSelector;
|
|
|
| private boolean mInVr;
|
| - private boolean mEnteringVr;
|
| + private boolean mDONSucceeded;
|
| private boolean mPaused;
|
| private int mRestoreSystemUiVisibilityFlag = -1;
|
| private Integer mRestoreOrientation = null;
|
| @@ -117,6 +125,35 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| private boolean mListeningForWebVrActivate;
|
| private boolean mListeningForWebVrActivateBeforePause;
|
|
|
| + private static class VrBroadcastReceiver extends BroadcastReceiver {
|
| + // In case there are multiple VrShellDelegates created in different processes, this ensures
|
| + // only the correct receiver listens to the broadcast.
|
| + private final int mTargetId;
|
| + private final WeakReference<Activity> mTargetActivity;
|
| + public VrBroadcastReceiver(Activity activity, int targetId) {
|
| + mTargetId = targetId;
|
| + mTargetActivity = new WeakReference<Activity>(activity);
|
| + }
|
| +
|
| + @Override
|
| + public void onReceive(Context context, Intent intent) {
|
| + Activity activity = mTargetActivity.get();
|
| + if (activity == null) return;
|
| + int target = intent.getIntExtra(TARGET_EXTRA, -1);
|
| + assert target != -1;
|
| + if (mTargetId != target) return;
|
| + getInstance(activity).mDONSucceeded = true;
|
| + ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
|
| + .moveTaskToFront(activity.getTaskId(), 0);
|
| + }
|
| +
|
| + public void unregister() {
|
| + Activity activity = mTargetActivity.get();
|
| + if (activity == null) return;
|
| + activity.unregisterReceiver(VrBroadcastReceiver.this);
|
| + }
|
| + }
|
| +
|
| /**
|
| * Called when the native library is first available.
|
| */
|
| @@ -163,17 +200,6 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| }
|
|
|
| /**
|
| - * Handles a VR intent, entering VR in the process.
|
| - */
|
| - public static void enterVRFromIntent(Intent intent) {
|
| - assert isDaydreamVrIntent(intent);
|
| - boolean created_delegate = sInstance == null;
|
| - VrShellDelegate instance = getInstance();
|
| - if (instance == null) return;
|
| - if (!instance.enterVRFromIntent() && created_delegate) instance.destroy();
|
| - }
|
| -
|
| - /**
|
| * Whether or not the intent is a Daydream VR Intent.
|
| */
|
| public static boolean isDaydreamVrIntent(Intent intent) {
|
| @@ -211,7 +237,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| public static void maybeRegisterVREntryHook(Activity activity) {
|
| if (sInstance != null) return; // Will be handled in onResume.
|
| if (!(activity instanceof ChromeTabbedActivity)) return;
|
| - VrClassesWrapper wrapper = createVrClassesWrapper();
|
| + VrClassesWrapper wrapper = getVrClassesWrapper();
|
| if (wrapper == null) return;
|
| VrDaydreamApi api = wrapper.createVrDaydreamApi(activity);
|
| if (api == null) return;
|
| @@ -226,7 +252,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| public static void maybeUnregisterVREntryHook(Activity activity) {
|
| if (sInstance != null) return; // Will be handled in onPause.
|
| if (!(activity instanceof ChromeTabbedActivity)) return;
|
| - VrClassesWrapper wrapper = createVrClassesWrapper();
|
| + VrClassesWrapper wrapper = getVrClassesWrapper();
|
| if (wrapper == null) return;
|
| VrDaydreamApi api = wrapper.createVrDaydreamApi(activity);
|
| if (api == null) return;
|
| @@ -250,7 +276,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| }
|
|
|
| private static boolean isSupportedActivity(Activity activity) {
|
| - return activity instanceof ChromeTabbedActivity;
|
| + return activity instanceof ChromeTabbedActivity || activity instanceof CustomTabActivity;
|
| }
|
|
|
| /**
|
| @@ -281,16 +307,23 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
|
|
| private static PendingIntent getEnterVRPendingIntent(
|
| VrDaydreamApi dayreamApi, Activity activity) {
|
| - return PendingIntent.getActivity(activity, 0,
|
| - dayreamApi.createVrIntent(new ComponentName(activity, VR_ACTIVITY_ALIAS)),
|
| - PendingIntent.FLAG_ONE_SHOT);
|
| + Intent vrIntent = dayreamApi.createVrIntent(new ComponentName(activity, VR_ACTIVITY_ALIAS));
|
| + vrIntent.putExtra(TARGET_EXTRA, activity.getTaskId());
|
| + return PendingIntent.getActivity(activity, 0, vrIntent,
|
| + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
| }
|
|
|
| /**
|
| * Registers the Intent to fire after phone inserted into a headset.
|
| */
|
| - private static void registerDaydreamIntent(VrDaydreamApi dayreamApi, Activity activity) {
|
| + private static void registerDaydreamIntent(
|
| + final VrDaydreamApi dayreamApi, final Activity activity) {
|
| dayreamApi.registerDaydreamIntent(getEnterVRPendingIntent(dayreamApi, activity));
|
| +
|
| + if (sVrBroadcastReceiver != null) sVrBroadcastReceiver.unregister();
|
| + IntentFilter filter = new IntentFilter(VR_ENTRY_RESULT_ACTION);
|
| + sVrBroadcastReceiver = new VrBroadcastReceiver(activity, activity.getTaskId());
|
| + activity.registerReceiver(sVrBroadcastReceiver, filter);
|
| }
|
|
|
| /**
|
| @@ -370,46 +403,41 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| mVrDaydreamApi, mVrCoreVersionChecker, mActivity.getActivityTab());
|
| }
|
|
|
| + private void maybeSetPresentResult(boolean result) {
|
| + if (mNativeVrShellDelegate != 0 && mRequestedWebVR) {
|
| + nativeSetPresentResult(mNativeVrShellDelegate, result);
|
| + }
|
| + mRequestedWebVR = false;
|
| + }
|
| +
|
| /**
|
| - * Handle a VR intent, entering VR in the process unless we're unable to.
|
| + * Handle a successful VR DON flow, entering VR in the process unless we're unable to.
|
| + * @return False if VR entry failed.
|
| */
|
| - private boolean enterVRFromIntent() {
|
| - // Vr Intent is only used on Daydream devices.
|
| - if (mVrSupportLevel != VR_DAYDREAM) return false;
|
| + private boolean enterVRAfterDON() {
|
| if (mNativeVrShellDelegate == 0) return false;
|
| if (mListeningForWebVrActivateBeforePause && !mRequestedWebVR) {
|
| nativeDisplayActivate(mNativeVrShellDelegate);
|
| - return false;
|
| + return true;
|
| }
|
| +
|
| // Normally, if the active page doesn't have a vrdisplayactivate listener, and WebVR was not
|
| - // presenting and VrShell was not enabled, we shouldn't enter VR and Daydream Homescreen
|
| - // should show after DON flow. But due to a failure in unregisterDaydreamIntent, we still
|
| - // try to enterVR. Here we detect this case and force switch to Daydream Homescreen.
|
| + // presenting and VrShell was not enabled, the Daydream Homescreen should show after the DON
|
| + // flow. However, due to a failure in unregisterDaydreamIntent, we still try to enterVR, so
|
| + // detect this case and fail to enter VR.
|
| if (!mListeningForWebVrActivateBeforePause && !mRequestedWebVR
|
| - && !isVrShellEnabled(mVrSupportLevel)) {
|
| - mVrDaydreamApi.launchVrHomescreen();
|
| + && !canEnterVR(mActivity.getActivityTab())) {
|
| return false;
|
| }
|
|
|
| - if (mInVr) {
|
| - setEnterVRResult(true);
|
| - return false;
|
| - }
|
| - if (!canEnterVR(mActivity.getActivityTab())) {
|
| - setEnterVRResult(false);
|
| - return false;
|
| - }
|
| - if (mPaused) {
|
| - // We can't enter VR before the application resumes, or we encounter bizarre crashes
|
| - // related to gpu surfaces. Set this flag to enter VR on the next resume.
|
| - mEnteringVr = true;
|
| - } else {
|
| - enterVR();
|
| - }
|
| + enterVR();
|
| return true;
|
| }
|
|
|
| private void enterVR() {
|
| + // We can't enter VR before the application resumes, or we encounter bizarre crashes
|
| + // related to gpu surfaces. Set this flag to enter VR on the next resume.
|
| + assert !mPaused;
|
| if (mNativeVrShellDelegate == 0) return;
|
| if (mInVr) return;
|
| if (!isWindowModeCorrectForVr()) {
|
| @@ -422,10 +450,9 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| });
|
| return;
|
| }
|
| - mEnteringVr = false;
|
| if (!createVrShell()) {
|
| - restoreWindowMode();
|
| - setEnterVRResult(false);
|
| + maybeSetPresentResult(false);
|
| + mVrDaydreamApi.launchVrHomescreen();
|
| return;
|
| }
|
| mVrClassesWrapper.setVrModeEnabled(mActivity, true);
|
| @@ -441,7 +468,7 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| // properly.
|
| mVrShell.resume();
|
|
|
| - setEnterVRResult(true);
|
| + maybeSetPresentResult(true);
|
| mVrShell.getContainer().setOnSystemUiVisibilityChangeListener(this);
|
| }
|
|
|
| @@ -472,16 +499,6 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| clearVrModeWindowFlags();
|
| }
|
|
|
| - private void setEnterVRResult(boolean success) {
|
| - if (mRequestedWebVR && mNativeVrShellDelegate != 0) {
|
| - nativeSetPresentResult(mNativeVrShellDelegate, success);
|
| - }
|
| - if (!success && !mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) {
|
| - mVrClassesWrapper.setVrModeEnabled(mActivity, false);
|
| - }
|
| - mRequestedWebVR = false;
|
| - }
|
| -
|
| /* package */ boolean canEnterVR(Tab tab) {
|
| if (!LibraryLoader.isInitialized()) {
|
| return false;
|
| @@ -510,31 +527,19 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
|
|
| @CalledByNative
|
| private void presentRequested() {
|
| - // TODO(mthiesse): There's a GVR bug where they're not calling us back with the intent we
|
| - // ask them to when we call DaydreamApi#launchInVr. As a temporary hack, remember locally
|
| - // that we want to enter webVR.
|
| mRequestedWebVR = true;
|
| switch (enterVRInternal()) {
|
| case ENTER_VR_NOT_NECESSARY:
|
| mVrShell.setWebVrModeEnabled(true);
|
| - if (mNativeVrShellDelegate != 0) {
|
| - nativeSetPresentResult(mNativeVrShellDelegate, true);
|
| - }
|
| - mRequestedWebVR = false;
|
| + maybeSetPresentResult(true);
|
| break;
|
| case ENTER_VR_CANCELLED:
|
| - if (mNativeVrShellDelegate != 0) {
|
| - nativeSetPresentResult(mNativeVrShellDelegate, false);
|
| - }
|
| - mRequestedWebVR = false;
|
| + maybeSetPresentResult(false);
|
| break;
|
| case ENTER_VR_REQUESTED:
|
| break;
|
| case ENTER_VR_SUCCEEDED:
|
| - if (mNativeVrShellDelegate != 0) {
|
| - nativeSetPresentResult(mNativeVrShellDelegate, true);
|
| - }
|
| - mRequestedWebVR = false;
|
| + maybeSetPresentResult(true);
|
| break;
|
| default:
|
| Log.e(TAG, "Unexpected enum.");
|
| @@ -589,22 +594,10 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
|
|
| private void resumeVR() {
|
| mPaused = false;
|
| - if (mVrSupportLevel == VR_NOT_AVAILABLE) return;
|
| - if (mVrSupportLevel == VR_DAYDREAM
|
| - && (isVrShellEnabled(mVrSupportLevel) || mListeningForWebVrActivateBeforePause)) {
|
| - registerDaydreamIntent(mVrDaydreamApi, mActivity);
|
| - }
|
| -
|
| - if (mEnteringVr) {
|
| - enterVR();
|
| - } else if (mRequestedWebVR) {
|
| - // If this is still set, it means the user backed out of the DON flow, and we won't be
|
| - // receiving an intent from daydream.
|
| - if (mNativeVrShellDelegate != 0) nativeSetPresentResult(mNativeVrShellDelegate, false);
|
| - restoreWindowMode();
|
| - mRequestedWebVR = false;
|
| - }
|
| + assert !mInVr;
|
|
|
| + // TODO(mthiesse): If we ever support staying in VR while paused, make sure to call resume
|
| + // on VrShell.
|
| StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
|
| try {
|
| if (mNativeVrShellDelegate != 0) nativeOnResume(mNativeVrShellDelegate);
|
| @@ -612,19 +605,29 @@ public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
|
| StrictMode.setThreadPolicy(oldPolicy);
|
| }
|
|
|
| - if (mInVr) {
|
| - setupVrModeWindowFlags();
|
| - oldPolicy = StrictMode.allowThreadDiskWrites();
|
| - try {
|
| - mVrShell.resume();
|
| - } catch (IllegalArgumentException e) {
|
| - Log.e(TAG, "Unable to resume VrShell", e);
|
| - } finally {
|
| - StrictMode.setThreadPolicy(oldPolicy);
|
| - }
|
| - } else if (mVrSupportLevel == VR_DAYDREAM && mVrDaydreamApi.isDaydreamCurrentViewer()
|
| + if (mVrSupportLevel != VR_DAYDREAM) return;
|
| + if (mListeningForWebVrActivateBeforePause
|
| + || (isVrShellEnabled(mVrSupportLevel)
|
| + && (mActivity instanceof ChromeTabbedActivity))) {
|
| + registerDaydreamIntent(mVrDaydreamApi, mActivity);
|
| + }
|
| +
|
| + if (mVrDaydreamApi.isDaydreamCurrentViewer()
|
| && mLastVRExit + REENTER_VR_TIMEOUT_MS > SystemClock.uptimeMillis()) {
|
| - enterVRInternal();
|
| + mDONSucceeded = true;
|
| + }
|
| +
|
| + if (mDONSucceeded) {
|
| + mDONSucceeded = false;
|
| + // If we fail to enter VR when we should have entered VR, return to the home screen.
|
| + if (!enterVRAfterDON()) {
|
| + maybeSetPresentResult(false);
|
| + mVrDaydreamApi.launchVrHomescreen();
|
| + }
|
| + } else if (mRestoreOrientation != null) {
|
| + // This means the user backed out of the DON flow, and we won't be entering VR.
|
| + maybeSetPresentResult(false);
|
| + restoreWindowMode();
|
| }
|
| }
|
|
|
|
|