Commit b1e9929b authored by Michael Thiessen's avatar Michael Thiessen Committed by Commit Bot

VR: Daydream Home launch Samsung device support

This CL adds support for the S8/S9 running O, which no longer change
resolution when entering VR.

However, the user can still change resolution manually and Chrome could
end up in the wrong resolution when launched from Daydream, so we handle
this case by just killing the process and returning to Daydream, so that
when Chrome is re-launched it's launched into the correct density. It
turned out to be way too difficult to properly support Activity
recreation while avoiding showing 2D UI to users.

Bug: 817987, 822292
Change-Id: Ia8fa7bca41831810c2fa9f17088ba3049c6253f3
Reviewed-on: https://chromium-review.googlesource.com/971942Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarYash Malik <ymalik@chromium.org>
Commit-Queue: Michael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545178}
parent 7826cf96
......@@ -2297,4 +2297,15 @@ public abstract class ChromeActivity extends AsyncInitializationActivity
if (VrShellDelegate.isInVr() && !VrIntentUtils.isVrIntent(intent)) return false;
return super.startActivityIfNeeded(intent, requestCode, options);
}
/**
* If the density of the device changes while Chrome is in the background (not resumed), we
* won't have received an onConfigurationChanged yet for this new density. In this case, the
* density this Activity thinks it's in, and the actual display density will differ.
* @return The density this Activity thinks it's in (the density it was in last time it was in
* the resumed state).
*/
public float getLastActiveDensity() {
return mDensityDpi;
}
}
......@@ -4,11 +4,16 @@
package org.chromium.chrome.browser.vr;
import android.app.Activity;
import android.os.Bundle;
import org.chromium.base.ApplicationStatus;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
import java.lang.ref.WeakReference;
/**
* 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
......@@ -27,6 +32,20 @@ public class VrMainActivity extends ChromeLauncherActivity {
// This Launcher may be launched through an alias, which leads to vrmode not being correctly
// set, so we need to set it here as a fallback. b/65271215
VrShellDelegate.setVrModeEnabled(this);
for (WeakReference<Activity> weakActivity : ApplicationStatus.getRunningActivities()) {
final Activity activity = weakActivity.get();
if (activity == null) continue;
if (activity instanceof ChromeActivity) {
if (VrShellDelegate.willChangeDensityInVr((ChromeActivity) activity)) {
// In the rare case that entering VR will trigger a density change (and hence
// an Activity recreation), just return to Daydream home and kill the process,
// as there's no good way to recreate without showing 2D UI in-headset.
finish();
System.exit(0);
}
}
}
super.onCreate(savedInstanceState);
}
}
......@@ -35,6 +35,7 @@ import android.widget.FrameLayout;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.CollectionUtil;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.PackageUtils;
......@@ -66,7 +67,9 @@ import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Manages interactions with the VR Shell.
......@@ -122,6 +125,13 @@ public class VrShellDelegate
private static final String GVR_KEYBOARD_MARKET_URI =
"market://details?id=" + GVR_KEYBOARD_PACKAGE_ID;
private static final String SAMSUNG_GALAXY_PREFIX = "SM-";
private static final Set<String> SAMSUNG_GALAXY_8_MODELS =
Collections.unmodifiableSet(CollectionUtil.newHashSet("G950", "N950", "G955", "G892"));
private static final Set<String> SAMSUNG_GALAXY_8_ALT_MODELS = Collections.unmodifiableSet(
CollectionUtil.newHashSet("SC-02J", "SCV36", "SC-03J", "SCV35", "SC-01K", "SCV37"));
// This value is intentionally probably overkill. This is the time we need to wait from when
// Chrome is resumed, to when Chrome actually renders a black frame, so that we can cancel the
// stay_hidden animation and not see a white monoscopic frame in-headset. 150ms is definitely
......@@ -440,7 +450,7 @@ public class VrShellDelegate
registerVrAssetsComponentIfDaydreamUser(api.isDaydreamCurrentViewer());
}
if (ApplicationStatus.getStateForActivity(activity) == ActivityState.RESUMED
&& activitySupportsVrBrowsing(activity)) {
&& !willChangeDensityInVr(activity)) {
registerDaydreamIntent(api, activity);
}
api.close();
......@@ -663,11 +673,15 @@ public class VrShellDelegate
// Only enable ChromeVR (VrShell) on Daydream devices as it currently needs a Daydream
// controller.
if (vrSupportLevel != VrSupportLevel.VR_DAYDREAM) return false;
if (deviceChangesDensityInVr()) return false;
return ChromeFeatureList.isEnabled(ChromeFeatureList.VR_BROWSING);
}
private static boolean deviceChangesDensityInVr() {
// TODO(mthiesse): Should have package visibility only. We need to unify our vr and vr_shell
// packages.
public static boolean willChangeDensityInVr(ChromeActivity activity) {
// Only N+ support launching in VR at all, other OS versions don't care about this.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false;
// If the screen density changed while in VR, we have to disable the VR browser as java UI
// used or created by VR browsing will be broken.
if (sInstance != null) {
......@@ -675,9 +689,18 @@ public class VrShellDelegate
if (sInstance.mVrSupportLevel != VrSupportLevel.VR_DAYDREAM) return false;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false;
Display display = DisplayAndroidManager.getDefaultDisplayForContext(
ContextUtils.getApplicationContext());
DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
if (activity.getLastActiveDensity() != 0
&& (int) activity.getLastActiveDensity() != metrics.densityDpi) {
return true;
}
if (!deviceCanChangeResolutionForVr()) return false;
Display.Mode[] modes = display.getSupportedModes();
// Devices with only one mode won't switch modes while in VR.
if (modes.length <= 1) return false;
......@@ -690,8 +713,6 @@ public class VrShellDelegate
// We actually can't use display.getMode() to get the current mode as that just always
// returns the same mode ignoring the override, so we just check that our current display
// size is not equal to the vr mode size.
DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
if (vr_mode.getPhysicalWidth() != metrics.widthPixels
&& vr_mode.getPhysicalWidth() != metrics.heightPixels) {
return true;
......@@ -703,6 +724,20 @@ public class VrShellDelegate
return false;
}
private static boolean deviceCanChangeResolutionForVr() {
// Samsung devices no longer change density when entering VR on O+.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) return false;
String model = android.os.Build.MODEL;
if (SAMSUNG_GALAXY_8_ALT_MODELS.contains(model)) return true;
// Only Samsung devices change resolution in VR.
if (!model.startsWith(SAMSUNG_GALAXY_PREFIX)) return false;
CharSequence modelNumber = model.subSequence(3, 7);
// Only S8(+) and Note 8 models change resolution in VR.
if (!SAMSUNG_GALAXY_8_MODELS.contains(modelNumber)) return false;
return true;
}
/**
* @return Whether or not VR is supported on this platform.
*/
......@@ -904,7 +939,7 @@ public class VrShellDelegate
/* package */ boolean isVrBrowsingEnabled() {
return isVrShellEnabled(mVrSupportLevel) && activitySupportsVrBrowsing(mActivity)
&& isDaydreamCurrentViewer();
&& isDaydreamCurrentViewer() && !willChangeDensityInVr(mActivity);
}
private void enterVr(final boolean tentativeWebVrMode) {
......@@ -1072,7 +1107,7 @@ public class VrShellDelegate
return;
}
instance.onAutopresentIntent();
} else if (isVrShellEnabled(instance.mVrSupportLevel)) {
} else if (instance.isVrBrowsingEnabled()) {
if (DEBUG_LOGS) Log.i(TAG, "onNewIntentWithNative: vr");
instance.onVrIntent();
} else {
......@@ -1216,7 +1251,7 @@ public class VrShellDelegate
// If vr shell is not enabled and this is not a web vr request, then return false.
boolean presenting = mRequestedWebVr || mListeningForWebVrActivate
|| (justCompletedDon && mListeningForWebVrActivateBeforePause) || mAutopresentWebVr;
if (!isVrShellEnabled(mVrSupportLevel) && !presenting) return false;
if (!isVrBrowsingEnabled() && !presenting) return false;
return true;
}
......@@ -1367,7 +1402,7 @@ public class VrShellDelegate
}
if (mVrSupportLevel != VrSupportLevel.VR_DAYDREAM) return;
if (isVrShellEnabled(mVrSupportLevel) && activitySupportsVrBrowsing(mActivity)) {
if (isVrBrowsingEnabled()) {
// Perform slow initialization asynchronously.
new Handler().post(new Runnable() {
@Override
......@@ -1385,14 +1420,11 @@ public class VrShellDelegate
if (mEnterVrOnStartup) {
// This means that Chrome was started with a VR intent, so we should enter VR.
// TODO(crbug.com/776235): VR intents are dispatched to ChromeActivity via a launcher
// which should handle the DON flow to simplify the logic in VrShellDelegate.
// TODO(crbug.com/776235): The launcher should ensure that the DON flow has been run
// prior to starting Chrome.
assert !mProbablyInDon;
if (DEBUG_LOGS) Log.i(TAG, "onResume: entering VR mode for VR intent");
if (enterVrInternal() == ENTER_VR_CANCELLED) {
cancelPendingVrEntry();
}
enterVrAfterDon();
} else if (mDonSucceeded) {
handleDonFlowSuccess();
} else {
......
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