Commit 02aa6c71 authored by Michael Thiessen's avatar Michael Thiessen Committed by Commit Bot

Support Daydream's 2D-in-VR rendering path.

Basically, this CL ensures that when Daydream's 2D-in-VR path is enabled
that we (almost) never show VR-supporting chrome activities in 2D when
the user is in headset, even if Chrome is launched with a 2D intent.

Intents sent to the 2D launcher that can be supported by VR rendering
are forwarded to the VR launcher when the user is in headset (intents
that aren't supported by VR rendering will still show in Daydream's
2D-in-VR rendering path). If a ChromeActivity that supports VR is
launched directly while in headset, we just add the Daydream category to
the intent and continue with intent processing as usual.

If, somehow, Chrome was launched in 2D, then resumed while in headset,
Chrome will still render using the 2D-in-VR flow. This is fine for now
as Chrome will still be usable, and this situation is very difficult to
get into, but the user will probably be confused :P


This CL also avoids showing the request to exit VR prompt when 2D-in-VR
is supported, favoring immediately switching to 2D rendering mode and
showing the dialog or launching the Activity that triggered the request,
then returning to VR rendering after returning to Chrome.

Bug: 826732
Change-Id: I1a09f201554e4e3171f5657b0fb667acc6f4c784
Reviewed-on: https://chromium-review.googlesource.com/999997Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarYash Malik <ymalik@chromium.org>
Commit-Queue: Michael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549248}
parent 2c9efdd8
......@@ -303,9 +303,10 @@ public abstract class ChromeActivity extends AsyncInitializationActivity
public void 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())) {
// We need to explicitly enable VR mode here so that the system doesn't kick us out of VR,
// or drop us into the 2D-in-VR rendering mode, while we prepare for VR rendering.
if (VrIntentUtils.isVrIntent(getIntent())
|| VrIntentUtils.wouldUse2DInVrRenderingMode(this)) {
VrShellDelegate.setVrModeEnabled(this, true);
}
......@@ -2299,14 +2300,14 @@ public abstract class ChromeActivity extends AsyncInitializationActivity
@Override
public void startActivity(Intent intent, Bundle options) {
if (!VrShellDelegate.isInVr() || VrIntentUtils.isVrIntent(intent)) {
if (VrShellDelegate.canLaunch2DIntents() || VrIntentUtils.isVrIntent(intent)) {
super.startActivity(intent, options);
return;
}
VrShellDelegate.requestToExitVr(new OnExitVrRequestListener() {
@Override
public void onSucceeded() {
if (VrShellDelegate.isInVr()) {
if (!VrShellDelegate.canLaunch2DIntents()) {
throw new IllegalStateException("Still in VR after having exited VR.");
}
startActivity(intent, options);
......
......@@ -178,7 +178,7 @@ public class ChromeApplication extends Application {
@Override
public void startActivity(Intent intent, Bundle options) {
if (!VrShellDelegate.isInVr() || VrIntentUtils.isVrIntent(intent)) {
if (VrShellDelegate.canLaunch2DIntents() || VrIntentUtils.isVrIntent(intent)) {
super.startActivity(intent, options);
return;
}
......@@ -186,7 +186,7 @@ public class ChromeApplication extends Application {
VrShellDelegate.requestToExitVr(new OnExitVrRequestListener() {
@Override
public void onSucceeded() {
if (VrShellDelegate.isInVr()) {
if (!VrShellDelegate.canLaunch2DIntents()) {
throw new IllegalStateException("Still in VR after having exited VR.");
}
startActivity(intent, options);
......
......@@ -29,6 +29,13 @@ public class ChromeLauncherActivity extends Activity {
// VR Intents should only ever get routed through the VrMainActivity.
assert !VrIntentUtils.isVrIntent(getIntent());
// Handle Daydream's 2D-in-VR rendering mode by launching 2D intents in VR if we're in
// VR mode and the intent is supported in VR.
if (VrIntentUtils.maybeForwardToVrLauncher(getIntent(), this)) {
finish();
return;
}
@LaunchIntentDispatcher.Action
int dispatchAction = LaunchIntentDispatcher.dispatch(this, getIntent());
switch (dispatchAction) {
......
......@@ -93,4 +93,9 @@ public interface VrDaydreamApi {
* Closes this DaydreamApi instance.
*/
void close();
/**
* @return Whether 2D-in-VR rendering mode is currently supported.
*/
boolean supports2dInVr();
}
......@@ -151,6 +151,11 @@ public class VrDaydreamApiImpl implements VrDaydreamApi {
return isDaydreamReadyDevice() && DaydreamApi.isInVrSession(mContext);
}
@Override
public boolean supports2dInVr() {
return isDaydreamReadyDevice() && DaydreamApi.supports2dInVr(mContext);
}
@Override
public void close() {
if (mDaydreamApi == null) return;
......
......@@ -8,6 +8,8 @@ import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import org.chromium.base.VisibleForTesting;
......@@ -168,14 +170,46 @@ public class VrIntentUtils {
* @param activity The activity context to launch the intent from.
*/
public static void launchInVr(Intent intent, Activity activity) {
VrClassesWrapper wrapper = VrShellDelegate.getVrClassesWrapper();
if (wrapper == null) return;
VrDaydreamApi api = wrapper.createVrDaydreamApi(activity);
VrDaydreamApi api = createDaydreamApi(activity);
if (api == null) return;
api.launchInVr(intent);
api.close();
}
/**
* @param intent The intent to possibly forward to the VR launcher.
* @return whether the intent was forwarded to the VR launcher.
*/
public static boolean maybeForwardToVrLauncher(Intent intent, Activity activity) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) return false;
if (wouldUse2DInVrRenderingMode(activity)) {
Intent vrIntent = new Intent(intent);
vrIntent.setComponent(null);
vrIntent.setPackage(activity.getPackageName());
vrIntent.addCategory(VrIntentUtils.DAYDREAM_CATEGORY);
if (vrIntent.resolveActivity(activity.getPackageManager()) != null) {
VrIntentUtils.launchInVr(vrIntent, activity);
return true;
}
}
return false;
}
/**
* @param activity A context for reading the current device configuration.
* @return Whether launching a non-VR Activity would trigger the 2D-in-VR rendering path.
*/
public static boolean wouldUse2DInVrRenderingMode(Activity activity) {
Configuration config = activity.getResources().getConfiguration();
int uiMode = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
if (uiMode != Configuration.UI_MODE_TYPE_VR_HEADSET) return false;
VrDaydreamApi api = createDaydreamApi(activity);
if (api == null) return false;
boolean supports2dInVr = api.supports2dInVr();
api.close();
return supports2dInVr;
}
/**
* @return Whether the intent is fired from the recent apps overview.
*/
......@@ -191,4 +225,10 @@ public class VrIntentUtils {
intent.removeCategory(DAYDREAM_CATEGORY);
assert !isVrIntent(intent);
}
private static VrDaydreamApi createDaydreamApi(Activity activity) {
VrClassesWrapper wrapper = VrShellDelegate.getVrClassesWrapper();
if (wrapper == null) return null;
return wrapper.createVrDaydreamApi(activity);
}
}
......@@ -419,6 +419,16 @@ public class VrShellDelegate
return sInstance.mInVr;
}
/**
* @return Whether 2D intents can safely be launched without showing non-VR UI to users in VR
* headsets.
*/
public static boolean canLaunch2DIntents() {
if (!isInVr()) return true;
return sInstance.mVrDaydreamApi.supports2dInVr()
&& !sVrModeEnabledActivitys.contains(sInstance.mActivity);
}
/**
* See {@link ChromeActivity#handleBackPressed}
* Only handles the back press while in VR.
......@@ -581,7 +591,8 @@ public class VrShellDelegate
listener.onSucceeded();
return;
}
sInstance.requestToExitVrInternal(listener, reason, true);
sInstance.requestToExitVrInternal(
listener, reason, !sInstance.mVrDaydreamApi.supports2dInVr());
}
/**
......@@ -652,7 +663,12 @@ public class VrShellDelegate
* This is called when ChromeTabbedActivity gets a new intent before native is initialized.
*/
public static void maybeHandleVrIntentPreNative(ChromeActivity activity, Intent intent) {
if (!VrIntentUtils.isVrIntent(intent)) return;
if (!VrIntentUtils.isVrIntent(intent)) {
if (!VrIntentUtils.wouldUse2DInVrRenderingMode(activity)) return;
// This is to handle intents that are sent directly to ChromeActivitys, bypassing the
// launcher.
intent.addCategory(VrIntentUtils.DAYDREAM_CATEGORY);
}
if (sInstance != null && !sInstance.mInternalIntentUsedToStartVr) {
sInstance.swapHostActivity(activity, false /* disableVrMode */);
......@@ -1769,7 +1785,13 @@ public class VrShellDelegate
if (maybeCloseVrCct()) return;
mStopped = false;
if (mDonSucceeded) setWindowModeForVr();
if (mInVr && !mVrDaydreamApi.isInVrSession()) shutdownVr(true, false);
if (mInVr) {
if (!mVrDaydreamApi.isInVrSession()) {
shutdownVr(true, false);
} else {
setVrModeEnabled(mActivity, true);
}
}
}
private void onStop() {
......@@ -1802,6 +1824,12 @@ public class VrShellDelegate
assert !mShowingDaydreamDoff;
if (!isDaydreamCurrentViewer()) return false;
if (mVrDaydreamApi.supports2dInVr()) {
setVrModeEnabled(mActivity, false);
callOnExitVrRequestListener(true);
return true;
}
// To avoid taking the user out of VR mode when started for auto-presentation, never show
// DOFF and bail to Daydream if we're forced to leave Chrome. We still show DOFF if VR
// services are out of date though.
......
......@@ -554,6 +554,13 @@ public class VrShellImpl
mVrModalPresenter.closeCurrentDialog();
}
@Override
public void onWindowFocusChanged(boolean focused) {
// This handles the case where we open 2D popups in 2D-in-VR. We lose window focus, but stay
// resumed, so we have to listen for focus gain to know when the popup was closed.
if (focused) VrShellDelegate.setVrModeEnabled(mActivity, true);
}
@CalledByNative
public void setContentCssSize(float width, float height, float dpr) {
ThreadUtils.assertOnUiThread();
......
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