1 // Copyright 2020 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.vr; 6 7 import android.app.Activity; 8 import android.content.Intent; 9 import android.net.Uri; 10 11 import androidx.annotation.VisibleForTesting; 12 13 import com.google.vr.ndk.base.DaydreamApi; 14 15 import org.chromium.base.ContextUtils; 16 import org.chromium.base.Log; 17 import org.chromium.base.annotations.CalledByNative; 18 import org.chromium.base.annotations.JNINamespace; 19 import org.chromium.base.annotations.NativeMethods; 20 import org.chromium.chrome.browser.infobar.InfoBarIdentifier; 21 import org.chromium.chrome.browser.ui.messages.infobar.SimpleConfirmInfoBarBuilder; 22 import org.chromium.content_public.browser.WebContents; 23 import org.chromium.ui.base.WindowAndroid; 24 25 /** 26 * Manages logic around VrCore Installation and Versioning 27 */ 28 @JNINamespace("vr") 29 public class VrCoreInstallUtils { 30 private static final String TAG = "VrCoreInstallUtils"; 31 // Pseudo-random number to avoid request id collisions. Result codes must fit in lower 16 bits 32 // when used with startActivityForResult... 33 private static final int VR_SERVICES_UPDATE_RESULT = 7213; 34 35 private static final String VR_CORE_MARKET_URI = 36 "market://details?id=" + VrCoreVersionChecker.VR_CORE_PACKAGE_ID; 37 38 // Instance that requested installation of VRCore. 39 // Should be non-null only if there is a pending request to install VRCore. 40 private static VrCoreInstallUtils sRequestInstallInstance; 41 42 private static VrCoreVersionChecker sVrCoreVersionChecker; 43 private static @VrSupportLevel Integer sVrSupportLevel; 44 45 private long mNativeVrCoreInstallUtils; 46 47 @CalledByNative 48 @VisibleForTesting create(long nativeVrCoreInstallUtils)49 protected static VrCoreInstallUtils create(long nativeVrCoreInstallUtils) { 50 return new VrCoreInstallUtils(nativeVrCoreInstallUtils); 51 } 52 53 /** 54 * See {@link Activity#onActivityResult}. 55 */ onActivityResultWithNative(int requestCode, int resultCode)56 public static boolean onActivityResultWithNative(int requestCode, int resultCode) { 57 // Handles the result of requesting to update VR services. 58 if (requestCode == VR_SERVICES_UPDATE_RESULT) { 59 if (sRequestInstallInstance != null) { 60 sRequestInstallInstance.onVrCoreMaybeUpdated(); 61 sRequestInstallInstance = null; 62 } 63 return true; 64 } 65 return false; 66 } 67 isDaydreamReadyDevice()68 public static boolean isDaydreamReadyDevice() { 69 return DaydreamApi.isDaydreamReadyPlatform(ContextUtils.getApplicationContext()); 70 } 71 getVrCoreVersionChecker()72 public static VrCoreVersionChecker getVrCoreVersionChecker() { 73 if (sVrCoreVersionChecker == null) sVrCoreVersionChecker = new VrCoreVersionChecker(); 74 return sVrCoreVersionChecker; 75 } 76 77 /** 78 * Returns the current {@VrSupportLevel}. 79 */ 80 @CalledByNative getVrSupportLevel()81 public static int getVrSupportLevel() { 82 if (sVrSupportLevel == null) { 83 if (!isVrCoreCompatible()) { 84 sVrSupportLevel = VrSupportLevel.VR_NEEDS_UPDATE; 85 } else if (isDaydreamReadyDevice()) { 86 sVrSupportLevel = VrSupportLevel.VR_DAYDREAM; 87 } else { 88 sVrSupportLevel = VrSupportLevel.VR_CARDBOARD; 89 } 90 } 91 return sVrSupportLevel; 92 } 93 94 /** 95 * Returns the @{VrSupportLevel}, if known, without attempting to recalculate. 96 */ getCachedVrSupportLevel()97 public static Integer getCachedVrSupportLevel() { 98 return sVrSupportLevel; 99 } 100 101 @CalledByNative vrSupportNeedsUpdate()102 public static boolean vrSupportNeedsUpdate() { 103 return getVrSupportLevel() == VrSupportLevel.VR_NEEDS_UPDATE; 104 } 105 106 /** 107 * Returns whether the device has support for Daydream. 108 */ hasDaydreamSupport()109 /* package */ static boolean hasDaydreamSupport() { 110 return getVrSupportLevel() == VrSupportLevel.VR_DAYDREAM; 111 } 112 113 /** 114 * @param versionChecker The VrCoreVersionChecker object this delegate will use 115 */ 116 @VisibleForTesting overrideVrCoreVersionChecker(VrCoreVersionChecker versionChecker)117 protected static void overrideVrCoreVersionChecker(VrCoreVersionChecker versionChecker) { 118 sVrCoreVersionChecker = versionChecker; 119 updateVrSupportLevel(); 120 } 121 isVrCoreCompatible()122 private static boolean isVrCoreCompatible() { 123 VrCoreVersionChecker checker = getVrCoreVersionChecker(); 124 if (checker == null) return false; 125 return checker.getVrCoreCompatibility() 126 == VrCoreVersionChecker.VrCoreCompatibility.VR_READY; 127 } 128 129 /** 130 * Forces a recalculation of the current @{VrSupportLevel} as it may have changed. 131 */ updateVrSupportLevel()132 private static int updateVrSupportLevel() { 133 sVrSupportLevel = null; 134 return getVrSupportLevel(); 135 } 136 VrCoreInstallUtils(long nativeVrCoreInstallUtils)137 private VrCoreInstallUtils(long nativeVrCoreInstallUtils) { 138 mNativeVrCoreInstallUtils = nativeVrCoreInstallUtils; 139 } 140 141 @CalledByNative onNativeDestroy()142 private void onNativeDestroy() { 143 mNativeVrCoreInstallUtils = 0; 144 } 145 getActivity(final WebContents webContents)146 private Activity getActivity(final WebContents webContents) { 147 if (webContents == null) return null; 148 WindowAndroid window = webContents.getTopLevelNativeWindow(); 149 if (window == null) return null; 150 return window.getActivity().get(); 151 } 152 153 /** 154 * Prompts the user to install or update VRSupport if needed. 155 */ 156 @CalledByNative 157 @VisibleForTesting requestInstallVrCore(final WebContents webContents)158 protected void requestInstallVrCore(final WebContents webContents) { 159 if (webContents == null) { 160 maybeNotifyNativeOnInstallResult(false); 161 return; 162 } 163 164 final Activity activity = getActivity(webContents); 165 if (activity == null) { 166 maybeNotifyNativeOnInstallResult(false); 167 return; 168 } 169 170 // Force a recalculation of the VrSupportLevel 171 updateVrSupportLevel(); 172 if (!vrSupportNeedsUpdate()) { 173 // If we don't need an update, just return that install succeeded. 174 maybeNotifyNativeOnInstallResult(true); 175 return; 176 } 177 178 @VrCoreVersionChecker.VrCoreCompatibility 179 int vrCoreCompatibility = getVrCoreVersionChecker().getVrCoreCompatibility(); 180 181 String infobarText; 182 String buttonText; 183 184 if (vrCoreCompatibility == VrCoreVersionChecker.VrCoreCompatibility.VR_NOT_AVAILABLE) { 185 // Supported, but not installed. Ask user to install instead of upgrade. 186 infobarText = ContextUtils.getApplicationContext().getString( 187 org.chromium.chrome.vr.R.string.vr_services_check_infobar_install_text); 188 buttonText = ContextUtils.getApplicationContext().getString( 189 org.chromium.chrome.vr.R.string.vr_services_check_infobar_install_button); 190 } else if (vrCoreCompatibility == VrCoreVersionChecker.VrCoreCompatibility.VR_OUT_OF_DATE) { 191 infobarText = ContextUtils.getApplicationContext().getString( 192 org.chromium.chrome.vr.R.string.vr_services_check_infobar_update_text); 193 buttonText = ContextUtils.getApplicationContext().getString( 194 org.chromium.chrome.vr.R.string.vr_services_check_infobar_update_button); 195 } else { 196 Log.e(TAG, "Unknown VrCore compatibility: " + vrCoreCompatibility); 197 return; 198 } 199 200 SimpleConfirmInfoBarBuilder.Listener listener = new SimpleConfirmInfoBarBuilder.Listener() { 201 @Override 202 public void onInfoBarDismissed() { 203 maybeNotifyNativeOnInstallResult(false); 204 } 205 206 @Override 207 public boolean onInfoBarButtonClicked(boolean isPrimary) { 208 assert sRequestInstallInstance == null; 209 sRequestInstallInstance = VrCoreInstallUtils.this; 210 activity.startActivityForResult( 211 new Intent(Intent.ACTION_VIEW, Uri.parse(VR_CORE_MARKET_URI)), 212 VR_SERVICES_UPDATE_RESULT); 213 return false; 214 } 215 216 @Override 217 public boolean onInfoBarLinkClicked() { 218 return false; 219 } 220 }; 221 SimpleConfirmInfoBarBuilder.create(webContents, listener, 222 InfoBarIdentifier.VR_SERVICES_UPGRADE_ANDROID, activity, 223 org.chromium.chrome.vr.R.drawable.vr_services, infobarText, buttonText, null, null, 224 true); 225 } 226 onVrCoreMaybeUpdated()227 private void onVrCoreMaybeUpdated() { 228 maybeNotifyNativeOnInstallResult(updateVrSupportLevel() != VrSupportLevel.VR_NEEDS_UPDATE); 229 } 230 231 /** 232 * Helper used to notify native code about the result of the request to install VRCore. 233 */ maybeNotifyNativeOnInstallResult(boolean success)234 private void maybeNotifyNativeOnInstallResult(boolean success) { 235 if (mNativeVrCoreInstallUtils != 0) { 236 VrCoreInstallUtilsJni.get().onInstallResult(mNativeVrCoreInstallUtils, success); 237 } 238 } 239 240 @NativeMethods 241 interface Natives { onInstallResult(long nativeVrCoreInstallHelper, boolean success)242 void onInstallResult(long nativeVrCoreInstallHelper, boolean success); 243 } 244 } 245