diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 81da1aa0d3c34e8c36815411cf3ca807ac558236..cc31044b9ec0079e6bda336ff631c4d3f023994d 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -1,6 +1,10 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { VR_DEVICE_AVAILABILITY } from "../utils/vr-caps-detect.js"; +import queryString from "query-string"; + +const { detect } = require("detect-browser"); +const browser = detect(); const ENTRY_STEPS = { start: "start", @@ -96,7 +100,8 @@ class UIRoot extends Component { availableVREntryTypes: PropTypes.object, store: PropTypes.object, concurrentLoadDetector: PropTypes.object, - disableAutoExitOnConcurrentLoad: PropTypes.bool + disableAutoExitOnConcurrentLoad: PropTypes.bool, + forcedVREntryType: PropTypes.string }; state = { @@ -125,6 +130,17 @@ class UIRoot extends Component { componentDidMount() { this.setupTestTone(); this.props.concurrentLoadDetector.addEventListener("concurrentload", this.onConcurrentLoad); + this.handleForcedVREntryType(); + } + + handleForcedVREntryType = () => { + if (!this.props.forcedVREntryType) return; + + if (this.props.forcedVREntryType === "daydream") { + this.enterDaydream(); + } else if (this.props.forcedVREntryType === "gearvr") { + this.enterGearVR(); + } } setupTestTone = () => { @@ -222,11 +238,32 @@ class UIRoot extends Component { } enterGearVR = async () => { - document.location = `ovrweb://${document.location.toString()}`; + this.exit(); + + // Launch via Oculus Browser + const qs = queryString.parse(document.location.search); + qs.vr_entry_type = "gearvr"; // Auto-choose 'gearvr' after landing in Oculus Browser + + const ovrwebUrl = `ovrweb://${document.location.protocol || "http:"}//${document.location.host}${document.location.pathname || ""}?${queryString.stringify(qs)}#{document.location.hash || ""}`; + + document.location = ovrwebUrl; } enterDaydream = async () => { - console.log("daydream"); + const loc = document.location; + + if (this.props.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe) { + this.exit(); + + // We are not in mobile chrome, so launch into chrome via an Intent URL + const qs = queryString.parse(document.location.search); + qs.vr_entry_type = "daydream"; // Auto-choose 'daydream' after landing in chrome + + const intentUrl = `intent://${document.location.host}${document.location.pathname || ""}?${queryString.stringify(qs)}#Intent;scheme=${(document.location.protocol || "http:").replace(":", "")};action=android.intent.action.VIEW;package=com.android.chrome;end;`; + document.location = intentUrl; + } else { + await this.performDirectEntryFlow(true); + } } mediaVideoConstraint = () => { diff --git a/src/room.js b/src/room.js index c302143ff51078945e432d09cb6a2a648c2f27ab..97e5311aaf4adbb50df42f2d2c3412f44ebc7afd 100644 --- a/src/room.js +++ b/src/room.js @@ -196,13 +196,19 @@ function mountUI() { getAvailableVREntryTypes().then(availableVREntryTypes => { const qs = queryString.parse(location.search); const disableAutoExitOnConcurrentLoad = qs.allow_multi === "true" + let forcedVREntryType = null; + + if (qs.vr_entry_type) { + forcedVREntryType = qs.vr_entry_type; + } ReactDOM.render(<UIRoot {...{ availableVREntryTypes, enterScene, exitScene, concurrentLoadDetector, - disableAutoExitOnConcurrentLoad + disableAutoExitOnConcurrentLoad, + forcedVREntryType }} />, document.getElementById("ui-root")); document.getElementById("loader").style.display = "none"; diff --git a/src/utils/vr-caps-detect.js b/src/utils/vr-caps-detect.js index bde6cd71498aea96e6a6b22748f8d9fcb865c068..a89f589f6e44e270f5294e55d238c4c2ced1dc96 100644 --- a/src/utils/vr-caps-detect.js +++ b/src/utils/vr-caps-detect.js @@ -9,32 +9,12 @@ export const VR_DEVICE_AVAILABILITY = { maybe: "maybe" // Implies this device may support this VR platform, but may not be installed or in a compatible browser }; -function hasPhysicalScreenDimensions(w, h) { - const dpr = window.devicePixelRatio; - const width = screen.width * dpr; - const height = screen.height * dpr; - - // Compensate for rounding error due to fractional pixel densities - return Math.abs(w - width) < 3 && Math.abs(h - height) < 3; -} - -function matchesScreenSizesAndUserAgentRegexes(sizes, regexes) { - return !!(sizes.find(s => hasPhysicalScreenDimensions(...s)) && regexes.find(r => navigator.userAgent.match())); -} - function isMaybeGearVRCompatibleDevice() { - // Modern Samsung Galaxy devices have model numbers in the user agent of the form SX-XXXX or GT-XXXX for note 8 - return matchesScreenSizesAndUserAgentRegexes([[1440, 2560], [1440, 2960]], [/\WS.-.....?\W/, /\WGT-.....?\W/]); + return navigator.userAgent.match(/\WAndroid\W/); } function isMaybeDaydreamCompatibleDevice() { - // If it might be a GearVR device, then we assume it might also be a Daydream device since S8 and higher - // support Daydream. - if (isMaybeGearVRCompatibleDevice()) return true; - - // List of resolutions of Pixel line of phones as well as other announced Daydream compatible - // phones. This list may need to be updated as new phones roll out. - return matchesScreenSizesAndUserAgentRegexes([[1080, 1920], [1440, 2560], [1440, 2880]], [/\WAndroid\W/]); + return navigator.userAgent.match(/\WAndroid\W/); } // Blacklist of VR device name regex matchers that we do not want to consider as valid VR devices @@ -60,7 +40,8 @@ const GENERIC_ENTRY_TYPE_DEVICE_BLACKLIST = [/cardboard/i]; // export async function getAvailableVREntryTypes() { const isWebVRCapableBrowser = !!navigator.getVRDisplays; - const isDaydreamCapableBrowser = !!(isWebVRCapableBrowser && browser.name === "chrome"); + const isSamsungBrowser = browser.name === "chrome" && navigator.userAgent.match(/SamsungBrowser/); + const isDaydreamCapableBrowser = !!(isWebVRCapableBrowser && browser.name === "chrome" && !isSamsungBrowser); let generic = VR_DEVICE_AVAILABILITY.no;