| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.vr_shell; | 5 package org.chromium.chrome.browser.vr_shell; |
| 6 | 6 |
| 7 import android.app.Activity; | 7 import android.app.Activity; |
| 8 import android.app.PendingIntent; | 8 import android.app.PendingIntent; |
| 9 import android.content.ComponentName; | 9 import android.content.ComponentName; |
| 10 import android.content.Intent; | 10 import android.content.Intent; |
| 11 import android.content.pm.ActivityInfo; | 11 import android.content.pm.ActivityInfo; |
| 12 import android.os.StrictMode; | 12 import android.os.StrictMode; |
| 13 import android.os.SystemClock; | 13 import android.os.SystemClock; |
| 14 import android.support.annotation.IntDef; |
| 14 import android.view.View; | 15 import android.view.View; |
| 15 import android.view.ViewGroup; | 16 import android.view.ViewGroup; |
| 16 import android.view.ViewGroup.LayoutParams; | 17 import android.view.ViewGroup.LayoutParams; |
| 17 import android.view.WindowManager; | 18 import android.view.WindowManager; |
| 18 import android.widget.FrameLayout; | 19 import android.widget.FrameLayout; |
| 19 | 20 |
| 20 import org.chromium.base.Log; | 21 import org.chromium.base.Log; |
| 21 import org.chromium.base.annotations.CalledByNative; | 22 import org.chromium.base.annotations.CalledByNative; |
| 22 import org.chromium.base.annotations.JNINamespace; | 23 import org.chromium.base.annotations.JNINamespace; |
| 23 import org.chromium.base.library_loader.LibraryLoader; | 24 import org.chromium.base.library_loader.LibraryLoader; |
| 24 import org.chromium.chrome.browser.ChromeFeatureList; | 25 import org.chromium.chrome.browser.ChromeFeatureList; |
| 25 import org.chromium.chrome.browser.ChromeTabbedActivity; | 26 import org.chromium.chrome.browser.ChromeTabbedActivity; |
| 26 import org.chromium.chrome.browser.tab.EmptyTabObserver; | 27 import org.chromium.chrome.browser.tab.EmptyTabObserver; |
| 27 import org.chromium.chrome.browser.tab.Tab; | 28 import org.chromium.chrome.browser.tab.Tab; |
| 28 import org.chromium.chrome.browser.tab.TabObserver; | 29 import org.chromium.chrome.browser.tab.TabObserver; |
| 29 import org.chromium.content_public.common.BrowserControlsState; | 30 import org.chromium.content_public.common.BrowserControlsState; |
| 30 | 31 |
| 32 import java.lang.annotation.Retention; |
| 33 import java.lang.annotation.RetentionPolicy; |
| 31 import java.lang.reflect.Constructor; | 34 import java.lang.reflect.Constructor; |
| 32 import java.lang.reflect.InvocationTargetException; | 35 import java.lang.reflect.InvocationTargetException; |
| 33 | 36 |
| 34 /** | 37 /** |
| 35 * Manages interactions with the VR Shell. | 38 * Manages interactions with the VR Shell. |
| 36 */ | 39 */ |
| 37 @JNINamespace("vr_shell") | 40 @JNINamespace("vr_shell") |
| 38 public class VrShellDelegate { | 41 public class VrShellDelegate { |
| 39 private static final String TAG = "VrShellDelegate"; | 42 private static final String TAG = "VrShellDelegate"; |
| 40 // Pseudo-random number to avoid request id collisions. | 43 // Pseudo-random number to avoid request id collisions. |
| 41 public static final int EXIT_VR_RESULT = 721251; | 44 public static final int EXIT_VR_RESULT = 721251; |
| 42 | 45 |
| 46 public static final int ENTER_VR_NOT_NECESSARY = 0; |
| 47 public static final int ENTER_VR_CANCELLED = 1; |
| 48 public static final int ENTER_VR_REQUESTED = 2; |
| 49 @Retention(RetentionPolicy.SOURCE) |
| 50 @IntDef({ENTER_VR_NOT_NECESSARY, ENTER_VR_CANCELLED, ENTER_VR_REQUESTED}) |
| 51 public @interface EnterVRResult {} |
| 52 |
| 43 // TODO(bshe): These should be replaced by string provided by NDK. Currently
, it only available | 53 // TODO(bshe): These should be replaced by string provided by NDK. Currently
, it only available |
| 44 // in SDK and we don't want add dependency to SDK just to get these strings. | 54 // in SDK and we don't want add dependency to SDK just to get these strings. |
| 45 private static final String DAYDREAM_VR_EXTRA = "android.intent.extra.VR_LAU
NCH"; | 55 private static final String DAYDREAM_VR_EXTRA = "android.intent.extra.VR_LAU
NCH"; |
| 46 private static final String DAYDREAM_CATEGORY = "com.google.intent.category.
DAYDREAM"; | 56 private static final String DAYDREAM_CATEGORY = "com.google.intent.category.
DAYDREAM"; |
| 47 private static final String CARDBOARD_CATEGORY = "com.google.intent.category
.CARDBOARD"; | 57 private static final String CARDBOARD_CATEGORY = "com.google.intent.category
.CARDBOARD"; |
| 48 | 58 |
| 49 private static final String VR_ACTIVITY_ALIAS = | 59 private static final String VR_ACTIVITY_ALIAS = |
| 50 "org.chromium.chrome.browser.VRChromeTabbedActivity"; | 60 "org.chromium.chrome.browser.VRChromeTabbedActivity"; |
| 51 private static final String DAYDREAM_DON_TYPE = "DAYDREAM_DON_TYPE"; | |
| 52 | |
| 53 private static final int DAYDREAM_DON_TYPE_VR_SHELL = 0; | |
| 54 private static final int DAYDREAM_DON_TYPE_WEBVR = 1; | |
| 55 private static final int DAYDREAM_DON_TYPE_AUTO = 2; | |
| 56 | 61 |
| 57 private static final long REENTER_VR_TIMEOUT_MS = 1000; | 62 private static final long REENTER_VR_TIMEOUT_MS = 1000; |
| 58 | 63 |
| 59 private final ChromeTabbedActivity mActivity; | 64 private final ChromeTabbedActivity mActivity; |
| 60 private final TabObserver mTabObserver; | 65 private final TabObserver mTabObserver; |
| 66 private final Intent mEnterVRIntent; |
| 61 | 67 |
| 62 private boolean mVrAvailable; | 68 private boolean mVrAvailable; |
| 63 private Boolean mVrShellEnabled; | 69 private Boolean mVrShellEnabled; |
| 64 | 70 |
| 65 private Class<? extends VrShell> mVrShellClass; | 71 private Class<? extends VrShell> mVrShellClass; |
| 66 private Class<? extends NonPresentingGvrContext> mNonPresentingGvrContextCla
ss; | 72 private Class<? extends NonPresentingGvrContext> mNonPresentingGvrContextCla
ss; |
| 67 private Class<? extends VrDaydreamApi> mVrDaydreamApiClass; | 73 private Class<? extends VrDaydreamApi> mVrDaydreamApiClass; |
| 68 private Class<? extends VrCoreVersionChecker> mVrCoreVersionCheckerClass; | 74 private Class<? extends VrCoreVersionChecker> mVrCoreVersionCheckerClass; |
| 69 private VrShell mVrShell; | 75 private VrShell mVrShell; |
| 70 private NonPresentingGvrContext mNonPresentingGvrContext; | 76 private NonPresentingGvrContext mNonPresentingGvrContext; |
| 71 private VrDaydreamApi mVrDaydreamApi; | 77 private VrDaydreamApi mVrDaydreamApi; |
| 72 private VrCoreVersionChecker mVrCoreVersionChecker; | 78 private VrCoreVersionChecker mVrCoreVersionChecker; |
| 73 private boolean mInVr; | 79 private boolean mInVr; |
| 74 private int mRestoreSystemUiVisibilityFlag = -1; | 80 private int mRestoreSystemUiVisibilityFlag = -1; |
| 75 private int mRestoreOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIE
D; | 81 private int mRestoreOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIE
D; |
| 76 private long mNativeVrShellDelegate; | 82 private long mNativeVrShellDelegate; |
| 77 private Tab mTab; | 83 private Tab mTab; |
| 78 private boolean mRequestedWebVR; | 84 private boolean mRequestedWebVR; |
| 79 private long mLastVRExit; | 85 private long mLastVRExit; |
| 80 | 86 |
| 81 public VrShellDelegate(ChromeTabbedActivity activity) { | 87 public VrShellDelegate(ChromeTabbedActivity activity) { |
| 82 mActivity = activity; | 88 mActivity = activity; |
| 83 mVrAvailable = maybeFindVrClasses() && isVrCoreCompatible(); | 89 mVrAvailable = maybeFindVrClasses() && isVrCoreCompatible() && createVrD
aydreamApi(); |
| 84 createVrDaydreamApi(); | 90 if (mVrAvailable) { |
| 91 mEnterVRIntent = mVrDaydreamApi.createVrIntent( |
| 92 new ComponentName(mActivity, VR_ACTIVITY_ALIAS)); |
| 93 } else { |
| 94 mEnterVRIntent = null; |
| 95 } |
| 85 mTabObserver = new EmptyTabObserver() { | 96 mTabObserver = new EmptyTabObserver() { |
| 86 @Override | 97 @Override |
| 87 public void onContentChanged(Tab tab) { | 98 public void onContentChanged(Tab tab) { |
| 88 if (tab.getNativePage() != null || tab.isShowingSadTab()) { | 99 if (tab.getNativePage() != null || tab.isShowingSadTab()) { |
| 89 // For now we don't handle native pages. crbug.com/661609 | 100 // For now we don't handle native pages. crbug.com/661609 |
| 90 exitVRIfNecessary(true); | 101 exitVRIfNecessary(true); |
| 91 } | 102 } |
| 92 } | 103 } |
| 93 | 104 |
| 94 @Override | 105 @Override |
| 95 public void onWebContentsSwapped(Tab tab, boolean didStartLoad, bool
ean didFinishLoad) { | 106 public void onWebContentsSwapped(Tab tab, boolean didStartLoad, bool
ean didFinishLoad) { |
| 96 // TODO(mthiesse): Update the native WebContents pointer and com
positor. This | 107 // TODO(mthiesse): Update the native WebContents pointer and com
positor. This |
| 97 // doesn't seem to get triggered in VR Shell currently, but that
's likely to change | 108 // doesn't seem to get triggered in VR Shell currently, but that
's likely to change |
| 98 // when we have omnibar navigation. | 109 // when we have omnibar navigation. |
| 99 } | 110 } |
| 100 }; | 111 }; |
| 101 } | 112 } |
| 102 | 113 |
| 103 /** | 114 /** |
| 104 * Should be called once the native library is loaded so that the native por
tion of this | 115 * Should be called once the native library is loaded so that the native por
tion of this |
| 105 * class can be initialized. | 116 * class can be initialized. |
| 106 */ | 117 */ |
| 107 public void onNativeLibraryReady() { | 118 public void onNativeLibraryReady() { |
| 108 if (mVrAvailable) { | 119 if (!mVrAvailable) return; |
| 109 mNativeVrShellDelegate = nativeInit(); | 120 mNativeVrShellDelegate = nativeInit(); |
| 110 } | |
| 111 } | 121 } |
| 112 | 122 |
| 113 @SuppressWarnings("unchecked") | 123 @SuppressWarnings("unchecked") |
| 114 private boolean maybeFindVrClasses() { | 124 private boolean maybeFindVrClasses() { |
| 115 try { | 125 try { |
| 116 mVrShellClass = (Class<? extends VrShell>) Class.forName( | 126 mVrShellClass = (Class<? extends VrShell>) Class.forName( |
| 117 "org.chromium.chrome.browser.vr_shell.VrShellImpl"); | 127 "org.chromium.chrome.browser.vr_shell.VrShellImpl"); |
| 118 mNonPresentingGvrContextClass = | 128 mNonPresentingGvrContextClass = |
| 119 (Class<? extends NonPresentingGvrContext>) Class.forName( | 129 (Class<? extends NonPresentingGvrContext>) Class.forName( |
| 120 "org.chromium.chrome.browser.vr_shell.NonPresentingG
vrContextImpl"); | 130 "org.chromium.chrome.browser.vr_shell.NonPresentingG
vrContextImpl"); |
| 121 mVrDaydreamApiClass = (Class<? extends VrDaydreamApi>) Class.forName
( | 131 mVrDaydreamApiClass = (Class<? extends VrDaydreamApi>) Class.forName
( |
| 122 "org.chromium.chrome.browser.vr_shell.VrDaydreamApiImpl"); | 132 "org.chromium.chrome.browser.vr_shell.VrDaydreamApiImpl"); |
| 123 mVrCoreVersionCheckerClass = (Class<? extends VrCoreVersionChecker>)
Class.forName( | 133 mVrCoreVersionCheckerClass = (Class<? extends VrCoreVersionChecker>)
Class.forName( |
| 124 "org.chromium.chrome.browser.vr_shell.VrCoreVersionCheckerIm
pl"); | 134 "org.chromium.chrome.browser.vr_shell.VrCoreVersionCheckerIm
pl"); |
| 125 return true; | 135 return true; |
| 126 } catch (ClassNotFoundException e) { | 136 } catch (ClassNotFoundException e) { |
| 127 mVrShellClass = null; | 137 mVrShellClass = null; |
| 128 mNonPresentingGvrContextClass = null; | 138 mNonPresentingGvrContextClass = null; |
| 129 mVrDaydreamApiClass = null; | 139 mVrDaydreamApiClass = null; |
| 130 mVrCoreVersionCheckerClass = null; | 140 mVrCoreVersionCheckerClass = null; |
| 131 return false; | 141 return false; |
| 132 } | 142 } |
| 133 } | 143 } |
| 134 | 144 |
| 135 /** | 145 /** |
| 136 * Handle a VR intent, entering VR in the process. | 146 * Handle a VR intent, entering VR in the process. |
| 137 */ | 147 */ |
| 138 public boolean enterVRFromIntent(Intent intent) { | 148 public void enterVRFromIntent(Intent intent) { |
| 149 if (!mVrAvailable) return; |
| 139 assert isVrIntent(intent); | 150 assert isVrIntent(intent); |
| 140 int transitionType = intent.getIntExtra(DAYDREAM_DON_TYPE, DAYDREAM_DON_
TYPE_VR_SHELL); | 151 if (enterVR()) { |
| 141 if (!isVrShellEnabled()) { | 152 if (mRequestedWebVR) { |
| 142 assert transitionType != DAYDREAM_DON_TYPE_VR_SHELL; | 153 nativeSetPresentResult(mNativeVrShellDelegate, true); |
| 154 mVrShell.setWebVrModeEnabled(true); |
| 155 } |
| 156 } else { |
| 157 if (mRequestedWebVR) nativeSetPresentResult(mNativeVrShellDelegate,
false); |
| 158 mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent()); |
| 143 } | 159 } |
| 144 | 160 |
| 145 boolean inWebVR = transitionType == DAYDREAM_DON_TYPE_WEBVR | |
| 146 || (transitionType == DAYDREAM_DON_TYPE_AUTO | |
| 147 && (!isVrShellEnabled() || mRequestedWebVR)); | |
| 148 mRequestedWebVR = false; | 161 mRequestedWebVR = false; |
| 149 Tab tab = mActivity.getActivityTab(); | 162 } |
| 150 if (!canEnterVR(inWebVR, tab)) return false; | |
| 151 | 163 |
| 152 // TODO(mthiesse): We should handle switching between webVR and VR Shell
mode through | 164 private boolean enterVR() { |
| 153 // intents. | |
| 154 if (mInVr) return true; | 165 if (mInVr) return true; |
| 155 | |
| 156 mVrDaydreamApi.setVrModeEnabled(true); | 166 mVrDaydreamApi.setVrModeEnabled(true); |
| 157 | 167 |
| 158 mTab = tab; | 168 Tab tab = mActivity.getActivityTab(); |
| 159 mTab.addObserver(mTabObserver); | 169 if (!canEnterVR(tab)) return false; |
| 160 | 170 |
| 161 mRestoreOrientation = mActivity.getRequestedOrientation(); | 171 mRestoreOrientation = mActivity.getRequestedOrientation(); |
| 162 mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSC
APE); | 172 mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSC
APE); |
| 163 if (!createVrShell()) { | 173 if (!createVrShell()) { |
| 164 mActivity.setRequestedOrientation(mRestoreOrientation); | 174 mActivity.setRequestedOrientation(mRestoreOrientation); |
| 165 return false; | 175 return false; |
| 166 } | 176 } |
| 177 mInVr = true; |
| 178 mTab = tab; |
| 179 mTab.addObserver(mTabObserver); |
| 167 addVrViews(); | 180 addVrViews(); |
| 168 setupVrModeWindowFlags(); | 181 setupVrModeWindowFlags(); |
| 169 mVrShell.initializeNative(mTab, this); | 182 mVrShell.initializeNative(mTab, this); |
| 170 if (inWebVR) mVrShell.setWebVrModeEnabled(true); | |
| 171 mVrShell.setCloseButtonListener(new Runnable() { | 183 mVrShell.setCloseButtonListener(new Runnable() { |
| 172 @Override | 184 @Override |
| 173 public void run() { | 185 public void run() { |
| 174 exitVRIfNecessary(true); | 186 exitVRIfNecessary(true); |
| 175 } | 187 } |
| 176 }); | 188 }); |
| 177 // onResume needs to be called on GvrLayout after initialization to make
sure DON flow work | 189 // onResume needs to be called on GvrLayout after initialization to make
sure DON flow work |
| 178 // properly. | 190 // properly. |
| 179 mVrShell.resume(); | 191 mVrShell.resume(); |
| 180 mInVr = true; | |
| 181 mTab.updateFullscreenEnabledState(); | 192 mTab.updateFullscreenEnabledState(); |
| 182 return true; | 193 return true; |
| 183 } | 194 } |
| 184 | 195 |
| 185 private boolean canEnterVR(boolean inWebVR, Tab tab) { | 196 private boolean canEnterVR(Tab tab) { |
| 186 if (!LibraryLoader.isInitialized()) { | 197 if (!LibraryLoader.isInitialized()) { |
| 187 return false; | 198 return false; |
| 188 } | 199 } |
| 189 // If vr isn't in the build, or we haven't initialized yet, or vr shell
is not enabled and | 200 // If vr isn't in the build, or we haven't initialized yet, or vr shell
is not enabled and |
| 190 // this is not a web vr request, then return immediately. | 201 // this is not a web vr request, then return immediately. |
| 191 if (!mVrAvailable || mNativeVrShellDelegate == 0 || (!isVrShellEnabled()
&& !inWebVR)) { | 202 if (!mVrAvailable || mNativeVrShellDelegate == 0 |
| 203 || (!isVrShellEnabled() && !mRequestedWebVR)) { |
| 192 return false; | 204 return false; |
| 193 } | 205 } |
| 194 // TODO(mthiesse): When we have VR UI for opening new tabs, etc., allow
VR Shell to be | 206 // TODO(mthiesse): When we have VR UI for opening new tabs, etc., allow
VR Shell to be |
| 195 // entered without any current tabs. | 207 // entered without any current tabs. |
| 196 if (tab == null || tab.getContentViewCore() == null) { | 208 if (tab == null || tab.getContentViewCore() == null) { |
| 197 return false; | 209 return false; |
| 198 } | 210 } |
| 199 | 211 |
| 200 // For now we don't handle native pages. crbug.com/661609 | 212 // For now we don't handle native pages. crbug.com/661609 |
| 201 if (tab.getNativePage() != null || tab.isShowingSadTab()) { | 213 if (tab.getNativePage() != null || tab.isShowingSadTab()) { |
| 202 return false; | 214 return false; |
| 203 } | 215 } |
| 204 return true; | 216 return true; |
| 205 } | 217 } |
| 206 | 218 |
| 219 @CalledByNative |
| 220 private void presentRequested(boolean inWebVR) { |
| 221 // TODO(mthiesse): There's a GVR bug where they're not calling us back w
ith the |
| 222 // intent we ask them to when we call DaydreamApi#launchInVr. As a tempo
rary hack, |
| 223 // remember locally that we want to enter webVR. |
| 224 mRequestedWebVR = inWebVR; |
| 225 switch (enterVRIfNecessary()) { |
| 226 case ENTER_VR_NOT_NECESSARY: |
| 227 mVrShell.setWebVrModeEnabled(true); |
| 228 nativeSetPresentResult(mNativeVrShellDelegate, true); |
| 229 mRequestedWebVR = false; |
| 230 break; |
| 231 case ENTER_VR_CANCELLED: |
| 232 nativeSetPresentResult(mNativeVrShellDelegate, false); |
| 233 mRequestedWebVR = false; |
| 234 break; |
| 235 case ENTER_VR_REQUESTED: |
| 236 break; |
| 237 } |
| 238 } |
| 239 |
| 207 /** | 240 /** |
| 208 * Enters VR Shell, displaying browser UI and tab contents in VR. | 241 * Enters VR Shell if necessary, displaying browser UI and tab contents in V
R. |
| 209 * | |
| 210 * @param inWebVR If true should begin displaying WebVR content rather than
the VrShell UI. | |
| 211 */ | 242 */ |
| 212 @CalledByNative | 243 @EnterVRResult |
| 213 public void enterVRIfNecessary(boolean inWebVR) { | 244 public int enterVRIfNecessary() { |
| 214 if (mInVr) { | 245 if (!mVrAvailable) return ENTER_VR_CANCELLED; |
| 215 if (inWebVR) mVrShell.setWebVrModeEnabled(true); | 246 if (mInVr) return ENTER_VR_NOT_NECESSARY; |
| 216 return; | 247 if (!canEnterVR(mActivity.getActivityTab())) return ENTER_VR_CANCELLED; |
| 217 } | |
| 218 if (!canEnterVR(inWebVR, mActivity.getActivityTab())) return; | |
| 219 | 248 |
| 220 // TODO(mthiesse): There's a GVR bug where they're not calling us back w
ith the intent we | 249 mVrDaydreamApi.launchInVr(getPendingEnterVRIntent()); |
| 221 // ask them to when we call DaydreamApi#launchInVr. As a temporary hack,
remember locally | 250 return ENTER_VR_REQUESTED; |
| 222 // that we want to enter webVR. | |
| 223 mRequestedWebVR = inWebVR; | |
| 224 Intent intent = createDaydreamIntent( | |
| 225 inWebVR ? DAYDREAM_DON_TYPE_WEBVR : DAYDREAM_DON_TYPE_VR_SHELL); | |
| 226 | |
| 227 mVrDaydreamApi.launchInVr( | |
| 228 PendingIntent.getActivity(mActivity, 0, intent, PendingIntent.FL
AG_ONE_SHOT)); | |
| 229 } | 251 } |
| 230 | 252 |
| 231 @CalledByNative | 253 @CalledByNative |
| 232 private boolean exitWebVR() { | 254 private boolean exitWebVR() { |
| 233 if (!mInVr) return false; | 255 if (!mInVr) return false; |
| 234 mVrShell.setWebVrModeEnabled(false); | 256 mVrShell.setWebVrModeEnabled(false); |
| 235 // TODO(bajones): Once VR Shell can be invoked outside of WebVR this | 257 // TODO(bajones): Once VR Shell can be invoked outside of WebVR this |
| 236 // should no longer exit the shell outright. Need a way to determine | 258 // should no longer exit the shell outright. Need a way to determine |
| 237 // how VrShell was created. | 259 // how VrShell was created. |
| 238 shutdownVR(true); | 260 shutdownVR(true); |
| 239 return true; | 261 return true; |
| 240 } | 262 } |
| 241 | 263 |
| 242 /** | 264 /** |
| 243 * Resumes VR Shell. | 265 * Resumes VR Shell. |
| 244 */ | 266 */ |
| 245 public void maybeResumeVR() { | 267 public void maybeResumeVR() { |
| 268 if (!mVrAvailable) return; |
| 269 // TODO(mthiesse): Register the intent when on a page that supports WebV
R, even if VrShell |
| 270 // isn't enabled. |
| 246 if (isVrShellEnabled()) { | 271 if (isVrShellEnabled()) { |
| 247 registerDaydreamIntent(); | 272 registerDaydreamIntent(); |
| 248 } | 273 } |
| 274 // If this is still set, it means the user backed out of the DON flow, a
nd we won't be |
| 275 // receiving an intent from daydream. |
| 276 if (mRequestedWebVR) { |
| 277 nativeSetPresentResult(mNativeVrShellDelegate, false); |
| 278 mRequestedWebVR = false; |
| 279 } |
| 249 | 280 |
| 250 // TODO(bshe): Ideally, we do not need two gvr context exist at the same
time. We can | 281 // TODO(bshe): Ideally, we do not need two gvr context exist at the same
time. We can |
| 251 // probably shutdown non presenting gvr when presenting and create a new
one after exit | 282 // probably shutdown non presenting gvr when presenting and create a new
one after exit |
| 252 // presenting. See crbug.com/655242 | 283 // presenting. See crbug.com/655242 |
| 253 if (mNonPresentingGvrContext != null) { | 284 if (mNonPresentingGvrContext != null) { |
| 254 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites
(); | 285 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites
(); |
| 255 try { | 286 try { |
| 256 mNonPresentingGvrContext.resume(); | 287 mNonPresentingGvrContext.resume(); |
| 257 } catch (IllegalArgumentException e) { | 288 } catch (IllegalArgumentException e) { |
| 258 Log.e(TAG, "Unable to resume mNonPresentingGvrContext", e); | 289 Log.e(TAG, "Unable to resume mNonPresentingGvrContext", e); |
| 259 } finally { | 290 } finally { |
| 260 StrictMode.setThreadPolicy(oldPolicy); | 291 StrictMode.setThreadPolicy(oldPolicy); |
| 261 } | 292 } |
| 262 } | 293 } |
| 263 | 294 |
| 264 if (mInVr) { | 295 if (mInVr) { |
| 265 setupVrModeWindowFlags(); | 296 setupVrModeWindowFlags(); |
| 266 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites
(); | 297 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites
(); |
| 267 try { | 298 try { |
| 268 mVrShell.resume(); | 299 mVrShell.resume(); |
| 269 } catch (IllegalArgumentException e) { | 300 } catch (IllegalArgumentException e) { |
| 270 Log.e(TAG, "Unable to resume VrShell", e); | 301 Log.e(TAG, "Unable to resume VrShell", e); |
| 271 } finally { | 302 } finally { |
| 272 StrictMode.setThreadPolicy(oldPolicy); | 303 StrictMode.setThreadPolicy(oldPolicy); |
| 273 } | 304 } |
| 274 } else if (mLastVRExit + REENTER_VR_TIMEOUT_MS > SystemClock.uptimeMilli
s()) { | 305 } else if (mLastVRExit + REENTER_VR_TIMEOUT_MS > SystemClock.uptimeMilli
s()) { |
| 275 enterVRIfNecessary(mRequestedWebVR); | 306 enterVRIfNecessary(); |
| 276 } | 307 } |
| 277 } | 308 } |
| 278 | 309 |
| 279 /** | 310 /** |
| 280 * Pauses VR Shell. | 311 * Pauses VR Shell. |
| 281 */ | 312 */ |
| 282 public void maybePauseVR() { | 313 public void maybePauseVR() { |
| 283 if (isVrShellEnabled()) { | 314 if (!mVrAvailable) return; |
| 284 unregisterDaydreamIntent(); | 315 unregisterDaydreamIntent(); |
| 285 } | |
| 286 | 316 |
| 287 if (mNonPresentingGvrContext != null) { | 317 if (mNonPresentingGvrContext != null) { |
| 288 mNonPresentingGvrContext.pause(); | 318 mNonPresentingGvrContext.pause(); |
| 289 } | 319 } |
| 290 | 320 |
| 291 // TODO(mthiesse): When VR Shell lives in its own activity, and integrat
es with Daydream | 321 // TODO(mthiesse): When VR Shell lives in its own activity, and integrat
es with Daydream |
| 292 // home, pause instead of exiting VR here. For now, because VR Apps shou
ldn't show up in the | 322 // home, pause instead of exiting VR here. For now, because VR Apps shou
ldn't show up in the |
| 293 // non-VR recents, and we don't want ChromeTabbedActivity disappearing,
exit VR. | 323 // non-VR recents, and we don't want ChromeTabbedActivity disappearing,
exit VR. |
| 294 exitVRIfNecessary(false); | 324 exitVRIfNecessary(false); |
| 295 } | 325 } |
| 296 | 326 |
| 297 /** | 327 /** |
| 298 * Exits the current VR mode (WebVR or VRShell) | 328 * Exits the current VR mode (WebVR or VRShell) |
| 299 * @return Whether or not we exited VR. | 329 * @return Whether or not we exited VR. |
| 300 */ | 330 */ |
| 301 public boolean exitVRIfNecessary(boolean returnTo2D) { | 331 public boolean exitVRIfNecessary(boolean returnTo2D) { |
| 332 if (!mVrAvailable) return false; |
| 302 if (!mInVr) return false; | 333 if (!mInVr) return false; |
| 303 shutdownVR(returnTo2D); | 334 shutdownVR(returnTo2D); |
| 304 return true; | 335 return true; |
| 305 } | 336 } |
| 306 | 337 |
| 307 public void onExitVRResult(int resultCode) { | 338 public void onExitVRResult(int resultCode) { |
| 339 assert mVrAvailable; |
| 308 if (resultCode == Activity.RESULT_OK) { | 340 if (resultCode == Activity.RESULT_OK) { |
| 309 mVrDaydreamApi.setVrModeEnabled(false); | 341 mVrDaydreamApi.setVrModeEnabled(false); |
| 310 } else { | 342 } else { |
| 311 // For now, we don't handle re-entering VR when exit fails, so keep
trying to exit. | 343 // For now, we don't handle re-entering VR when exit fails, so keep
trying to exit. |
| 312 mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent()); | 344 mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent()); |
| 313 } | 345 } |
| 314 } | 346 } |
| 315 | 347 |
| 316 private Intent createDaydreamIntent(int transitionType) { | 348 private PendingIntent getPendingEnterVRIntent() { |
| 317 if (mVrDaydreamApi == null) return null; | 349 return PendingIntent.getActivity(mActivity, 0, mEnterVRIntent, PendingIn
tent.FLAG_ONE_SHOT); |
| 318 // TODO(bshe): Ideally, this should go through ChromeLauncherActivity. T
o avoid polluting | |
| 319 // metrics, use the VR Activity alias for now. | |
| 320 Intent intent = mVrDaydreamApi.createVrIntent( | |
| 321 new ComponentName(mActivity, VR_ACTIVITY_ALIAS)); | |
| 322 intent.putExtra(DAYDREAM_DON_TYPE, transitionType); | |
| 323 return intent; | |
| 324 } | 350 } |
| 325 | 351 |
| 326 /** | 352 /** |
| 327 * Registers the Intent to fire after phone inserted into a headset. | 353 * Registers the Intent to fire after phone inserted into a headset. |
| 328 */ | 354 */ |
| 329 private void registerDaydreamIntent() { | 355 private void registerDaydreamIntent() { |
| 330 if (mVrDaydreamApi == null) return; | 356 mVrDaydreamApi.registerDaydreamIntent(getPendingEnterVRIntent()); |
| 331 Intent intent = createDaydreamIntent(DAYDREAM_DON_TYPE_AUTO); | |
| 332 mVrDaydreamApi.registerDaydreamIntent( | |
| 333 PendingIntent.getActivity(mActivity, 0, intent, PendingIntent.FL
AG_ONE_SHOT)); | |
| 334 } | 357 } |
| 335 | 358 |
| 336 /** | 359 /** |
| 337 * Unregisters the Intent which registered by this context if any. | 360 * Unregisters the Intent which registered by this context if any. |
| 338 */ | 361 */ |
| 339 private void unregisterDaydreamIntent() { | 362 private void unregisterDaydreamIntent() { |
| 340 if (mVrDaydreamApi != null) { | 363 mVrDaydreamApi.unregisterDaydreamIntent(); |
| 341 mVrDaydreamApi.unregisterDaydreamIntent(); | |
| 342 } | |
| 343 } | 364 } |
| 344 | 365 |
| 345 @CalledByNative | 366 @CalledByNative |
| 346 private long createNonPresentingNativeContext() { | 367 private long createNonPresentingNativeContext() { |
| 347 if (!mVrAvailable) return 0; | 368 if (!mVrAvailable) return 0; |
| 348 | 369 |
| 349 try { | 370 try { |
| 350 Constructor<?> nonPresentingGvrContextConstructor = | 371 Constructor<?> nonPresentingGvrContextConstructor = |
| 351 mNonPresentingGvrContextClass.getConstructor(Activity.class)
; | 372 mNonPresentingGvrContextClass.getConstructor(Activity.class)
; |
| 352 mNonPresentingGvrContext = | 373 mNonPresentingGvrContext = |
| (...skipping 11 matching lines...) Expand all Loading... |
| 364 private void shutdownNonPresentingNativeContext() { | 385 private void shutdownNonPresentingNativeContext() { |
| 365 mNonPresentingGvrContext.shutdown(); | 386 mNonPresentingGvrContext.shutdown(); |
| 366 mNonPresentingGvrContext = null; | 387 mNonPresentingGvrContext = null; |
| 367 } | 388 } |
| 368 | 389 |
| 369 /** | 390 /** |
| 370 * Exits VR Shell, performing all necessary cleanup. | 391 * Exits VR Shell, performing all necessary cleanup. |
| 371 */ | 392 */ |
| 372 private void shutdownVR(boolean returnTo2D) { | 393 private void shutdownVR(boolean returnTo2D) { |
| 373 if (!mInVr) return; | 394 if (!mInVr) return; |
| 395 mRequestedWebVR = false; |
| 374 if (returnTo2D) { | 396 if (returnTo2D) { |
| 375 mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent()); | 397 mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent()); |
| 376 } else { | 398 } else { |
| 377 mVrDaydreamApi.setVrModeEnabled(false); | 399 mVrDaydreamApi.setVrModeEnabled(false); |
| 378 mLastVRExit = SystemClock.uptimeMillis(); | 400 mLastVRExit = SystemClock.uptimeMillis(); |
| 379 } | 401 } |
| 380 mActivity.setRequestedOrientation(mRestoreOrientation); | 402 mActivity.setRequestedOrientation(mRestoreOrientation); |
| 381 mVrShell.pause(); | 403 mVrShell.pause(); |
| 382 removeVrViews(); | 404 removeVrViews(); |
| 383 clearVrModeWindowFlags(); | 405 clearVrModeWindowFlags(); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 397 return false; | 419 return false; |
| 398 } | 420 } |
| 399 | 421 |
| 400 try { | 422 try { |
| 401 Constructor<?> mVrCoreVersionCheckerConstructor = | 423 Constructor<?> mVrCoreVersionCheckerConstructor = |
| 402 mVrCoreVersionCheckerClass.getConstructor(); | 424 mVrCoreVersionCheckerClass.getConstructor(); |
| 403 mVrCoreVersionChecker = | 425 mVrCoreVersionChecker = |
| 404 (VrCoreVersionChecker) mVrCoreVersionCheckerConstructor.newI
nstance(); | 426 (VrCoreVersionChecker) mVrCoreVersionCheckerConstructor.newI
nstance(); |
| 405 } catch (InstantiationException | IllegalAccessException | IllegalArgume
ntException | 427 } catch (InstantiationException | IllegalAccessException | IllegalArgume
ntException |
| 406 | InvocationTargetException | NoSuchMethodException e) { | 428 | InvocationTargetException | NoSuchMethodException e) { |
| 407 Log.e(TAG, "Unable to instantiate VrCoreVersionChecker", e); | 429 Log.d(TAG, "Unable to instantiate VrCoreVersionChecker", e); |
| 408 return false; | 430 return false; |
| 409 } | 431 } |
| 410 return mVrCoreVersionChecker.isVrCoreCompatible(); | 432 return mVrCoreVersionChecker.isVrCoreCompatible(); |
| 411 } | 433 } |
| 412 | 434 |
| 413 private boolean createVrDaydreamApi() { | 435 private boolean createVrDaydreamApi() { |
| 414 if (!mVrAvailable) return false; | |
| 415 | |
| 416 try { | 436 try { |
| 417 Constructor<?> vrPrivateApiConstructor = | 437 Constructor<?> vrPrivateApiConstructor = |
| 418 mVrDaydreamApiClass.getConstructor(Activity.class); | 438 mVrDaydreamApiClass.getConstructor(Activity.class); |
| 419 mVrDaydreamApi = (VrDaydreamApi) vrPrivateApiConstructor.newInstance
(mActivity); | 439 mVrDaydreamApi = (VrDaydreamApi) vrPrivateApiConstructor.newInstance
(mActivity); |
| 420 } catch (InstantiationException | IllegalAccessException | IllegalArgume
ntException | 440 } catch (InstantiationException | IllegalAccessException | IllegalArgume
ntException |
| 421 | InvocationTargetException | NoSuchMethodException e) { | 441 | InvocationTargetException | NoSuchMethodException | SecurityEx
ception e) { |
| 422 Log.e(TAG, "Unable to instantiate VrDaydreamApi", e); | 442 Log.d(TAG, "Unable to instantiate VrDaydreamApi", e); |
| 423 return false; | 443 return false; |
| 424 } | 444 } |
| 425 return true; | 445 return true; |
| 426 } | 446 } |
| 427 | 447 |
| 428 private boolean createVrShell() { | 448 private boolean createVrShell() { |
| 429 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); | 449 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
| 430 StrictMode.allowThreadDiskWrites(); | 450 StrictMode.allowThreadDiskWrites(); |
| 431 try { | 451 try { |
| 432 Constructor<?> vrShellConstructor = mVrShellClass.getConstructor(Act
ivity.class); | 452 Constructor<?> vrShellConstructor = mVrShellClass.getConstructor(Act
ivity.class); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 514 if (mVrShellEnabled == null) { | 534 if (mVrShellEnabled == null) { |
| 515 if (!LibraryLoader.isInitialized()) { | 535 if (!LibraryLoader.isInitialized()) { |
| 516 return false; | 536 return false; |
| 517 } | 537 } |
| 518 mVrShellEnabled = ChromeFeatureList.isEnabled(ChromeFeatureList.VR_S
HELL); | 538 mVrShellEnabled = ChromeFeatureList.isEnabled(ChromeFeatureList.VR_S
HELL); |
| 519 } | 539 } |
| 520 return mVrShellEnabled; | 540 return mVrShellEnabled; |
| 521 } | 541 } |
| 522 | 542 |
| 523 /** | 543 /** |
| 524 * @return Whether or not VR Shell is currently enabled. | |
| 525 */ | |
| 526 public boolean isVrInitialized() { | |
| 527 return mVrDaydreamApi != null; | |
| 528 } | |
| 529 | |
| 530 /** | |
| 531 * @return Pointer to the native VrShellDelegate object. | 544 * @return Pointer to the native VrShellDelegate object. |
| 532 */ | 545 */ |
| 533 @CalledByNative | 546 @CalledByNative |
| 534 private long getNativePointer() { | 547 private long getNativePointer() { |
| 535 return mNativeVrShellDelegate; | 548 return mNativeVrShellDelegate; |
| 536 } | 549 } |
| 537 | 550 |
| 538 private native long nativeInit(); | 551 private native long nativeInit(); |
| 552 private native void nativeSetPresentResult(long nativeVrShellDelegate, boole
an result); |
| 539 } | 553 } |
| OLD | NEW |