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

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java

Issue 2776243007: Enable WebVR presentation from Chrome Custom Tab (Closed)
Patch Set: Remove file IO, fix PendingIntent errors Created 3 years, 8 months 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/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();
}
}

Powered by Google App Engine
This is Rietveld 408576698