Commit 812aebef authored by Yash Malik's avatar Yash Malik Committed by Commit Bot

VR: Add support for VR intents and transitioning into VR rendering

This CL enters Chrome into VR mode upon receiving a VR intent. It facilitates
a smooth transition into VR by having VR intents go through VrMainActivity
which has the correct manifest flags to keep the system in VR mode and 
disabling the window preview feature to hide the system UI on startup. Note
that currently, VrMainActivity doesn't have any intent-filters and can only
be explicitly targeted.

VrMainActivity continues to open a CCT for intents from the Daydream app
and opens a CTA otherwise.

Follow-up work:
- Fix issues with devices that support multiple screen densities 

Note that this feature is guarded behind the "VrLaunchIntent" runtime 
flag.

Bug: 752200
Change-Id: I7656e0df53c0095ca3a958ede281e2ef7157cd95
Reviewed-on: https://chromium-review.googlesource.com/722456Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Commit-Queue: Yash Malik <ymalik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#512877}
parent 8e6d2ea6
...@@ -352,9 +352,18 @@ by a child template that "extends" this file. ...@@ -352,9 +352,18 @@ by a child template that "extends" this file.
{% endfor %} {% endfor %}
{% if enable_vr == "true" %} {% if enable_vr == "true" %}
<activity android:name="org.chromium.chrome.browser.vr.VrMainActivity" <activity android:name="org.chromium.chrome.browser.vr.VrMainActivity"
android:theme="@style/VrSupportThemeLauncher"
android:exported="true"
android:enableVrMode="@string/gvr_vr_mode_component"
android:taskAffinity=""
android:relinquishTaskIdentity="true"
android:excludeFromRecents="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
{{ self.supports_vr() }}
</activity>
<activity android:name="org.chromium.chrome.browser.vr.CustomTabVrActivity"
android:theme="@style/VrSupportTheme" android:theme="@style/VrSupportTheme"
android:screenOrientation="landscape" android:screenOrientation="landscape"
android:exported="true"
android:enableVrMode="@string/gvr_vr_mode_component" android:enableVrMode="@string/gvr_vr_mode_component"
android:taskAffinity="" android:taskAffinity=""
android:persistableMode="persistNever" android:persistableMode="persistNever"
......
...@@ -62,6 +62,10 @@ ...@@ -62,6 +62,10 @@
<item name="android:windowDisablePreview">true</item> <item name="android:windowDisablePreview">true</item>
<item name="android:windowBackground">@android:color/black</item> <item name="android:windowBackground">@android:color/black</item>
</style> </style>
<style name="VrSupportThemeLauncher" parent="@android:style/Theme.NoDisplay">
<item name="android:windowDisablePreview">true</item>
<item name="android:windowBackground">@android:color/black</item>
</style>
<style name="FullscreenWhite" parent="Theme.AppCompat.Light" > <style name="FullscreenWhite" parent="Theme.AppCompat.Light" >
<item name="android:windowBackground">@android:color/white</item> <item name="android:windowBackground">@android:color/white</item>
......
...@@ -137,6 +137,7 @@ import org.chromium.chrome.browser.util.AccessibilityUtil; ...@@ -137,6 +137,7 @@ import org.chromium.chrome.browser.util.AccessibilityUtil;
import org.chromium.chrome.browser.util.ChromeFileProvider; import org.chromium.chrome.browser.util.ChromeFileProvider;
import org.chromium.chrome.browser.util.ColorUtils; import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.chrome.browser.vr_shell.VrIntentUtils;
import org.chromium.chrome.browser.vr_shell.VrShellDelegate; import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
import org.chromium.chrome.browser.webapps.AddToHomescreenManager; import org.chromium.chrome.browser.webapps.AddToHomescreenManager;
import org.chromium.chrome.browser.widget.ControlContainer; import org.chromium.chrome.browser.widget.ControlContainer;
...@@ -308,6 +309,12 @@ public abstract class ChromeActivity extends AsyncInitializationActivity ...@@ -308,6 +309,12 @@ public abstract class ChromeActivity extends AsyncInitializationActivity
public void preInflationStartup() { public void preInflationStartup() {
super.preInflationStartup(); super.preInflationStartup();
// We need to explicitly enable VR mode here so that the system doesn't kick us out of VR
// mode while we prepare for VR rendering.
if (VrIntentUtils.isVrIntent(getIntent())) {
VrShellDelegate.setVrModeEnabled(this);
}
// Force a partner customizations refresh if it has yet to be initialized. This can happen // Force a partner customizations refresh if it has yet to be initialized. This can happen
// if Chrome is killed and you refocus a previous activity from Android recents, which does // if Chrome is killed and you refocus a previous activity from Android recents, which does
// not go through ChromeLauncherActivity that would have normally triggered this. // not go through ChromeLauncherActivity that would have normally triggered this.
...@@ -925,8 +932,10 @@ public abstract class ChromeActivity extends AsyncInitializationActivity ...@@ -925,8 +932,10 @@ public abstract class ChromeActivity extends AsyncInitializationActivity
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
super.onNewIntent(intent); // This should be called before the call to super so that the needed VR flags are set as
// soon as the VR intent is received.
VrShellDelegate.maybeHandleVrIntentPreNative(this, intent); VrShellDelegate.maybeHandleVrIntentPreNative(this, intent);
super.onNewIntent(intent);
} }
@Override @Override
......
...@@ -223,6 +223,7 @@ public abstract class ChromeFeatureList { ...@@ -223,6 +223,7 @@ public abstract class ChromeFeatureList {
public static final String VIDEO_PERSISTENCE = "VideoPersistence"; public static final String VIDEO_PERSISTENCE = "VideoPersistence";
public static final String VR_BROWSING_FEEDBACK = "VrBrowsingFeedback"; public static final String VR_BROWSING_FEEDBACK = "VrBrowsingFeedback";
public static final String VR_CUSTOM_TAB_BROWSING = "VrCustomTabBrowsing"; public static final String VR_CUSTOM_TAB_BROWSING = "VrCustomTabBrowsing";
public static final String VR_LAUNCH_INTENT = "VrLaunchIntent";
public static final String VR_SHELL = "VrShell"; public static final String VR_SHELL = "VrShell";
public static final String WEB_PAYMENTS = "WebPayments"; public static final String WEB_PAYMENTS = "WebPayments";
public static final String WEB_PAYMENTS_METHOD_SECTION_ORDER_V2 = public static final String WEB_PAYMENTS_METHOD_SECTION_ORDER_V2 =
......
...@@ -15,6 +15,7 @@ import android.os.Build; ...@@ -15,6 +15,7 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.StrictMode; import android.os.StrictMode;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.text.TextUtils; import android.text.TextUtils;
...@@ -40,7 +41,7 @@ import org.chromium.chrome.browser.upgrade.UpgradeActivity; ...@@ -40,7 +41,7 @@ import org.chromium.chrome.browser.upgrade.UpgradeActivity;
import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.chrome.browser.util.UrlUtilities; import org.chromium.chrome.browser.util.UrlUtilities;
import org.chromium.chrome.browser.vr.VrMainActivity; import org.chromium.chrome.browser.vr.CustomTabVrActivity;
import org.chromium.chrome.browser.vr_shell.VrIntentUtils; import org.chromium.chrome.browser.vr_shell.VrIntentUtils;
import org.chromium.chrome.browser.webapps.ActivityAssigner; import org.chromium.chrome.browser.webapps.ActivityAssigner;
import org.chromium.chrome.browser.webapps.WebappLauncherActivity; import org.chromium.chrome.browser.webapps.WebappLauncherActivity;
...@@ -83,6 +84,7 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -83,6 +84,7 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
private final Intent mIntent; private final Intent mIntent;
private final boolean mIsCustomTabIntent; private final boolean mIsCustomTabIntent;
private final boolean mIsHerbIntent; private final boolean mIsHerbIntent;
private final boolean mIsVrIntent;
@IntDef({Action.CONTINUE, Action.FINISH_ACTIVITY, Action.FINISH_ACTIVITY_REMOVE_TASK}) @IntDef({Action.CONTINUE, Action.FINISH_ACTIVITY, Action.FINISH_ACTIVITY_REMOVE_TASK})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
...@@ -144,11 +146,13 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -144,11 +146,13 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
recordIntentMetrics(); recordIntentMetrics();
boolean isCustomTabIntent = isCustomTabIntent(mIntent); mIsVrIntent = VrIntentUtils.isVrIntent(mIntent);
boolean isCustomTabIntent = (!mIsVrIntent && isCustomTabIntent(mIntent))
|| (mIsVrIntent && VrIntentUtils.isCustomTabVrIntent(mIntent));
boolean isHerbIntent = false; boolean isHerbIntent = false;
// If the intent was created by Reader Mode, ignore herb and custom tab information. // If the intent was created by Reader Mode, ignore herb and custom tab information.
if (!isCustomTabIntent && !ReaderModeManager.isReaderModeCreatedIntent(mIntent) if (!isCustomTabIntent && !ReaderModeManager.isReaderModeCreatedIntent(mIntent)
&& !VrIntentUtils.isVrIntent(mIntent)) { && !mIsVrIntent) {
isHerbIntent = isHerbIntent(mIntent); isHerbIntent = isHerbIntent(mIntent);
isCustomTabIntent = isHerbIntent; isCustomTabIntent = isHerbIntent;
} }
...@@ -156,6 +160,19 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -156,6 +160,19 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
mIsHerbIntent = isHerbIntent; mIsHerbIntent = isHerbIntent;
} }
/**
* Returns the options that should be used to start an activity.
*/
@Nullable
private Bundle getStartActivityIntentOptions() {
Bundle options = null;
if (mIsVrIntent) {
// These options hide the 2D screenshot while we prepare for VR rendering.
options = VrIntentUtils.getVrIntentOptions(mActivity);
}
return options;
}
/** /**
* Figure out how to route the Intent. Because this is on the critical path to startup, please * Figure out how to route the Intent. Because this is on the critical path to startup, please
* avoid making the pathway any more complicated than it already is. Make sure that anything * avoid making the pathway any more complicated than it already is. Make sure that anything
...@@ -332,9 +349,8 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -332,9 +349,8 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
*/ */
public static boolean isCustomTabIntent(Intent intent) { public static boolean isCustomTabIntent(Intent intent) {
if (intent == null) return false; if (intent == null) return false;
if ((CustomTabsIntent.shouldAlwaysUseBrowserUI(intent) if (CustomTabsIntent.shouldAlwaysUseBrowserUI(intent)
|| !intent.hasExtra(CustomTabsIntent.EXTRA_SESSION)) || !intent.hasExtra(CustomTabsIntent.EXTRA_SESSION)) {
&& !VrIntentUtils.isCustomTabVrIntent(intent)) {
return false; return false;
} }
return IntentHandler.getUrlFromIntent(intent) != null; return IntentHandler.getUrlFromIntent(intent) != null;
...@@ -365,7 +381,7 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -365,7 +381,7 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
// Force a new document L+ to ensure the proper task/stack creation. // Force a new document L+ to ensure the proper task/stack creation.
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
if (VrIntentUtils.isVrIntent(intent)) { if (VrIntentUtils.isVrIntent(intent)) {
newIntent.setClassName(context, VrMainActivity.class.getName()); newIntent.setClassName(context, CustomTabVrActivity.class.getName());
} else { } else {
newIntent.setClassName(context, SeparateTaskCustomTabActivity.class.getName()); newIntent.setClassName(context, SeparateTaskCustomTabActivity.class.getName());
} }
...@@ -413,17 +429,9 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -413,17 +429,9 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
maybePrefetchDnsInBackground(); maybePrefetchDnsInBackground();
// Create and fire a launch intent. // Create and fire a launch intent.
Bundle options = null;
if (VrIntentUtils.isVrIntent(mIntent)) {
// VR intents will open a VR-specific CCT {@link VrMainActivity} which
// starts with a theme that disables the system preview window. As a side effect, you
// see a flash of the previous app exiting before Chrome is started. These options
// prevent that flash as it can look jarring while the user is in their headset.
options = VrIntentUtils.getVrIntentOptions(mActivity);
}
mActivity.startActivity(createCustomTabActivityIntent(mActivity, mIntent, mActivity.startActivity(createCustomTabActivityIntent(mActivity, mIntent,
!isCustomTabIntent(mIntent) && mIsHerbIntent), !isCustomTabIntent(mIntent) && mIsHerbIntent),
options); getStartActivityIntentOptions());
if (mIsHerbIntent) { if (mIsHerbIntent) {
mActivity.overridePendingTransition(R.anim.activity_open_enter, R.anim.no_anim); mActivity.overridePendingTransition(R.anim.activity_open_enter, R.anim.no_anim);
} }
...@@ -463,7 +471,7 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -463,7 +471,7 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
// This system call is often modified by OEMs and not actionable. http://crbug.com/619646. // This system call is often modified by OEMs and not actionable. http://crbug.com/619646.
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try { try {
mActivity.startActivity(newIntent); mActivity.startActivity(newIntent, getStartActivityIntentOptions());
} catch (SecurityException ex) { } catch (SecurityException ex) {
if (isContentScheme) { if (isContentScheme) {
Toast.makeText(mActivity, Toast.makeText(mActivity,
......
// Copyright 2017 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.vr;
import android.content.Intent;
import android.view.WindowManager;
import org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.chrome.browser.vr_shell.VrIntentUtils;
import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
/**
* A subclass of SeparateTaskCustomTabActivity created when starting Chrome in VR mode.
*
* The main purpose of this activity is to add flexibility to the way Chrome is started when the
* user's phone is already in their VR headset (e.g, we want to hide the System UI).
*/
public class CustomTabVrActivity extends SeparateTaskCustomTabActivity {
@Override
public void preInflationStartup() {
assert VrIntentUtils.getHandlerInstance().isTrustedDaydreamIntent(getIntent());
// Set VR specific window mode.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().getDecorView().setSystemUiVisibility(VrShellDelegate.VR_SYSTEM_UI_FLAGS);
super.preInflationStartup();
}
@Override
protected boolean isStartedUpCorrectly(Intent intent) {
if (!VrIntentUtils.getHandlerInstance().isTrustedDaydreamIntent(getIntent())) {
return false;
}
return super.isStartedUpCorrectly(intent);
}
@Override
protected Intent validateIntent(final Intent intent) {
return IntentUtils.sanitizeIntent(intent);
}
}
...@@ -4,42 +4,18 @@ ...@@ -4,42 +4,18 @@
package org.chromium.chrome.browser.vr; package org.chromium.chrome.browser.vr;
import android.content.Intent; import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import android.view.WindowManager;
import org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.chrome.browser.vr_shell.VrIntentUtils;
import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
/** /**
* A subclass of SeparateTaskCustomTabActivity created when starting Chrome in VR mode. * This is the VR equivalent of {@link ChromeLauncherActivity}. It exists only because the Android
* platform doesn't inherently support hybrid VR apps (like Chrome). All VR intents for Chrome
* should eventually be routed through this activity as its manifest entry contains VR specific
* attributes to ensure a smooth transition into Chrome VR.
* *
* The main purpose of this activity is to add flexibility to the way Chrome is started when the * More specifically, a special VR theme disables the Preview Window feature to prevent the system
* user's phone is already in their VR headset (e.g, we want to hide the System UI). * UI from showing up while Chrome prepares to enter VR mode. The android:enabledVrMode attribute
* ensures that the system doesn't kick us out of VR mode during the transition as this as this can
* result in a screen brightness flicker. Both of these sound minor but look jarring from a VR
* headset.
*/ */
public class VrMainActivity extends SeparateTaskCustomTabActivity { public class VrMainActivity extends ChromeLauncherActivity {}
@Override
public void preInflationStartup() {
assert VrIntentUtils.getHandlerInstance().isTrustedDaydreamIntent(getIntent());
// Set VR specific window mode.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().getDecorView().setSystemUiVisibility(VrShellDelegate.VR_SYSTEM_UI_FLAGS);
super.preInflationStartup();
}
@Override
protected boolean isStartedUpCorrectly(Intent intent) {
if (!VrIntentUtils.getHandlerInstance().isTrustedDaydreamIntent(getIntent())) {
return false;
}
return super.isStartedUpCorrectly(intent);
}
@Override
protected Intent validateIntent(final Intent intent) {
return IntentUtils.sanitizeIntent(intent);
}
}
...@@ -21,8 +21,9 @@ import org.chromium.chrome.browser.util.IntentUtils; ...@@ -21,8 +21,9 @@ import org.chromium.chrome.browser.util.IntentUtils;
public class VrIntentUtils { public class VrIntentUtils {
private static final String DAYDREAM_HOME_PACKAGE = "com.google.android.vr.home"; private static final String DAYDREAM_HOME_PACKAGE = "com.google.android.vr.home";
// The Daydream Home app adds this extra to auto-present intents. // The Daydream Home app adds this extra to auto-present intents.
private static final String AUTOPRESENT_WEVBVR_EXTRA = "browser.vr.AUTOPRESENT_WEBVR"; public static final String AUTOPRESENT_WEVBVR_EXTRA = "browser.vr.AUTOPRESENT_WEBVR";
public static final String DAYDREAM_VR_EXTRA = "android.intent.extra.VR_LAUNCH"; public static final String DAYDREAM_VR_EXTRA = "android.intent.extra.VR_LAUNCH";
public static final String DAYDREAM_CATEGORY = "com.google.intent.category.DAYDREAM";
static final String VR_FRE_INTENT_EXTRA = "org.chromium.chrome.browser.vr_shell.VR_FRE"; static final String VR_FRE_INTENT_EXTRA = "org.chromium.chrome.browser.vr_shell.VR_FRE";
static final String VR_FRE_CALLER_INTENT_EXTRA = static final String VR_FRE_CALLER_INTENT_EXTRA =
...@@ -91,11 +92,17 @@ public class VrIntentUtils { ...@@ -91,11 +92,17 @@ public class VrIntentUtils {
* @return Whether or not the given intent is a VR-specific intent. * @return Whether or not the given intent is a VR-specific intent.
*/ */
public static boolean isVrIntent(Intent intent) { public static boolean isVrIntent(Intent intent) {
if (intent == null) return false;
// For simplicity, we only return true here if VR is enabled on the platform and this intent // For simplicity, we only return true here if VR is enabled on the platform and this intent
// is not fired from a recent apps page. The latter is there so that we don't enter VR mode // is not fired from a recent apps page. The latter is there so that we don't enter VR mode
// when we're being resumed from the recent apps in 2D mode. // when we're being resumed from the recent apps in 2D mode.
// Note that Daydream removes the Daydream category for deep-links (for no real reason). In
// addition to the category, DAYDREAM_VR_EXTRA tells us that this intent is coming directly
// from VR.
boolean canHandleIntent = VrShellDelegate.isVrEnabled() && !launchedFromRecentApps(intent); boolean canHandleIntent = VrShellDelegate.isVrEnabled() && !launchedFromRecentApps(intent);
return IntentUtils.safeGetBooleanExtra(intent, DAYDREAM_VR_EXTRA, false) && canHandleIntent; return (intent.hasCategory(DAYDREAM_CATEGORY)
|| IntentUtils.safeGetBooleanExtra(intent, DAYDREAM_VR_EXTRA, false))
&& canHandleIntent;
} }
/** /**
...@@ -104,7 +111,9 @@ public class VrIntentUtils { ...@@ -104,7 +111,9 @@ public class VrIntentUtils {
public static boolean isCustomTabVrIntent(Intent intent) { public static boolean isCustomTabVrIntent(Intent intent) {
// TODO(crbug.com/719661): Currently, only Daydream intents open in a CustomTab. We should // TODO(crbug.com/719661): Currently, only Daydream intents open in a CustomTab. We should
// probably change this once we figure out core CCT flows in VR. // probably change this once we figure out core CCT flows in VR.
return getHandlerInstance().isTrustedDaydreamIntent(intent); if (intent == null) return false;
return IntentHandler.getUrlFromIntent(intent) != null
&& getHandlerInstance().isTrustedDaydreamIntent(intent);
} }
/** /**
...@@ -157,4 +166,14 @@ public class VrIntentUtils { ...@@ -157,4 +166,14 @@ public class VrIntentUtils {
/* package */ static boolean launchedFromRecentApps(Intent intent) { /* package */ static boolean launchedFromRecentApps(Intent intent) {
return ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0); return ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0);
} }
/**
* Removes VR specific extras from the given intent to make it a non-VR intent.
*/
/* package */ static void removeVrExtras(Intent intent) {
if (intent == null) return;
intent.removeExtra(DAYDREAM_VR_EXTRA);
intent.removeCategory(DAYDREAM_CATEGORY);
assert !isVrIntent(intent);
}
} }
...@@ -1211,6 +1211,7 @@ chrome_java_sources = [ ...@@ -1211,6 +1211,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/util/PlatformUtil.java", "java/src/org/chromium/chrome/browser/util/PlatformUtil.java",
"java/src/org/chromium/chrome/browser/util/UrlUtilities.java", "java/src/org/chromium/chrome/browser/util/UrlUtilities.java",
"java/src/org/chromium/chrome/browser/util/ViewUtils.java", "java/src/org/chromium/chrome/browser/util/ViewUtils.java",
"java/src/org/chromium/chrome/browser/vr/CustomTabVrActivity.java",
"java/src/org/chromium/chrome/browser/vr/VrMainActivity.java", "java/src/org/chromium/chrome/browser/vr/VrMainActivity.java",
"java/src/org/chromium/chrome/browser/vr_shell/VrCancelAnimationActivity.java", "java/src/org/chromium/chrome/browser/vr_shell/VrCancelAnimationActivity.java",
"java/src/org/chromium/chrome/browser/vr_shell/VrClassesWrapper.java", "java/src/org/chromium/chrome/browser/vr_shell/VrClassesWrapper.java",
......
...@@ -12,6 +12,7 @@ import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_D ...@@ -12,6 +12,7 @@ import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_D
import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_NON_DAYDREAM; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_NON_DAYDREAM;
import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM; import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM;
import android.app.Activity;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
...@@ -21,10 +22,12 @@ import org.junit.Rule; ...@@ -21,10 +22,12 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.Restriction;
import org.chromium.base.test.util.RetryOnFailure; import org.chromium.base.test.util.RetryOnFailure;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.vr_shell.mock.MockVrDaydreamApi; import org.chromium.chrome.browser.vr_shell.mock.MockVrDaydreamApi;
import org.chromium.chrome.browser.vr_shell.rules.ChromeTabbedActivityVrTestRule; import org.chromium.chrome.browser.vr_shell.rules.ChromeTabbedActivityVrTestRule;
import org.chromium.chrome.browser.vr_shell.util.NfcSimUtils; import org.chromium.chrome.browser.vr_shell.util.NfcSimUtils;
...@@ -36,7 +39,10 @@ import org.chromium.content.browser.test.util.Criteria; ...@@ -36,7 +39,10 @@ import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content.browser.test.util.DOMUtils; import org.chromium.content.browser.test.util.DOMUtils;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* End-to-end tests for state transitions in VR, e.g. exiting WebVR presentation * End-to-end tests for state transitions in VR, e.g. exiting WebVR presentation
...@@ -126,6 +132,57 @@ public class VrShellTransitionTest { ...@@ -126,6 +132,57 @@ public class VrShellTransitionTest {
enterExitVrShell(true /* supported */); enterExitVrShell(true /* supported */);
} }
/**
* Verifies that browser successfully transitions from 2D chrome to the VR
* browser when Chrome gets a VR intent.
* Note that we need to remove the enable-features flag that's set above and reset it with
* VrLaunchIntent above otherwise the one set on the class takes precedence.
*/
@Test
@Restriction(RESTRICTION_TYPE_DEVICE_DAYDREAM)
@MediumTest
@CommandLineFlags.Remove("enable-features=VrShell")
@CommandLineFlags.Add("enable-features=VrShell,VrLaunchIntent")
public void testVrIntentStartsVrShell() {
// Send a VR intent, which will open the link in a CTA.
String url = VrTestFramework.getHtmlTestFile("test_navigation_2d_page");
VrTransitionUtils.sendVrLaunchIntent(
url, mVrTestRule.getActivity(), false /* autopresent */);
// Wait until a CTA is opened due to the intent
final AtomicReference<ChromeTabbedActivity> cta =
new AtomicReference<ChromeTabbedActivity>();
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
List<WeakReference<Activity>> list = ApplicationStatus.getRunningActivities();
for (WeakReference<Activity> ref : list) {
Activity activity = ref.get();
if (activity == null) continue;
if (activity instanceof ChromeTabbedActivity) {
cta.set((ChromeTabbedActivity) activity);
return true;
}
}
return false;
}
}, POLL_TIMEOUT_LONG_MS, POLL_CHECK_INTERVAL_SHORT_MS);
// Wait until the tab is ready
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
if (cta.get().getActivityTab() == null) return false;
return !cta.get().getActivityTab().isLoading();
}
}, POLL_TIMEOUT_LONG_MS, POLL_CHECK_INTERVAL_SHORT_MS);
VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
Assert.assertTrue(VrShellDelegate.isInVr());
Assert.assertEquals("Url correct", url,
mVrTestRule.getActivity().getActivityTab().getWebContents().getVisibleUrl());
}
/** /**
* Verifies that browser does not enter VR mode on Non-Daydream-ready devices. * Verifies that browser does not enter VR mode on Non-Daydream-ready devices.
*/ */
......
...@@ -158,9 +158,9 @@ public class WebVrTransitionTest { ...@@ -158,9 +158,9 @@ public class WebVrTransitionTest {
true /* useMockImplementation */, true /* treatIntentsAsTrusted */)); true /* useMockImplementation */, true /* treatIntentsAsTrusted */));
// Send an autopresent intent, which will open the link in a CCT // Send an autopresent intent, which will open the link in a CCT
VrTransitionUtils.sendDaydreamAutopresentIntent( VrTransitionUtils.sendVrLaunchIntent(
VrTestFramework.getHtmlTestFile("test_webvr_autopresent"), VrTestFramework.getHtmlTestFile("test_webvr_autopresent"),
mVrTestRule.getActivity()); mVrTestRule.getActivity(), true /* autopresent */);
// Wait until a CCT is opened due to the intent // Wait until a CCT is opened due to the intent
final AtomicReference<CustomTabActivity> cct = new AtomicReference<CustomTabActivity>(); final AtomicReference<CustomTabActivity> cct = new AtomicReference<CustomTabActivity>();
......
...@@ -17,7 +17,7 @@ import org.junit.Assert; ...@@ -17,7 +17,7 @@ import org.junit.Assert;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.vr.VrMainActivity;
import org.chromium.chrome.browser.vr_shell.TestVrShellDelegate; import org.chromium.chrome.browser.vr_shell.TestVrShellDelegate;
import org.chromium.chrome.browser.vr_shell.VrClassesWrapperImpl; import org.chromium.chrome.browser.vr_shell.VrClassesWrapperImpl;
import org.chromium.chrome.browser.vr_shell.VrIntentUtils; import org.chromium.chrome.browser.vr_shell.VrIntentUtils;
...@@ -182,20 +182,27 @@ public class VrTransitionUtils { ...@@ -182,20 +182,27 @@ public class VrTransitionUtils {
} }
/** /**
* Sends an intent to Chrome telling it to autopresent the given URL. This * Sends an intent to Chrome telling it to launch in VR mode. If the given autopresent param is
* is expected to fail unless the trusted intent check is disabled in VrShellDelegate. * true, this is expected to fail unless the trusted intent check is disabled in
* VrShellDelegate.
* *
* @param url String containing the URL to open * @param url String containing the URL to open
* @param activity The activity to launch the intent from * @param activity The activity to launch the intent from
* @param autopresent If this intent is expected to auto-present WebVR
*/ */
public static void sendDaydreamAutopresentIntent(String url, final Activity activity) { public static void sendVrLaunchIntent(
// Create an intent that will launch Chrome at the specified URL with autopresent String url, final Activity activity, boolean autopresent) {
// Create an intent that will launch Chrome at the specified URL.
final Intent intent = final Intent intent =
new Intent(ContextUtils.getApplicationContext(), ChromeLauncherActivity.class); new Intent(ContextUtils.getApplicationContext(), VrMainActivity.class);
intent.setData(Uri.parse(url)); intent.setData(Uri.parse(url));
intent.putExtra(VrIntentUtils.DAYDREAM_VR_EXTRA, true); intent.putExtra(VrIntentUtils.DAYDREAM_VR_EXTRA, true);
DaydreamApi.setupVrIntent(intent); DaydreamApi.setupVrIntent(intent);
intent.removeCategory("com.google.intent.category.DAYDREAM"); if (autopresent) {
// Daydream removes this category for deep-linked URLs for legacy reasons.
intent.removeCategory(VrIntentUtils.DAYDREAM_CATEGORY);
intent.putExtra(VrIntentUtils.AUTOPRESENT_WEVBVR_EXTRA, true);
}
final VrClassesWrapperImpl wrapper = new VrClassesWrapperImpl(); final VrClassesWrapperImpl wrapper = new VrClassesWrapperImpl();
ThreadUtils.runOnUiThreadBlocking(new Runnable() { ThreadUtils.runOnUiThreadBlocking(new Runnable() {
......
...@@ -2237,6 +2237,9 @@ const FeatureEntry kFeatureEntries[] = { ...@@ -2237,6 +2237,9 @@ const FeatureEntry kFeatureEntries[] = {
flag_descriptions::kVrShellExperimentalRenderingName, flag_descriptions::kVrShellExperimentalRenderingName,
flag_descriptions::kVrShellExperimentalRenderingDescription, kOsAndroid, flag_descriptions::kVrShellExperimentalRenderingDescription, kOsAndroid,
FEATURE_VALUE_TYPE(features::kVrShellExperimentalRendering)}, FEATURE_VALUE_TYPE(features::kVrShellExperimentalRendering)},
{"enable-vr-launch-intent", flag_descriptions::kVrLaunchIntentName,
flag_descriptions::kVrLaunchIntentDescription, kOsAndroid,
FEATURE_VALUE_TYPE(chrome::android::kVrLaunchIntent)},
{"enable-webvr-autopresent", flag_descriptions::kWebVrAutopresentName, {"enable-webvr-autopresent", flag_descriptions::kWebVrAutopresentName,
flag_descriptions::kWebVrAutopresentDescription, kOsAndroid, flag_descriptions::kWebVrAutopresentDescription, kOsAndroid,
FEATURE_VALUE_TYPE(chrome::android::kWebVrAutopresent)}, FEATURE_VALUE_TYPE(chrome::android::kWebVrAutopresent)},
......
...@@ -105,6 +105,7 @@ const base::Feature* kFeaturesExposedToJava[] = { ...@@ -105,6 +105,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
&kVideoPersistence, &kVideoPersistence,
&kVrBrowsingFeedback, &kVrBrowsingFeedback,
&kVrCustomTabBrowsing, &kVrCustomTabBrowsing,
&kVrLaunchIntent,
&payments::features::kWebPaymentsMethodSectionOrderV2, &payments::features::kWebPaymentsMethodSectionOrderV2,
&payments::features::kWebPaymentsModifiers, &payments::features::kWebPaymentsModifiers,
&kWebPaymentsSingleAppUiSkip, &kWebPaymentsSingleAppUiSkip,
...@@ -308,6 +309,9 @@ const base::Feature kVrBrowsingFeedback{"VrBrowsingFeedback", ...@@ -308,6 +309,9 @@ const base::Feature kVrBrowsingFeedback{"VrBrowsingFeedback",
const base::Feature kVrCustomTabBrowsing{"VrCustomTabBrowsing", const base::Feature kVrCustomTabBrowsing{"VrCustomTabBrowsing",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kVrLaunchIntent{"VrLaunchIntent",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kWebPaymentsSingleAppUiSkip{ const base::Feature kWebPaymentsSingleAppUiSkip{
"WebPaymentsSingleAppUiSkip", base::FEATURE_ENABLED_BY_DEFAULT}; "WebPaymentsSingleAppUiSkip", base::FEATURE_ENABLED_BY_DEFAULT};
......
...@@ -68,6 +68,7 @@ extern const base::Feature kUserMediaScreenCapturing; ...@@ -68,6 +68,7 @@ extern const base::Feature kUserMediaScreenCapturing;
extern const base::Feature kVideoPersistence; extern const base::Feature kVideoPersistence;
extern const base::Feature kVrBrowsingFeedback; extern const base::Feature kVrBrowsingFeedback;
extern const base::Feature kVrCustomTabBrowsing; extern const base::Feature kVrCustomTabBrowsing;
extern const base::Feature kVrLaunchIntent;
extern const base::Feature kWebPaymentsSingleAppUiSkip; extern const base::Feature kWebPaymentsSingleAppUiSkip;
extern const base::Feature kWebVrAutopresent; extern const base::Feature kWebVrAutopresent;
extern const base::Feature kWebVRCardboardSupport; extern const base::Feature kWebVRCardboardSupport;
......
...@@ -2737,6 +2737,10 @@ const char kVrShellExperimentalRenderingDescription[] = ...@@ -2737,6 +2737,10 @@ const char kVrShellExperimentalRenderingDescription[] =
"Turns on experimental rendering features for Chrome VR, like power saving " "Turns on experimental rendering features for Chrome VR, like power saving "
"rendering modes."; "rendering modes.";
const char kVrLaunchIntentName[] = "Enable VR intents";
const char kVrLaunchIntentDescription[] =
"Allow intents to launch Chrome in VR mode.";
const char kWebVrAutopresentName[] = "Enable WebVr auto presentation"; const char kWebVrAutopresentName[] = "Enable WebVr auto presentation";
const char kWebVrAutopresentDescription[] = const char kWebVrAutopresentDescription[] =
"Allows auto presentation of WebVr content from trusted first-party apps"; "Allows auto presentation of WebVr content from trusted first-party apps";
......
...@@ -1684,6 +1684,9 @@ extern const char kVrCustomTabBrowsingDescription[]; ...@@ -1684,6 +1684,9 @@ extern const char kVrCustomTabBrowsingDescription[];
extern const char kVrShellExperimentalRenderingName[]; extern const char kVrShellExperimentalRenderingName[];
extern const char kVrShellExperimentalRenderingDescription[]; extern const char kVrShellExperimentalRenderingDescription[];
extern const char kVrLaunchIntentName[];
extern const char kVrLaunchIntentDescription[];
extern const char kWebVrAutopresentName[]; extern const char kWebVrAutopresentName[];
extern const char kWebVrAutopresentDescription[]; extern const char kWebVrAutopresentDescription[];
......
...@@ -23977,6 +23977,7 @@ from previous Chrome versions. ...@@ -23977,6 +23977,7 @@ from previous Chrome versions.
<int value="-2097515669" label="disable-cast"/> <int value="-2097515669" label="disable-cast"/>
<int value="-2091404586" label="TabStripKeyboardFocus:enabled"/> <int value="-2091404586" label="TabStripKeyboardFocus:enabled"/>
<int value="-2090484194" label="ContextualSearchUrlActions:disabled"/> <int value="-2090484194" label="ContextualSearchUrlActions:disabled"/>
<int value="-2083998415" label="VrLaunchIntent:enabled"/>
<int value="-2083195884" label="enable-firewall-hole-punching"/> <int value="-2083195884" label="enable-firewall-hole-punching"/>
<int value="-2082042818" <int value="-2082042818"
label="AutofillCreditCardLastUsedDateDisplay:enabled"/> label="AutofillCreditCardLastUsedDateDisplay:enabled"/>
...@@ -24914,6 +24915,7 @@ from previous Chrome versions. ...@@ -24914,6 +24915,7 @@ from previous Chrome versions.
<int value="593707592" label="disable-network-portal-notification"/> <int value="593707592" label="disable-network-portal-notification"/>
<int value="596106994" label="CustomFeedbackUi:enabled"/> <int value="596106994" label="CustomFeedbackUi:enabled"/>
<int value="598827460" label="enable-roboto-font-ui"/> <int value="598827460" label="enable-roboto-font-ui"/>
<int value="598926697" label="VrLaunchIntent:disabled"/>
<int value="600037637" label="AndroidSigninPromos:enabled"/> <int value="600037637" label="AndroidSigninPromos:enabled"/>
<int value="602117675" label="NTPBookmarkSuggestions:enabled"/> <int value="602117675" label="NTPBookmarkSuggestions:enabled"/>
<int value="603326800" label="UsePasswordSeparatedSigninFlow:enabled"/> <int value="603326800" label="UsePasswordSeparatedSigninFlow:enabled"/>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment