├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle ├── LICENSE ├── README └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── libs │ └── libprotobuf-java-2.3-nano.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── google │ └── vrtoolkit │ └── cardboard │ ├── CardboardActivity.java │ ├── CardboardDeviceParams.java │ ├── CardboardView.java │ ├── ConfigUtils.java │ ├── Constants.java │ ├── Distortion.java │ ├── DistortionRenderer.java │ ├── Eye.java │ ├── FieldOfView.java │ ├── GLStateBackup.java │ ├── HeadMountedDisplay.java │ ├── HeadMountedDisplayManager.java │ ├── HeadTransform.java │ ├── PhoneParams.java │ ├── ScreenParams.java │ ├── UiLayer.java │ ├── UiUtils.java │ ├── Viewport.java │ ├── proto │ ├── CardboardDevice.java │ └── Phone.java │ └── sensors │ ├── Clock.java │ ├── DeviceSensorLooper.java │ ├── HeadTracker.java │ ├── MagnetSensor.java │ ├── NfcSensor.java │ ├── SensorEventProvider.java │ ├── SystemClock.java │ └── internal │ ├── GyroBiasEstimator.java │ ├── Matrix3x3d.java │ ├── OrientationEKF.java │ ├── So3Util.java │ └── Vector3d.java ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── google │ │ └── vrtoolkit │ │ └── cardboard │ │ └── samples │ │ └── treasurehunt │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── google │ │ └── vrtoolkit │ │ └── cardboard │ │ └── samples │ │ └── treasurehunt │ │ ├── CardboardOverlayView.java │ │ ├── MainActivity.java │ │ └── WorldLayoutData.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout │ └── common_ui.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── raw │ ├── grid_fragment.shader │ ├── light_vertex.shader │ └── passthrough_fragment.shader │ └── values │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | 22 | # Android Studio 23 | .idea/ 24 | .gradle 25 | /*/local.properties 26 | /*/out 27 | build/* 28 | /*/*/build 29 | /*/*/production 30 | *.iml 31 | *.iws 32 | *.ipr 33 | *~ 34 | *.swp 35 | 36 | .DS_Store 37 | 38 | .history -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Google Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an "AS IS" BASIS, 8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | See the License for the specific language governing permissions and 10 | limitations under the License. 11 | 12 | 13 | Apache License 14 | Version 2.0, January 2004 15 | http://www.apache.org/licenses/ 16 | 17 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 18 | 19 | 1. Definitions. 20 | 21 | "License" shall mean the terms and conditions for use, reproduction, 22 | and distribution as defined by Sections 1 through 9 of this document. 23 | 24 | "Licensor" shall mean the copyright owner or entity authorized by 25 | the copyright owner that is granting the License. 26 | 27 | "Legal Entity" shall mean the union of the acting entity and all 28 | other entities that control, are controlled by, or are under common 29 | control with that entity. For the purposes of this definition, 30 | "control" means (i) the power, direct or indirect, to cause the 31 | direction or management of such entity, whether by contract or 32 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 33 | outstanding shares, or (iii) beneficial ownership of such entity. 34 | 35 | "You" (or "Your") shall mean an individual or Legal Entity 36 | exercising permissions granted by this License. 37 | 38 | "Source" form shall mean the preferred form for making modifications, 39 | including but not limited to software source code, documentation 40 | source, and configuration files. 41 | 42 | "Object" form shall mean any form resulting from mechanical 43 | transformation or translation of a Source form, including but 44 | not limited to compiled object code, generated documentation, 45 | and conversions to other media types. 46 | 47 | "Work" shall mean the work of authorship, whether in Source or 48 | Object form, made available under the License, as indicated by a 49 | copyright notice that is included in or attached to the work 50 | (an example is provided in the Appendix below). 51 | 52 | "Derivative Works" shall mean any work, whether in Source or Object 53 | form, that is based on (or derived from) the Work and for which the 54 | editorial revisions, annotations, elaborations, or other modifications 55 | represent, as a whole, an original work of authorship. For the purposes 56 | of this License, Derivative Works shall not include works that remain 57 | separable from, or merely link (or bind by name) to the interfaces of, 58 | the Work and Derivative Works thereof. 59 | 60 | "Contribution" shall mean any work of authorship, including 61 | the original version of the Work and any modifications or additions 62 | to that Work or Derivative Works thereof, that is intentionally 63 | submitted to Licensor for inclusion in the Work by the copyright owner 64 | or by an individual or Legal Entity authorized to submit on behalf of 65 | the copyright owner. For the purposes of this definition, "submitted" 66 | means any form of electronic, verbal, or written communication sent 67 | to the Licensor or its representatives, including but not limited to 68 | communication on electronic mailing lists, source code control systems, 69 | and issue tracking systems that are managed by, or on behalf of, the 70 | Licensor for the purpose of discussing and improving the Work, but 71 | excluding communication that is conspicuously marked or otherwise 72 | designated in writing by the copyright owner as "Not a Contribution." 73 | 74 | "Contributor" shall mean Licensor and any individual or Legal Entity 75 | on behalf of whom a Contribution has been received by Licensor and 76 | subsequently incorporated within the Work. 77 | 78 | 2. Grant of Copyright License. Subject to the terms and conditions of 79 | this License, each Contributor hereby grants to You a perpetual, 80 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 81 | copyright license to reproduce, prepare Derivative Works of, 82 | publicly display, publicly perform, sublicense, and distribute the 83 | Work and such Derivative Works in Source or Object form. 84 | 85 | 3. Grant of Patent License. Subject to the terms and conditions of 86 | this License, each Contributor hereby grants to You a perpetual, 87 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 88 | (except as stated in this section) patent license to make, have made, 89 | use, offer to sell, sell, import, and otherwise transfer the Work, 90 | where such license applies only to those patent claims licensable 91 | by such Contributor that are necessarily infringed by their 92 | Contribution(s) alone or by combination of their Contribution(s) 93 | with the Work to which such Contribution(s) was submitted. If You 94 | institute patent litigation against any entity (including a 95 | cross-claim or counterclaim in a lawsuit) alleging that the Work 96 | or a Contribution incorporated within the Work constitutes direct 97 | or contributory patent infringement, then any patent licenses 98 | granted to You under this License for that Work shall terminate 99 | as of the date such litigation is filed. 100 | 101 | 4. Redistribution. You may reproduce and distribute copies of the 102 | Work or Derivative Works thereof in any medium, with or without 103 | modifications, and in Source or Object form, provided that You 104 | meet the following conditions: 105 | 106 | (a) You must give any other recipients of the Work or 107 | Derivative Works a copy of this License; and 108 | 109 | (b) You must cause any modified files to carry prominent notices 110 | stating that You changed the files; and 111 | 112 | (c) You must retain, in the Source form of any Derivative Works 113 | that You distribute, all copyright, patent, trademark, and 114 | attribution notices from the Source form of the Work, 115 | excluding those notices that do not pertain to any part of 116 | the Derivative Works; and 117 | 118 | (d) If the Work includes a "NOTICE" text file as part of its 119 | distribution, then any Derivative Works that You distribute must 120 | include a readable copy of the attribution notices contained 121 | within such NOTICE file, excluding those notices that do not 122 | pertain to any part of the Derivative Works, in at least one 123 | of the following places: within a NOTICE text file distributed 124 | as part of the Derivative Works; within the Source form or 125 | documentation, if provided along with the Derivative Works; or, 126 | within a display generated by the Derivative Works, if and 127 | wherever such third-party notices normally appear. The contents 128 | of the NOTICE file are for informational purposes only and 129 | do not modify the License. You may add Your own attribution 130 | notices within Derivative Works that You distribute, alongside 131 | or as an addendum to the NOTICE text from the Work, provided 132 | that such additional attribution notices cannot be construed 133 | as modifying the License. 134 | 135 | You may add Your own copyright statement to Your modifications and 136 | may provide additional or different license terms and conditions 137 | for use, reproduction, or distribution of Your modifications, or 138 | for any such Derivative Works as a whole, provided Your use, 139 | reproduction, and distribution of the Work otherwise complies with 140 | the conditions stated in this License. 141 | 142 | 5. Submission of Contributions. Unless You explicitly state otherwise, 143 | any Contribution intentionally submitted for inclusion in the Work 144 | by You to the Licensor shall be under the terms and conditions of 145 | this License, without any additional terms or conditions. 146 | Notwithstanding the above, nothing herein shall supersede or modify 147 | the terms of any separate license agreement you may have executed 148 | with Licensor regarding such Contributions. 149 | 150 | 6. Trademarks. This License does not grant permission to use the trade 151 | names, trademarks, service marks, or product names of the Licensor, 152 | except as required for reasonable and customary use in describing the 153 | origin of the Work and reproducing the content of the NOTICE file. 154 | 155 | 7. Disclaimer of Warranty. Unless required by applicable law or 156 | agreed to in writing, Licensor provides the Work (and each 157 | Contributor provides its Contributions) on an "AS IS" BASIS, 158 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 159 | implied, including, without limitation, any warranties or conditions 160 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 161 | PARTICULAR PURPOSE. You are solely responsible for determining the 162 | appropriateness of using or redistributing the Work and assume any 163 | risks associated with Your exercise of permissions under this License. 164 | 165 | 8. Limitation of Liability. In no event and under no legal theory, 166 | whether in tort (including negligence), contract, or otherwise, 167 | unless required by applicable law (such as deliberate and grossly 168 | negligent acts) or agreed to in writing, shall any Contributor be 169 | liable to You for damages, including any direct, indirect, special, 170 | incidental, or consequential damages of any character arising as a 171 | result of this License or out of the use or inability to use the 172 | Work (including but not limited to damages for loss of goodwill, 173 | work stoppage, computer failure or malfunction, or any and all 174 | other commercial damages or losses), even if such Contributor 175 | has been advised of the possibility of such damages. 176 | 177 | 9. Accepting Warranty or Additional Liability. While redistributing 178 | the Work or Derivative Works thereof, You may choose to offer, 179 | and charge a fee for, acceptance of support, warranty, indemnity, 180 | or other liability obligations and/or rights consistent with this 181 | License. However, in accepting such obligations, You may act only 182 | on Your own behalf and on Your sole responsibility, not on behalf 183 | of any other Contributor, and only if You agree to indemnify, 184 | defend, and hold each Contributor harmless for any liability 185 | incurred by, or claims asserted against, such Contributor by reason 186 | of your accepting any such warranty or additional liability. 187 | 188 | END OF TERMS AND CONDITIONS 189 | 190 | 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cardboard Java 2 | ===================== 3 | Copyright (c) 2014 Google Inc. All rights reserved. 4 | 5 | Decompiled CardboardSDK 0.5.1. 6 | 7 | The *TreasureHunt* sample project, running fully from source. It compiles and runs on Android Studio 1.0.2. The Cardboard.jar package has been replaced by the decompiled sources. Decompiled using [Luyten](https://github.com/deathmarine/Luyten). 8 | 9 | Inspiration from [raasun's cardboard](https://github.com/raasun/cardboard) (decompilation of an older version of CardboardSDK). According to *raasun*, Google should eventually release the official CardboardSDK source. 10 | 11 | [https://developers.google.com/cardboard/android/get-started](https://developers.google.com/cardboard/android/get-started) 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.0.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | mavenCentral() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2007-2011 the original author or authors 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /gradle/README: -------------------------------------------------------------------------------- 1 | Retrieved from www.gradle.org/license on June 18, 2014 at 3pm. 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion '21.1.2' 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 21 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | 14 | compileOptions { 15 | sourceCompatibility JavaVersion.VERSION_1_7 16 | targetCompatibility JavaVersion.VERSION_1_7 17 | } 18 | 19 | lintOptions { 20 | abortOnError false 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | compile fileTree(dir: 'libs', include: ['*.jar']) 33 | } 34 | -------------------------------------------------------------------------------- /library/libs/libprotobuf-java-2.3-nano.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/library/libs/libprotobuf-java-2.3-nano.jar -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add any project specific keep options here: 2 | 3 | # If your project uses WebView with JS, uncomment the following 4 | # and specify the fully qualified class name to the JavaScript interface 5 | # class: 6 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 7 | # public *; 8 | #} 9 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/CardboardActivity.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import com.google.vrtoolkit.cardboard.sensors.MagnetSensor; 4 | import com.google.vrtoolkit.cardboard.sensors.NfcSensor; 5 | 6 | import android.app.Activity; 7 | import android.nfc.NdefMessage; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.os.Handler; 11 | import android.view.KeyEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.Window; 15 | import android.view.WindowManager; 16 | 17 | public class CardboardActivity extends Activity { 18 | 19 | private static final long NAVIGATION_BAR_TIMEOUT_MS = 2000L; 20 | 21 | private CardboardView mCardboardView; 22 | private MagnetSensor mMagnetSensor; 23 | private NfcSensor mNfcSensor; 24 | private SensorListener sensorListener; 25 | private int mVolumeKeysMode; 26 | 27 | public CardboardActivity() { 28 | super(); 29 | sensorListener = new SensorListener(); 30 | } 31 | 32 | public void setCardboardView(final CardboardView cardboardView) { 33 | mCardboardView = cardboardView; 34 | if (cardboardView == null) { 35 | return; 36 | } 37 | final NdefMessage tagContents = mNfcSensor.getTagContents(); 38 | if (tagContents != null) { 39 | updateCardboardDeviceParams(CardboardDeviceParams.createFromNfcContents(tagContents)); 40 | } 41 | } 42 | 43 | public CardboardView getCardboardView() { 44 | return mCardboardView; 45 | } 46 | 47 | public NfcSensor getNfcSensor() { 48 | return mNfcSensor; 49 | } 50 | 51 | public void setVolumeKeysMode(final int mode) { 52 | mVolumeKeysMode = mode; 53 | } 54 | 55 | public int getVolumeKeysMode() { 56 | return mVolumeKeysMode; 57 | } 58 | 59 | public boolean areVolumeKeysDisabled() { 60 | switch (mVolumeKeysMode) { 61 | case VolumeKeys.NOT_DISABLED: { 62 | return false; 63 | } 64 | case VolumeKeys.DISABLED_WHILE_IN_CARDBOARD: { 65 | return this.mNfcSensor.isDeviceInCardboard(); 66 | } 67 | case VolumeKeys.DISABLED: { 68 | return true; 69 | } 70 | default: { 71 | throw new IllegalStateException(new StringBuilder() 72 | .append("Invalid volume keys mode ") 73 | .append(mVolumeKeysMode).toString() 74 | ); 75 | } 76 | } 77 | } 78 | 79 | public void onInsertedIntoCardboard(final CardboardDeviceParams cardboardDeviceParams) { 80 | this.updateCardboardDeviceParams(cardboardDeviceParams); 81 | } 82 | 83 | public void onRemovedFromCardboard() { 84 | } 85 | 86 | public void onCardboardTrigger() { 87 | } 88 | 89 | protected void updateCardboardDeviceParams(final CardboardDeviceParams newParams) { 90 | if (mCardboardView != null) { 91 | mCardboardView.updateCardboardDeviceParams(newParams); 92 | } 93 | } 94 | 95 | @Override 96 | protected void onCreate(final Bundle savedInstanceState) { 97 | super.onCreate(savedInstanceState); 98 | 99 | requestWindowFeature(Window.FEATURE_NO_TITLE); 100 | getWindow().addFlags(WindowManager.LayoutParams.ALPHA_CHANGED); 101 | 102 | mMagnetSensor = new MagnetSensor(this); 103 | mMagnetSensor.setOnCardboardTriggerListener(sensorListener); 104 | mNfcSensor = NfcSensor.getInstance(this); 105 | mNfcSensor.addOnCardboardNfcListener(sensorListener); 106 | 107 | mNfcSensor.onNfcIntent(this.getIntent()); 108 | setVolumeKeysMode(VolumeKeys.DISABLED_WHILE_IN_CARDBOARD); 109 | 110 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 111 | final Handler handler = new Handler(); 112 | getWindow().getDecorView().setOnSystemUiVisibilityChangeListener( 113 | new View.OnSystemUiVisibilityChangeListener() { 114 | @Override 115 | public void onSystemUiVisibilityChange(final int visibility) { 116 | if ((visibility & 0x2) == 0x0) { 117 | handler.postDelayed(new Runnable() { 118 | @Override 119 | public void run() { 120 | CardboardActivity.this.setFullscreenMode(); 121 | } 122 | }, NAVIGATION_BAR_TIMEOUT_MS); 123 | } 124 | } 125 | }); 126 | } 127 | } 128 | 129 | @Override 130 | protected void onResume() { 131 | super.onResume(); 132 | if (mCardboardView != null) { 133 | mCardboardView.onResume(); 134 | } 135 | mMagnetSensor.start(); 136 | mNfcSensor.onResume(this); 137 | } 138 | 139 | @Override 140 | protected void onPause() { 141 | super.onPause(); 142 | if (mCardboardView != null) { 143 | mCardboardView.onPause(); 144 | } 145 | mMagnetSensor.stop(); 146 | mNfcSensor.onPause(this); 147 | } 148 | 149 | @Override 150 | protected void onDestroy() { 151 | mNfcSensor.removeOnCardboardNfcListener(sensorListener); 152 | super.onDestroy(); 153 | } 154 | 155 | @Override 156 | public void setContentView(final View view) { 157 | if (view instanceof CardboardView) { 158 | setCardboardView((CardboardView) view); 159 | } 160 | super.setContentView(view); 161 | } 162 | 163 | @Override 164 | public void setContentView(final View view, final ViewGroup.LayoutParams params) { 165 | if (view instanceof CardboardView) { 166 | setCardboardView((CardboardView) view); 167 | } 168 | super.setContentView(view, params); 169 | } 170 | 171 | @Override 172 | public boolean onKeyDown(final int keyCode, final KeyEvent event) { 173 | return ((keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) 174 | && areVolumeKeysDisabled()) || super.onKeyDown(keyCode, event); 175 | } 176 | 177 | @Override 178 | public boolean onKeyUp(final int keyCode, final KeyEvent event) { 179 | return ((keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) 180 | && areVolumeKeysDisabled()) || super.onKeyUp(keyCode, event); 181 | } 182 | 183 | @Override 184 | public void onWindowFocusChanged(final boolean hasFocus) { 185 | super.onWindowFocusChanged(hasFocus); 186 | if (hasFocus) { 187 | setFullscreenMode(); 188 | } 189 | } 190 | 191 | private void setFullscreenMode() { 192 | getWindow().getDecorView().setSystemUiVisibility(5894); 193 | } 194 | 195 | public abstract static class VolumeKeys { 196 | public static final int NOT_DISABLED = 0; 197 | public static final int DISABLED = 1; 198 | public static final int DISABLED_WHILE_IN_CARDBOARD = 2; 199 | } 200 | 201 | private class SensorListener 202 | implements MagnetSensor.OnCardboardTriggerListener, NfcSensor.OnCardboardNfcListener{ 203 | @Override 204 | public void onInsertedIntoCardboard(final CardboardDeviceParams deviceParams) { 205 | CardboardActivity.this.onInsertedIntoCardboard(deviceParams); 206 | } 207 | 208 | @Override 209 | public void onRemovedFromCardboard() { 210 | CardboardActivity.this.onRemovedFromCardboard(); 211 | } 212 | 213 | @Override 214 | public void onCardboardTrigger() { 215 | CardboardActivity.this.onCardboardTrigger(); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/CardboardDeviceParams.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import android.net.*; 4 | import com.google.vrtoolkit.cardboard.proto.*; 5 | import android.util.*; 6 | import java.nio.*; 7 | import com.google.protobuf.nano.*; 8 | import java.io.*; 9 | import android.nfc.*; 10 | 11 | public class CardboardDeviceParams{ 12 | private static final String TAG = "CardboardDeviceParams"; 13 | private static final String HTTP_SCHEME = "http"; 14 | private static final String URI_HOST_GOOGLE_SHORT = "g.co"; 15 | private static final String URI_HOST_GOOGLE = "google.com"; 16 | private static final String URI_PATH_CARDBOARD_HOME = "cardboard"; 17 | private static final String URI_PATH_CARDBOARD_CONFIG = "cardboard/cfg"; 18 | private static final String URI_SCHEME_LEGACY_CARDBOARD = "cardboard"; 19 | private static final String URI_HOST_LEGACY_CARDBOARD = "v1.0.0"; 20 | private static final Uri URI_ORIGINAL_CARDBOARD_NFC; 21 | private static final Uri URI_ORIGINAL_CARDBOARD_QR_CODE; 22 | private static final String URI_KEY_PARAMS = "p"; 23 | private static final int STREAM_SENTINEL = 894990891; 24 | private static final String DEFAULT_VENDOR = "Google, Inc."; 25 | private static final String DEFAULT_MODEL = "Cardboard v1"; 26 | private static final float DEFAULT_INTER_LENS_DISTANCE = 0.06f; 27 | private static final float DEFAULT_VERTICAL_DISTANCE_TO_LENS_CENTER = 0.035f; 28 | private static final float DEFAULT_SCREEN_TO_LENS_DISTANCE = 0.042f; 29 | private String mVendor; 30 | private String mModel; 31 | private float mInterLensDistance; 32 | private float mVerticalDistanceToLensCenter; 33 | private float mScreenToLensDistance; 34 | private FieldOfView mLeftEyeMaxFov; 35 | private boolean mHasMagnet; 36 | private Distortion mDistortion; 37 | 38 | public CardboardDeviceParams() { 39 | super(); 40 | this.setDefaultValues(); 41 | } 42 | 43 | public CardboardDeviceParams(final CardboardDeviceParams params) { 44 | super(); 45 | this.copyFrom(params); 46 | } 47 | 48 | public CardboardDeviceParams(final CardboardDevice.DeviceParams params) { 49 | super(); 50 | this.setDefaultValues(); 51 | if (params == null) { 52 | return; 53 | } 54 | this.mVendor = params.getVendor(); 55 | this.mModel = params.getModel(); 56 | this.mInterLensDistance = params.getInterLensDistance(); 57 | this.mVerticalDistanceToLensCenter = params.getTrayBottomToLensHeight(); 58 | this.mScreenToLensDistance = params.getScreenToLensDistance(); 59 | this.mLeftEyeMaxFov = FieldOfView.parseFromProtobuf(params.leftEyeFieldOfViewAngles); 60 | if (this.mLeftEyeMaxFov == null) { 61 | this.mLeftEyeMaxFov = new FieldOfView(); 62 | } 63 | this.mDistortion = Distortion.parseFromProtobuf(params.distortionCoefficients); 64 | if (this.mDistortion == null) { 65 | this.mDistortion = new Distortion(); 66 | } 67 | this.mHasMagnet = params.getHasMagnet(); 68 | } 69 | 70 | public static boolean isOriginalCardboardDeviceUri(final Uri uri) { 71 | return CardboardDeviceParams.URI_ORIGINAL_CARDBOARD_QR_CODE.equals((Object)uri) || (CardboardDeviceParams.URI_ORIGINAL_CARDBOARD_NFC.getScheme().equals(uri.getScheme()) && CardboardDeviceParams.URI_ORIGINAL_CARDBOARD_NFC.getAuthority().equals(uri.getAuthority())); 72 | } 73 | 74 | private static boolean isCardboardDeviceUri(final Uri uri) { 75 | return HTTP_SCHEME.equals(uri.getScheme()) && URI_HOST_GOOGLE.equals(uri.getAuthority()) && URI_PATH_CARDBOARD_CONFIG.equals(uri.getPath()); 76 | } 77 | 78 | public static boolean isCardboardUri(final Uri uri) { 79 | return isOriginalCardboardDeviceUri(uri) || isCardboardDeviceUri(uri); 80 | } 81 | 82 | public static CardboardDeviceParams createFromUri(final Uri uri) { 83 | if (uri == null) { 84 | return null; 85 | } 86 | if (isOriginalCardboardDeviceUri(uri)) { 87 | Log.d("CardboardDeviceParams", "URI recognized as original cardboard device."); 88 | final CardboardDeviceParams deviceParams = new CardboardDeviceParams(); 89 | deviceParams.setDefaultValues(); 90 | return deviceParams; 91 | } 92 | if (!isCardboardDeviceUri(uri)) { 93 | Log.w("CardboardDeviceParams", String.format("URI \"%s\" not recognized as cardboard device.", uri)); 94 | return null; 95 | } 96 | CardboardDevice.DeviceParams params = null; 97 | final String paramsEncoded = uri.getQueryParameter(URI_KEY_PARAMS); 98 | if (paramsEncoded != null) { 99 | try { 100 | final byte[] bytes = Base64.decode(paramsEncoded, 11); 101 | params = (CardboardDevice.DeviceParams)MessageNano.mergeFrom((MessageNano)new CardboardDevice.DeviceParams(), bytes); 102 | Log.d("CardboardDeviceParams", "Read cardboard params from URI."); 103 | } 104 | catch (Exception e) { 105 | final String s = "CardboardDeviceParams"; 106 | final String s2 = "Parsing cardboard parameters from URI failed: "; 107 | final String value = String.valueOf(e.toString()); 108 | Log.w(s, (value.length() != 0) ? s2.concat(value) : new String(s2)); 109 | } 110 | } 111 | else { 112 | Log.w("CardboardDeviceParams", "No cardboard parameters in URI."); 113 | } 114 | return new CardboardDeviceParams(params); 115 | } 116 | 117 | public static CardboardDeviceParams createFromInputStream(final InputStream inputStream) { 118 | if (inputStream == null) { 119 | return null; 120 | } 121 | try { 122 | final ByteBuffer header = ByteBuffer.allocate(8); 123 | if (inputStream.read(header.array(), 0, header.array().length) == -1) { 124 | Log.e("CardboardDeviceParams", "Error parsing param record: end of stream."); 125 | return null; 126 | } 127 | final int sentinel = header.getInt(); 128 | final int length = header.getInt(); 129 | if (sentinel != STREAM_SENTINEL) { 130 | Log.e("CardboardDeviceParams", "Error parsing param record: incorrect sentinel."); 131 | return null; 132 | } 133 | final byte[] protoBytes = new byte[length]; 134 | if (inputStream.read(protoBytes, 0, protoBytes.length) == -1) { 135 | Log.e("CardboardDeviceParams", "Error parsing param record: end of stream."); 136 | return null; 137 | } 138 | return new CardboardDeviceParams((CardboardDevice.DeviceParams)MessageNano.mergeFrom((MessageNano)new CardboardDevice.DeviceParams(), protoBytes)); 139 | } 140 | catch (InvalidProtocolBufferNanoException e) { 141 | final String s = "CardboardDeviceParams"; 142 | final String s2 = "Error parsing protocol buffer: "; 143 | final String value = String.valueOf(e.toString()); 144 | Log.w(s, (value.length() != 0) ? s2.concat(value) : new String(s2)); 145 | } 146 | catch (IOException e2) { 147 | final String s3 = "CardboardDeviceParams"; 148 | final String s4 = "Error reading Cardboard parameters: "; 149 | final String value2 = String.valueOf(e2.toString()); 150 | Log.w(s3, (value2.length() != 0) ? s4.concat(value2) : new String(s4)); 151 | } 152 | return null; 153 | } 154 | 155 | public boolean writeToOutputStream(final OutputStream outputStream) { 156 | try { 157 | final byte[] paramBytes = this.toByteArray(); 158 | final ByteBuffer header = ByteBuffer.allocate(8); 159 | header.putInt(STREAM_SENTINEL); 160 | header.putInt(paramBytes.length); 161 | outputStream.write(header.array()); 162 | outputStream.write(paramBytes); 163 | return true; 164 | } 165 | catch (IOException e) { 166 | final String s = "CardboardDeviceParams"; 167 | final String s2 = "Error writing Cardboard parameters: "; 168 | final String value = String.valueOf(e.toString()); 169 | Log.w(s, (value.length() != 0) ? s2.concat(value) : new String(s2)); 170 | return false; 171 | } 172 | } 173 | 174 | public static CardboardDeviceParams createFromNfcContents(final NdefMessage tagContents) { 175 | if (tagContents == null) { 176 | Log.w("CardboardDeviceParams", "Could not get contents from NFC tag."); 177 | return null; 178 | } 179 | for (final NdefRecord record : tagContents.getRecords()) { 180 | final CardboardDeviceParams params = createFromUri(record.toUri()); 181 | if (params != null) { 182 | return params; 183 | } 184 | } 185 | return null; 186 | } 187 | 188 | private byte[] toByteArray() { 189 | final CardboardDevice.DeviceParams params = new CardboardDevice.DeviceParams(); 190 | params.setVendor(this.mVendor); 191 | params.setModel(this.mModel); 192 | params.setInterLensDistance(this.mInterLensDistance); 193 | params.setTrayBottomToLensHeight(this.mVerticalDistanceToLensCenter); 194 | params.setScreenToLensDistance(this.mScreenToLensDistance); 195 | params.leftEyeFieldOfViewAngles = this.mLeftEyeMaxFov.toProtobuf(); 196 | params.distortionCoefficients = this.mDistortion.toProtobuf(); 197 | if (this.mHasMagnet) { 198 | params.setHasMagnet(this.mHasMagnet); 199 | } 200 | return MessageNano.toByteArray((MessageNano)params); 201 | } 202 | 203 | public Uri toUri() { 204 | final byte[] paramsData = this.toByteArray(); 205 | final int paramsSize = paramsData.length; 206 | return new Uri.Builder().scheme("http").authority("google.com").appendEncodedPath("cardboard/cfg").appendQueryParameter("p", Base64.encodeToString(paramsData, 0, paramsSize, 11)).build(); 207 | } 208 | 209 | public void setVendor(final String vendor) { 210 | this.mVendor = ((vendor != null) ? vendor : ""); 211 | } 212 | 213 | public String getVendor() { 214 | return this.mVendor; 215 | } 216 | 217 | public void setModel(final String model) { 218 | this.mModel = ((model != null) ? model : ""); 219 | } 220 | 221 | public String getModel() { 222 | return this.mModel; 223 | } 224 | 225 | public void setInterLensDistance(final float interLensDistance) { 226 | this.mInterLensDistance = interLensDistance; 227 | } 228 | 229 | public float getInterLensDistance() { 230 | return this.mInterLensDistance; 231 | } 232 | 233 | public void setVerticalDistanceToLensCenter(final float verticalDistanceToLensCenter) { 234 | this.mVerticalDistanceToLensCenter = verticalDistanceToLensCenter; 235 | } 236 | 237 | public float getVerticalDistanceToLensCenter() { 238 | return this.mVerticalDistanceToLensCenter; 239 | } 240 | 241 | public void setScreenToLensDistance(final float screenToLensDistance) { 242 | this.mScreenToLensDistance = screenToLensDistance; 243 | } 244 | 245 | public float getScreenToLensDistance() { 246 | return this.mScreenToLensDistance; 247 | } 248 | 249 | public Distortion getDistortion() { 250 | return this.mDistortion; 251 | } 252 | 253 | public FieldOfView getLeftEyeMaxFov() { 254 | return this.mLeftEyeMaxFov; 255 | } 256 | 257 | public boolean getHasMagnet() { 258 | return this.mHasMagnet; 259 | } 260 | 261 | public void setHasMagnet(final boolean magnet) { 262 | this.mHasMagnet = magnet; 263 | } 264 | 265 | @Override 266 | public boolean equals(final Object other) { 267 | if (other == null) { 268 | return false; 269 | } 270 | if (other == this) { 271 | return true; 272 | } 273 | if (!(other instanceof CardboardDeviceParams)) { 274 | return false; 275 | } 276 | final CardboardDeviceParams o = (CardboardDeviceParams)other; 277 | return this.mVendor.equals(o.mVendor) && this.mModel.equals(o.mModel) && this.mInterLensDistance == o.mInterLensDistance && this.mVerticalDistanceToLensCenter == o.mVerticalDistanceToLensCenter && this.mScreenToLensDistance == o.mScreenToLensDistance && this.mLeftEyeMaxFov.equals(o.mLeftEyeMaxFov) && this.mDistortion.equals(o.mDistortion) && this.mHasMagnet == o.mHasMagnet; 278 | } 279 | 280 | @Override 281 | public String toString() { 282 | final StringBuilder append = new StringBuilder().append("{\n"); 283 | final String value = String.valueOf(String.valueOf(this.mVendor)); 284 | final StringBuilder append2 = append.append(new StringBuilder(12 + value.length()).append(" vendor: ").append(value).append(",\n").toString()); 285 | final String value2 = String.valueOf(String.valueOf(this.mModel)); 286 | final StringBuilder append3 = append2.append(new StringBuilder(11 + value2.length()).append(" model: ").append(value2).append(",\n").toString()).append(new StringBuilder(40).append(" inter_lens_distance: ").append(this.mInterLensDistance).append(",\n").toString()).append(new StringBuilder(53).append(" vertical_distance_to_lens_center: ").append(this.mVerticalDistanceToLensCenter).append(",\n").toString()).append(new StringBuilder(44).append(" screen_to_lens_distance: ").append(this.mScreenToLensDistance).append(",\n").toString()); 287 | final String value3 = String.valueOf(String.valueOf(this.mLeftEyeMaxFov.toString().replace("\n", "\n "))); 288 | final StringBuilder append4 = append3.append(new StringBuilder(22 + value3.length()).append(" left_eye_max_fov: ").append(value3).append(",\n").toString()); 289 | final String value4 = String.valueOf(String.valueOf(this.mDistortion.toString().replace("\n", "\n "))); 290 | return append4.append(new StringBuilder(16 + value4.length()).append(" distortion: ").append(value4).append(",\n").toString()).append(new StringBuilder(17).append(" magnet: ").append(this.mHasMagnet).append(",\n").toString()).append("}\n").toString(); 291 | } 292 | 293 | private void setDefaultValues() { 294 | this.mVendor = DEFAULT_VENDOR; 295 | this.mModel = DEFAULT_MODEL; 296 | this.mInterLensDistance = DEFAULT_INTER_LENS_DISTANCE; 297 | this.mVerticalDistanceToLensCenter = DEFAULT_VERTICAL_DISTANCE_TO_LENS_CENTER; 298 | this.mScreenToLensDistance = DEFAULT_SCREEN_TO_LENS_DISTANCE; 299 | this.mLeftEyeMaxFov = new FieldOfView(); 300 | this.mHasMagnet = true; 301 | this.mDistortion = new Distortion(); 302 | } 303 | 304 | private void copyFrom(final CardboardDeviceParams params) { 305 | this.mVendor = params.mVendor; 306 | this.mModel = params.mModel; 307 | this.mInterLensDistance = params.mInterLensDistance; 308 | this.mVerticalDistanceToLensCenter = params.mVerticalDistanceToLensCenter; 309 | this.mScreenToLensDistance = params.mScreenToLensDistance; 310 | this.mLeftEyeMaxFov = new FieldOfView(params.mLeftEyeMaxFov); 311 | this.mHasMagnet = params.mHasMagnet; 312 | this.mDistortion = new Distortion(params.mDistortion); 313 | } 314 | 315 | static { 316 | URI_ORIGINAL_CARDBOARD_NFC = new Uri.Builder().scheme(URI_SCHEME_LEGACY_CARDBOARD).authority(URI_HOST_LEGACY_CARDBOARD).build(); 317 | URI_ORIGINAL_CARDBOARD_QR_CODE = new Uri.Builder().scheme(HTTP_SCHEME).authority(URI_HOST_GOOGLE_SHORT).appendEncodedPath(URI_PATH_CARDBOARD_HOME).build(); 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/ConfigUtils.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | 4 | import android.content.res.AssetManager; 5 | import android.os.Environment; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | 11 | public abstract class ConfigUtils { 12 | public static final String CARDBOARD_CONFIG_FOLDER = "Cardboard"; 13 | public static final String CARDBOARD_DEVICE_PARAMS_FILE = "current_device_params"; 14 | public static final String CARDBOARD_PHONE_PARAMS_FILE = "phone_params"; 15 | 16 | public static File getConfigFile(final String filename) { 17 | final File configFolder = new File( 18 | Environment.getExternalStorageDirectory(), CARDBOARD_CONFIG_FOLDER); 19 | if (!configFolder.exists()) { 20 | configFolder.mkdirs(); 21 | } 22 | else if (!configFolder.isDirectory()) { 23 | final String value = String.valueOf(String.valueOf(configFolder)); 24 | throw new IllegalStateException(new StringBuilder() 25 | .append("Folder ").append(value).append(" already exists").toString()); 26 | } 27 | return new File(configFolder, filename); 28 | } 29 | 30 | public static InputStream openAssetConfigFile(final AssetManager assetManager, 31 | final String filename) throws IOException { 32 | final String assetPath = new File(CARDBOARD_CONFIG_FOLDER, filename).getPath(); 33 | return assetManager.open(assetPath); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/Constants.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | public class Constants { 4 | public static final String CARDBOARD_CATEGORY = "com.google.intent.category.CARDBOARD"; 5 | public static final String VERSION = "0.5.1"; 6 | } 7 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/Distortion.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import java.util.*; 4 | 5 | public class Distortion { 6 | private static final float[] DEFAULT_COEFFICIENTS; 7 | private float[] mCoefficients; 8 | 9 | public Distortion() { 10 | super(); 11 | this.mCoefficients = Distortion.DEFAULT_COEFFICIENTS.clone(); 12 | } 13 | 14 | public Distortion(final Distortion other) { 15 | super(); 16 | this.setCoefficients(other.mCoefficients); 17 | } 18 | 19 | public static Distortion parseFromProtobuf(final float[] coefficients) { 20 | final Distortion distortion = new Distortion(); 21 | distortion.setCoefficients(coefficients); 22 | return distortion; 23 | } 24 | 25 | public float[] toProtobuf() { 26 | return this.mCoefficients.clone(); 27 | } 28 | 29 | public void setCoefficients(final float[] coefficients) { 30 | this.mCoefficients = ((coefficients != null) ? coefficients.clone() : new float[0]); 31 | } 32 | 33 | public float[] getCoefficients() { 34 | return this.mCoefficients; 35 | } 36 | 37 | public float distortionFactor(final float radius) { 38 | float result = 1.0f; 39 | float rFactor = 1.0f; 40 | final float rSquared = radius * radius; 41 | for (final float ki : this.mCoefficients) { 42 | rFactor *= rSquared; 43 | result += ki * rFactor; 44 | } 45 | return result; 46 | } 47 | 48 | public float distort(final float radius) { 49 | return radius * this.distortionFactor(radius); 50 | } 51 | 52 | public float distortInverse(final float radius) { 53 | float r0 = radius / 0.9f; 54 | float r = radius * 0.9f; 55 | float dr0 = radius - this.distort(r0); 56 | while (Math.abs(r - r0) > 1.0E-4) { 57 | final float dr = radius - this.distort(r); 58 | final float r2 = r - dr * ((r - r0) / (dr - dr0)); 59 | r0 = r; 60 | r = r2; 61 | dr0 = dr; 62 | } 63 | return r; 64 | } 65 | 66 | private static double[] solveLeastSquares(final double[][] matA, final double[] vecY) { 67 | final int numSamples = matA.length; 68 | final int numCoefficients = matA[0].length; 69 | final double[][] matATA = new double[numCoefficients][numCoefficients]; 70 | for (int k = 0; k < numCoefficients; ++k) { 71 | for (int j = 0; j < numCoefficients; ++j) { 72 | double sum = 0.0; 73 | for (int i = 0; i < numSamples; ++i) { 74 | sum += matA[i][j] * matA[i][k]; 75 | } 76 | matATA[j][k] = sum; 77 | } 78 | } 79 | final double[][] matInvATA = new double[numCoefficients][numCoefficients]; 80 | if (numCoefficients != 2) { 81 | throw new RuntimeException(new StringBuilder() 82 | .append("solveLeastSquares: only 2 coefficients currently supported, ") 83 | .append(numCoefficients).append(" given.").toString()); 84 | } 85 | final double det = matATA[0][0] * matATA[1][1] - matATA[0][1] * matATA[1][0]; 86 | matInvATA[0][0] = matATA[1][1] / det; 87 | matInvATA[1][1] = matATA[0][0] / det; 88 | matInvATA[0][1] = -matATA[1][0] / det; 89 | matInvATA[1][0] = -matATA[0][1] / det; 90 | final double[] vecATY = new double[numCoefficients]; 91 | for (int l = 0; l < numCoefficients; ++l) { 92 | double sum2 = 0.0; 93 | for (int m = 0; m < numSamples; ++m) { 94 | sum2 += matA[m][l] * vecY[m]; 95 | } 96 | vecATY[l] = sum2; 97 | } 98 | final double[] vecX = new double[numCoefficients]; 99 | for (int j2 = 0; j2 < numCoefficients; ++j2) { 100 | double sum3 = 0.0; 101 | for (int i2 = 0; i2 < numCoefficients; ++i2) { 102 | sum3 += matInvATA[i2][j2] * vecATY[i2]; 103 | } 104 | vecX[j2] = sum3; 105 | } 106 | return vecX; 107 | } 108 | 109 | public Distortion getApproximateInverseDistortion(final float maxRadius) { 110 | final int numSamples = 10; 111 | final int numCoefficients = 2; 112 | final double[][] matA = new double[10][2]; 113 | final double[] vecY = new double[10]; 114 | for (int i = 0; i < 10; ++i) { 115 | final float r = maxRadius * (i + 1) / 10.0f; 116 | double v; 117 | final double rp = v = this.distort(r); 118 | for (int j = 0; j < 2; ++j) { 119 | v *= rp * rp; 120 | matA[i][j] = v; 121 | } 122 | vecY[i] = r - rp; 123 | } 124 | final double[] vecK = solveLeastSquares(matA, vecY); 125 | final float[] coefficients = new float[vecK.length]; 126 | for (int k = 0; k < vecK.length; ++k) { 127 | coefficients[k] = (float)vecK[k]; 128 | } 129 | final Distortion inverse = new Distortion(); 130 | inverse.setCoefficients(coefficients); 131 | return inverse; 132 | } 133 | 134 | @Override 135 | public boolean equals(final Object other) { 136 | if (other == null) { 137 | return false; 138 | } 139 | if (other == this) { 140 | return true; 141 | } 142 | if (!(other instanceof Distortion)) { 143 | return false; 144 | } 145 | final Distortion o = (Distortion)other; 146 | return Arrays.equals(this.mCoefficients, o.mCoefficients); 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | final StringBuilder builder = new StringBuilder().append("{\n").append(" coefficients: ["); 152 | for (int i = 0; i < this.mCoefficients.length; ++i) { 153 | builder.append(Float.toString(this.mCoefficients[i])); 154 | if (i < this.mCoefficients.length - 1) { 155 | builder.append(", "); 156 | } 157 | } 158 | builder.append("],\n}"); 159 | return builder.toString(); 160 | } 161 | 162 | static { 163 | DEFAULT_COEFFICIENTS = new float[] { 0.441f, 0.156f }; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/Eye.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | public class Eye { 4 | private final int mType; 5 | private final float[] mEyeView; 6 | private final Viewport mViewport; 7 | private final FieldOfView mFov; 8 | private volatile boolean mProjectionChanged; 9 | private float[] mPerspective; 10 | private float mLastZNear; 11 | private float mLastZFar; 12 | 13 | public Eye(final int type) { 14 | super(); 15 | this.mType = type; 16 | this.mEyeView = new float[16]; 17 | this.mViewport = new Viewport(); 18 | this.mFov = new FieldOfView(); 19 | this.mProjectionChanged = true; 20 | } 21 | 22 | public int getType() { 23 | return this.mType; 24 | } 25 | 26 | public float[] getEyeView() { 27 | return this.mEyeView; 28 | } 29 | 30 | public float[] getPerspective(final float zNear, final float zFar) { 31 | if (!this.mProjectionChanged && this.mLastZNear == zNear && this.mLastZFar == zFar) { 32 | return this.mPerspective; 33 | } 34 | if (this.mPerspective == null) { 35 | this.mPerspective = new float[16]; 36 | } 37 | this.getFov().toPerspectiveMatrix(zNear, zFar, this.mPerspective, 0); 38 | this.mLastZNear = zNear; 39 | this.mLastZFar = zFar; 40 | this.mProjectionChanged = false; 41 | return this.mPerspective; 42 | } 43 | 44 | public Viewport getViewport() { 45 | return this.mViewport; 46 | } 47 | 48 | public FieldOfView getFov() { 49 | return this.mFov; 50 | } 51 | 52 | public void setProjectionChanged() { 53 | this.mProjectionChanged = true; 54 | } 55 | 56 | public abstract static class Type { 57 | public static final int MONOCULAR = 0; 58 | public static final int LEFT = 1; 59 | public static final int RIGHT = 2; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/FieldOfView.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import android.opengl.*; 4 | 5 | public class FieldOfView { 6 | private static final float DEFAULT_MAX_FOV_LEFT_RIGHT = 40.0f; 7 | private static final float DEFAULT_MAX_FOV_BOTTOM = 40.0f; 8 | private static final float DEFAULT_MAX_FOV_TOP = 40.0f; 9 | private float mLeft; 10 | private float mRight; 11 | private float mBottom; 12 | private float mTop; 13 | 14 | public FieldOfView() { 15 | super(); 16 | this.mLeft = DEFAULT_MAX_FOV_LEFT_RIGHT; 17 | this.mRight = DEFAULT_MAX_FOV_LEFT_RIGHT; 18 | this.mBottom = DEFAULT_MAX_FOV_BOTTOM; 19 | this.mTop = DEFAULT_MAX_FOV_TOP; 20 | } 21 | 22 | public FieldOfView(final float left, final float right, final float bottom, final float top) { 23 | super(); 24 | this.mLeft = left; 25 | this.mRight = right; 26 | this.mBottom = bottom; 27 | this.mTop = top; 28 | } 29 | 30 | public FieldOfView(final FieldOfView other) { 31 | super(); 32 | this.copy(other); 33 | } 34 | 35 | public static FieldOfView parseFromProtobuf(final float[] angles) { 36 | if (angles.length != 4) { 37 | return null; 38 | } 39 | return new FieldOfView(angles[0], angles[1], angles[2], angles[3]); 40 | } 41 | 42 | public float[] toProtobuf() { 43 | return new float[] { this.mLeft, this.mRight, this.mBottom, this.mTop }; 44 | } 45 | 46 | public void copy(final FieldOfView other) { 47 | this.mLeft = other.mLeft; 48 | this.mRight = other.mRight; 49 | this.mBottom = other.mBottom; 50 | this.mTop = other.mTop; 51 | } 52 | 53 | public void setLeft(final float left) { 54 | this.mLeft = left; 55 | } 56 | 57 | public float getLeft() { 58 | return this.mLeft; 59 | } 60 | 61 | public void setRight(final float right) { 62 | this.mRight = right; 63 | } 64 | 65 | public float getRight() { 66 | return this.mRight; 67 | } 68 | 69 | public void setBottom(final float bottom) { 70 | this.mBottom = bottom; 71 | } 72 | 73 | public float getBottom() { 74 | return this.mBottom; 75 | } 76 | 77 | public void setTop(final float top) { 78 | this.mTop = top; 79 | } 80 | 81 | public float getTop() { 82 | return this.mTop; 83 | } 84 | 85 | public void toPerspectiveMatrix(final float near, final float far, final float[] perspective, final int offset) { 86 | if (offset + 16 > perspective.length) { 87 | throw new IllegalArgumentException("Not enough space to write the result"); 88 | } 89 | final float l = (float)(-Math.tan(Math.toRadians(this.mLeft))) * near; 90 | final float r = (float)Math.tan(Math.toRadians(this.mRight)) * near; 91 | final float b = (float)(-Math.tan(Math.toRadians(this.mBottom))) * near; 92 | final float t = (float)Math.tan(Math.toRadians(this.mTop)) * near; 93 | Matrix.frustumM(perspective, offset, l, r, b, t, near, far); 94 | } 95 | 96 | @Override 97 | public boolean equals(final Object other) { 98 | if (other == null) { 99 | return false; 100 | } 101 | if (other == this) { 102 | return true; 103 | } 104 | if (!(other instanceof FieldOfView)) { 105 | return false; 106 | } 107 | final FieldOfView o = (FieldOfView)other; 108 | return this.mLeft == o.mLeft && this.mRight == o.mRight && this.mBottom == o.mBottom && this.mTop == o.mTop; 109 | } 110 | 111 | @Override 112 | public String toString() { 113 | return "{\n" + new StringBuilder(25).append(" left: ").append(this.mLeft).append(",\n").toString() + new StringBuilder(26).append(" right: ").append(this.mRight).append(",\n").toString() + new StringBuilder(27).append(" bottom: ").append(this.mBottom).append(",\n").toString() + new StringBuilder(24).append(" top: ").append(this.mTop).append(",\n").toString() + "}"; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/GLStateBackup.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import java.nio.*; 4 | import android.opengl.*; 5 | import java.util.*; 6 | 7 | class GLStateBackup { 8 | private boolean mCullFaceEnabled; 9 | private boolean mScissorTestEnabled; 10 | private boolean mDepthTestEnabled; 11 | private IntBuffer mViewport; 12 | private IntBuffer mTexture2dId; 13 | private IntBuffer mTextureUnit; 14 | private IntBuffer mScissorBox; 15 | private IntBuffer mShaderProgram; 16 | private IntBuffer mArrayBufferBinding; 17 | private IntBuffer mElementArrayBufferBinding; 18 | private FloatBuffer mClearColor; 19 | private ArrayList mVertexAttributes; 20 | 21 | GLStateBackup() { 22 | super(); 23 | this.mViewport = IntBuffer.allocate(4); 24 | this.mTexture2dId = IntBuffer.allocate(1); 25 | this.mTextureUnit = IntBuffer.allocate(1); 26 | this.mScissorBox = IntBuffer.allocate(4); 27 | this.mShaderProgram = IntBuffer.allocate(1); 28 | this.mArrayBufferBinding = IntBuffer.allocate(1); 29 | this.mElementArrayBufferBinding = IntBuffer.allocate(1); 30 | this.mClearColor = FloatBuffer.allocate(4); 31 | this.mVertexAttributes = new ArrayList(); 32 | } 33 | 34 | void addTrackedVertexAttribute(final int attributeId) { 35 | this.mVertexAttributes.add(new VertexAttributeState(attributeId)); 36 | } 37 | 38 | void clearTrackedVertexAttributes() { 39 | this.mVertexAttributes.clear(); 40 | } 41 | 42 | void readFromGL() { 43 | GLES20.glGetIntegerv(GLES20.GL_VIEWPORT, this.mViewport); 44 | this.mCullFaceEnabled = GLES20.glIsEnabled(GLES20.GL_CULL_FACE); 45 | this.mScissorTestEnabled = GLES20.glIsEnabled(GLES20.GL_SCISSOR_TEST); 46 | this.mDepthTestEnabled = GLES20.glIsEnabled(GLES20.GL_DEPTH_TEST); 47 | GLES20.glGetFloatv(GLES20.GL_COLOR_CLEAR_VALUE, this.mClearColor); 48 | GLES20.glGetIntegerv(GLES20.GL_CURRENT_PROGRAM, this.mShaderProgram); 49 | GLES20.glGetIntegerv(GLES20.GL_SCISSOR_BOX, this.mScissorBox); 50 | GLES20.glGetIntegerv(GLES20.GL_ACTIVE_TEXTURE, this.mTextureUnit); 51 | GLES20.glGetIntegerv(GLES20.GL_TEXTURE_BINDING_2D, this.mTexture2dId); 52 | GLES20.glGetIntegerv(GLES20.GL_ARRAY_BUFFER_BINDING, this.mArrayBufferBinding); 53 | GLES20.glGetIntegerv(GLES20.GL_ELEMENT_ARRAY_BUFFER_BINDING, this.mElementArrayBufferBinding); 54 | for (final VertexAttributeState vas : this.mVertexAttributes) { 55 | vas.readFromGL(); 56 | } 57 | } 58 | 59 | void writeToGL() { 60 | for (final VertexAttributeState vas : this.mVertexAttributes) { 61 | vas.writeToGL(); 62 | } 63 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, this.mArrayBufferBinding.array()[0]); 64 | GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, this.mElementArrayBufferBinding.array()[0]); 65 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.mTexture2dId.array()[0]); 66 | GLES20.glActiveTexture(this.mTextureUnit.array()[0]); 67 | GLES20.glScissor(this.mScissorBox.array()[0], this.mScissorBox.array()[1], this.mScissorBox.array()[2], this.mScissorBox.array()[3]); 68 | GLES20.glUseProgram(this.mShaderProgram.array()[0]); 69 | GLES20.glClearColor(this.mClearColor.array()[0], this.mClearColor.array()[1], this.mClearColor.array()[2], this.mClearColor.array()[3]); 70 | if (this.mCullFaceEnabled) { 71 | GLES20.glEnable(GLES20.GL_CULL_FACE); 72 | } 73 | else { 74 | GLES20.glDisable(GLES20.GL_CULL_FACE); 75 | } 76 | if (this.mScissorTestEnabled) { 77 | GLES20.glEnable(GLES20.GL_SCISSOR_TEST); 78 | } 79 | else { 80 | GLES20.glDisable(GLES20.GL_SCISSOR_TEST); 81 | } 82 | if (this.mDepthTestEnabled) { 83 | GLES20.glEnable(GLES20.GL_DEPTH_TEST); 84 | } 85 | else { 86 | GLES20.glDisable(GLES20.GL_DEPTH_TEST); 87 | } 88 | GLES20.glViewport(this.mViewport.array()[0], this.mViewport.array()[1], this.mViewport.array()[2], this.mViewport.array()[3]); 89 | } 90 | 91 | private class VertexAttributeState 92 | { 93 | private int mAttributeId; 94 | private IntBuffer mEnabled; 95 | 96 | VertexAttributeState(final int attributeId) { 97 | super(); 98 | this.mEnabled = IntBuffer.allocate(1); 99 | this.mAttributeId = attributeId; 100 | } 101 | 102 | void readFromGL() { 103 | GLES20.glGetVertexAttribiv(this.mAttributeId, GLES20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, this.mEnabled); 104 | } 105 | 106 | void writeToGL() { 107 | if (this.mEnabled.array()[0] == 0) { 108 | GLES20.glDisableVertexAttribArray(this.mAttributeId); 109 | } 110 | else { 111 | GLES20.glEnableVertexAttribArray(this.mAttributeId); 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/HeadMountedDisplay.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | public class HeadMountedDisplay { 4 | private ScreenParams mScreen; 5 | private CardboardDeviceParams mCardboardDevice; 6 | 7 | public HeadMountedDisplay(final ScreenParams screenParams, final CardboardDeviceParams cardboardDevice) { 8 | super(); 9 | this.mScreen = screenParams; 10 | this.mCardboardDevice = cardboardDevice; 11 | } 12 | 13 | public HeadMountedDisplay(final HeadMountedDisplay hmd) { 14 | super(); 15 | this.mScreen = new ScreenParams(hmd.mScreen); 16 | this.mCardboardDevice = new CardboardDeviceParams(hmd.mCardboardDevice); 17 | } 18 | 19 | public void setScreenParams(final ScreenParams screen) { 20 | this.mScreen = new ScreenParams(screen); 21 | } 22 | 23 | public ScreenParams getScreenParams() { 24 | return this.mScreen; 25 | } 26 | 27 | public void setCardboardDeviceParams(final CardboardDeviceParams cardboardDeviceParams) { 28 | this.mCardboardDevice = new CardboardDeviceParams(cardboardDeviceParams); 29 | } 30 | 31 | public CardboardDeviceParams getCardboardDeviceParams() { 32 | return this.mCardboardDevice; 33 | } 34 | 35 | @Override 36 | public boolean equals(final Object other) { 37 | if (other == null) { 38 | return false; 39 | } 40 | if (other == this) { 41 | return true; 42 | } 43 | if (!(other instanceof HeadMountedDisplay)) { 44 | return false; 45 | } 46 | final HeadMountedDisplay o = (HeadMountedDisplay)other; 47 | return this.mScreen.equals(o.mScreen) && this.mCardboardDevice.equals(o.mCardboardDevice); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/HeadMountedDisplayManager.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import android.content.*; 4 | import android.util.*; 5 | import android.view.*; 6 | import java.io.*; 7 | 8 | public class HeadMountedDisplayManager { 9 | private static final String TAG = "HeadMountedDisplayManager"; 10 | private final HeadMountedDisplay mHmd; 11 | private final Context mContext; 12 | 13 | public HeadMountedDisplayManager(final Context context) { 14 | super(); 15 | this.mContext = context; 16 | this.mHmd = new HeadMountedDisplay(this.createScreenParams(), this.createCardboardDeviceParams()); 17 | } 18 | 19 | public HeadMountedDisplay getHeadMountedDisplay() { 20 | return this.mHmd; 21 | } 22 | 23 | public void onResume() { 24 | final CardboardDeviceParams deviceParams = this.createCardboardDeviceParamsFromExternalStorage(); 25 | if (deviceParams != null && !deviceParams.equals(this.mHmd.getCardboardDeviceParams())) { 26 | this.mHmd.setCardboardDeviceParams(deviceParams); 27 | Log.i("HeadMountedDisplayManager", "Successfully read updated device params from external storage"); 28 | } 29 | final ScreenParams screenParams = this.createScreenParamsFromExternalStorage(this.getDisplay()); 30 | if (screenParams != null && !screenParams.equals(this.mHmd.getScreenParams())) { 31 | this.mHmd.setScreenParams(screenParams); 32 | Log.i("HeadMountedDisplayManager", "Successfully read updated screen params from external storage"); 33 | } 34 | } 35 | 36 | public void onPause() { 37 | } 38 | 39 | public boolean updateCardboardDeviceParams(final CardboardDeviceParams cardboardDeviceParams) { 40 | if (cardboardDeviceParams == null || cardboardDeviceParams.equals(this.mHmd.getCardboardDeviceParams())) { 41 | return false; 42 | } 43 | this.mHmd.setCardboardDeviceParams(cardboardDeviceParams); 44 | this.writeCardboardParamsToExternalStorage(); 45 | return true; 46 | } 47 | 48 | public boolean updateScreenParams(final ScreenParams screenParams) { 49 | if (screenParams == null || screenParams.equals(this.mHmd.getScreenParams())) { 50 | return false; 51 | } 52 | this.mHmd.setScreenParams(screenParams); 53 | return true; 54 | } 55 | 56 | private void writeCardboardParamsToExternalStorage() { 57 | boolean success = false; 58 | OutputStream stream = null; 59 | try { 60 | stream = new BufferedOutputStream(new FileOutputStream(ConfigUtils.getConfigFile("current_device_params"))); 61 | success = this.mHmd.getCardboardDeviceParams().writeToOutputStream(stream); 62 | } 63 | catch (FileNotFoundException e) {} 64 | finally { 65 | if (stream != null) { 66 | try { 67 | stream.close(); 68 | } 69 | catch (IOException ex) {} 70 | } 71 | } 72 | if (!success) { 73 | Log.e("HeadMountedDisplayManager", "Could not write Cardboard parameters to external storage."); 74 | } 75 | else { 76 | Log.i("HeadMountedDisplayManager", "Successfully wrote Cardboard parameters to external storage."); 77 | } 78 | } 79 | 80 | private Display getDisplay() { 81 | final WindowManager windowManager = (WindowManager)this.mContext.getSystemService("window"); 82 | return windowManager.getDefaultDisplay(); 83 | } 84 | 85 | private ScreenParams createScreenParams() { 86 | final Display display = this.getDisplay(); 87 | final ScreenParams params = this.createScreenParamsFromExternalStorage(display); 88 | if (params != null) { 89 | Log.i("HeadMountedDisplayManager", "Successfully read screen params from external storage"); 90 | return params; 91 | } 92 | return new ScreenParams(display); 93 | } 94 | 95 | private CardboardDeviceParams createCardboardDeviceParams() { 96 | CardboardDeviceParams params = this.createCardboardDeviceParamsFromExternalStorage(); 97 | if (params != null) { 98 | Log.i("HeadMountedDisplayManager", "Successfully read device params from external storage"); 99 | return params; 100 | } 101 | params = this.createCardboardDeviceParamsFromAssetFolder(); 102 | if (params != null) { 103 | Log.i("HeadMountedDisplayManager", "Successfully read device params from asset folder"); 104 | this.writeCardboardParamsToExternalStorage(); 105 | return params; 106 | } 107 | return new CardboardDeviceParams(); 108 | } 109 | 110 | private CardboardDeviceParams createCardboardDeviceParamsFromAssetFolder() { 111 | try { 112 | InputStream stream = null; 113 | try { 114 | stream = new BufferedInputStream(ConfigUtils.openAssetConfigFile(this.mContext.getAssets(), "current_device_params")); 115 | return CardboardDeviceParams.createFromInputStream(stream); 116 | } 117 | finally { 118 | if (stream != null) { 119 | stream.close(); 120 | } 121 | } 122 | } 123 | catch (FileNotFoundException e) { 124 | final String s = "HeadMountedDisplayManager"; 125 | final String value = String.valueOf(String.valueOf(e)); 126 | Log.d(s, new StringBuilder(47 + value.length()).append("Bundled Cardboard device parameters not found: ").append(value).toString()); 127 | } 128 | catch (IOException e2) { 129 | final String s2 = "HeadMountedDisplayManager"; 130 | final String value2 = String.valueOf(String.valueOf(e2)); 131 | Log.e(s2, new StringBuilder(43 + value2.length()).append("Error reading config file in asset folder: ").append(value2).toString()); 132 | } 133 | return null; 134 | } 135 | 136 | private CardboardDeviceParams createCardboardDeviceParamsFromExternalStorage() { 137 | try { 138 | InputStream stream = null; 139 | try { 140 | stream = new BufferedInputStream(new FileInputStream(ConfigUtils.getConfigFile("current_device_params"))); 141 | return CardboardDeviceParams.createFromInputStream(stream); 142 | } 143 | finally { 144 | if (stream != null) { 145 | try { 146 | stream.close(); 147 | } 148 | catch (IOException ex) {} 149 | } 150 | } 151 | } 152 | catch (FileNotFoundException e) { 153 | final String s = "HeadMountedDisplayManager"; 154 | final String value = String.valueOf(String.valueOf(e)); 155 | Log.d(s, new StringBuilder(44 + value.length()).append("Cardboard device parameters file not found: ").append(value).toString()); 156 | return null; 157 | } 158 | } 159 | 160 | private ScreenParams createScreenParamsFromExternalStorage(final Display display) { 161 | try { 162 | InputStream stream = null; 163 | try { 164 | stream = new BufferedInputStream(new FileInputStream(ConfigUtils.getConfigFile("phone_params"))); 165 | return ScreenParams.createFromInputStream(display, stream); 166 | } 167 | finally { 168 | if (stream != null) { 169 | try { 170 | stream.close(); 171 | } 172 | catch (IOException ex) {} 173 | } 174 | } 175 | } 176 | catch (FileNotFoundException e) { 177 | final String s = "HeadMountedDisplayManager"; 178 | final String value = String.valueOf(String.valueOf(e)); 179 | Log.d(s, new StringBuilder(44 + value.length()).append("Cardboard screen parameters file not found: ").append(value).toString()); 180 | return null; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/HeadTransform.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import android.opengl.*; 4 | import android.util.*; 5 | 6 | public class HeadTransform { 7 | private static final float GIMBAL_LOCK_EPSILON = 0.01f; 8 | private final float[] mHeadView; 9 | 10 | public HeadTransform() { 11 | super(); 12 | Matrix.setIdentityM(this.mHeadView = new float[16], 0); 13 | } 14 | 15 | float[] getHeadView() { 16 | return this.mHeadView; 17 | } 18 | 19 | public void getHeadView(final float[] headView, final int offset) { 20 | if (offset + 16 > headView.length) { 21 | throw new IllegalArgumentException("Not enough space to write the result"); 22 | } 23 | System.arraycopy(this.mHeadView, 0, headView, offset, 16); 24 | } 25 | 26 | public void getForwardVector(final float[] forward, final int offset) { 27 | if (offset + 3 > forward.length) { 28 | throw new IllegalArgumentException("Not enough space to write the result"); 29 | } 30 | for (int i = 0; i < 3; ++i) { 31 | forward[i + offset] = -this.mHeadView[8 + i]; 32 | } 33 | } 34 | 35 | public void getUpVector(final float[] up, final int offset) { 36 | if (offset + 3 > up.length) { 37 | throw new IllegalArgumentException("Not enough space to write the result"); 38 | } 39 | for (int i = 0; i < 3; ++i) { 40 | up[i + offset] = this.mHeadView[4 + i]; 41 | } 42 | } 43 | 44 | public void getRightVector(final float[] right, final int offset) { 45 | if (offset + 3 > right.length) { 46 | throw new IllegalArgumentException("Not enough space to write the result"); 47 | } 48 | for (int i = 0; i < 3; ++i) { 49 | right[i + offset] = this.mHeadView[i]; 50 | } 51 | } 52 | 53 | public void getQuaternion(final float[] quaternion, final int offset) { 54 | if (offset + 4 > quaternion.length) { 55 | throw new IllegalArgumentException("Not enough space to write the result"); 56 | } 57 | final float[] m = this.mHeadView; 58 | final float t = m[0] + m[5] + m[10]; 59 | float w; 60 | float x; 61 | float y; 62 | float z; 63 | if (t >= 0.0f) { 64 | float s = FloatMath.sqrt(t + 1.0f); 65 | w = 0.5f * s; 66 | s = 0.5f / s; 67 | x = (m[9] - m[6]) * s; 68 | y = (m[2] - m[8]) * s; 69 | z = (m[4] - m[1]) * s; 70 | } 71 | else if (m[0] > m[5] && m[0] > m[10]) { 72 | float s = FloatMath.sqrt(1.0f + m[0] - m[5] - m[10]); 73 | x = s * 0.5f; 74 | s = 0.5f / s; 75 | y = (m[4] + m[1]) * s; 76 | z = (m[2] + m[8]) * s; 77 | w = (m[9] - m[6]) * s; 78 | } 79 | else if (m[5] > m[10]) { 80 | float s = FloatMath.sqrt(1.0f + m[5] - m[0] - m[10]); 81 | y = s * 0.5f; 82 | s = 0.5f / s; 83 | x = (m[4] + m[1]) * s; 84 | z = (m[9] + m[6]) * s; 85 | w = (m[2] - m[8]) * s; 86 | } 87 | else { 88 | float s = FloatMath.sqrt(1.0f + m[10] - m[0] - m[5]); 89 | z = s * 0.5f; 90 | s = 0.5f / s; 91 | x = (m[2] + m[8]) * s; 92 | y = (m[9] + m[6]) * s; 93 | w = (m[4] - m[1]) * s; 94 | } 95 | quaternion[offset + 0] = x; 96 | quaternion[offset + 1] = y; 97 | quaternion[offset + 2] = z; 98 | quaternion[offset + 3] = w; 99 | } 100 | 101 | public void getEulerAngles(final float[] eulerAngles, final int offset) { 102 | if (offset + 3 > eulerAngles.length) { 103 | throw new IllegalArgumentException("Not enough space to write the result"); 104 | } 105 | final float pitch = (float)Math.asin(this.mHeadView[6]); 106 | float yaw; 107 | float roll; 108 | if (FloatMath.sqrt(1.0f - this.mHeadView[6] * this.mHeadView[6]) >= GIMBAL_LOCK_EPSILON) { 109 | yaw = (float)Math.atan2(-this.mHeadView[2], this.mHeadView[10]); 110 | roll = (float)Math.atan2(-this.mHeadView[4], this.mHeadView[5]); 111 | } 112 | else { 113 | yaw = 0.0f; 114 | roll = (float)Math.atan2(this.mHeadView[1], this.mHeadView[0]); 115 | } 116 | eulerAngles[offset + 0] = -pitch; 117 | eulerAngles[offset + 1] = -yaw; 118 | eulerAngles[offset + 2] = -roll; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/PhoneParams.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import com.google.vrtoolkit.cardboard.proto.*; 4 | import java.nio.*; 5 | import android.util.*; 6 | import com.google.protobuf.nano.*; 7 | import java.io.*; 8 | 9 | public class PhoneParams { 10 | private static final String TAG; 11 | private static final int STREAM_SENTINEL = 779508118; 12 | 13 | static Phone.PhoneParams readFromInputStream(final InputStream inputStream) { 14 | if (inputStream == null) { 15 | return null; 16 | } 17 | try { 18 | final ByteBuffer header = ByteBuffer.allocate(8); 19 | if (inputStream.read(header.array(), 0, header.array().length) == -1) { 20 | Log.e(PhoneParams.TAG, "Error parsing param record: end of stream."); 21 | return null; 22 | } 23 | final int sentinel = header.getInt(); 24 | final int length = header.getInt(); 25 | if (sentinel != STREAM_SENTINEL) { 26 | Log.e(PhoneParams.TAG, "Error parsing param record: incorrect sentinel."); 27 | return null; 28 | } 29 | final byte[] protoBytes = new byte[length]; 30 | if (inputStream.read(protoBytes, 0, protoBytes.length) == -1) { 31 | Log.e(PhoneParams.TAG, "Error parsing param record: end of stream."); 32 | return null; 33 | } 34 | return (Phone.PhoneParams)MessageNano.mergeFrom((MessageNano)new Phone.PhoneParams(), protoBytes); 35 | } 36 | catch (InvalidProtocolBufferNanoException e) { 37 | final String tag = PhoneParams.TAG; 38 | final String s = "Error parsing protocol buffer: "; 39 | final String value = String.valueOf(e.toString()); 40 | Log.w(tag, (value.length() != 0) ? s.concat(value) : new String(s)); 41 | } 42 | catch (IOException e2) { 43 | final String tag2 = PhoneParams.TAG; 44 | final String s2 = "Error reading Cardboard parameters: "; 45 | final String value2 = String.valueOf(e2.toString()); 46 | Log.w(tag2, (value2.length() != 0) ? s2.concat(value2) : new String(s2)); 47 | } 48 | return null; 49 | } 50 | 51 | static Phone.PhoneParams readFromExternalStorage() { 52 | try { 53 | InputStream stream = null; 54 | try { 55 | stream = new BufferedInputStream(new FileInputStream(ConfigUtils.getConfigFile("phone_params"))); 56 | return readFromInputStream(stream); 57 | } 58 | finally { 59 | if (stream != null) { 60 | try { 61 | stream.close(); 62 | } 63 | catch (IOException ex) {} 64 | } 65 | } 66 | } 67 | catch (FileNotFoundException e) { 68 | final String tag = PhoneParams.TAG; 69 | final String value = String.valueOf(String.valueOf(e)); 70 | Log.d(tag, new StringBuilder(43 + value.length()).append("Cardboard phone parameters file not found: ").append(value).toString()); 71 | return null; 72 | } 73 | } 74 | 75 | static { 76 | TAG = PhoneParams.class.getSimpleName(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/ScreenParams.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import android.view.*; 4 | import android.util.*; 5 | import com.google.vrtoolkit.cardboard.proto.*; 6 | import java.io.*; 7 | 8 | public class ScreenParams { 9 | private static final float METERS_PER_INCH = 0.0254f; 10 | private static final float DEFAULT_BORDER_SIZE_METERS = 0.003f; 11 | private int mWidth; 12 | private int mHeight; 13 | private float mXMetersPerPixel; 14 | private float mYMetersPerPixel; 15 | private float mBorderSizeMeters; 16 | 17 | public ScreenParams(final Display display) { 18 | super(); 19 | final DisplayMetrics metrics = new DisplayMetrics(); 20 | try { 21 | display.getRealMetrics(metrics); 22 | } 23 | catch (NoSuchMethodError e) { 24 | display.getMetrics(metrics); 25 | } 26 | this.mXMetersPerPixel = METERS_PER_INCH / metrics.xdpi; 27 | this.mYMetersPerPixel = METERS_PER_INCH / metrics.ydpi; 28 | this.mWidth = metrics.widthPixels; 29 | this.mHeight = metrics.heightPixels; 30 | this.mBorderSizeMeters = DEFAULT_BORDER_SIZE_METERS; 31 | if (this.mHeight > this.mWidth) { 32 | final int tempPx = this.mWidth; 33 | this.mWidth = this.mHeight; 34 | this.mHeight = tempPx; 35 | final float tempMetersPerPixel = this.mXMetersPerPixel; 36 | this.mXMetersPerPixel = this.mYMetersPerPixel; 37 | this.mYMetersPerPixel = tempMetersPerPixel; 38 | } 39 | } 40 | 41 | public static ScreenParams fromProto(final Display display, final Phone.PhoneParams params) { 42 | if (params == null) { 43 | return null; 44 | } 45 | final ScreenParams screenParams = new ScreenParams(display); 46 | if (params.hasXPpi()) { 47 | screenParams.mXMetersPerPixel = METERS_PER_INCH / params.getXPpi(); 48 | } 49 | if (params.hasYPpi()) { 50 | screenParams.mYMetersPerPixel = METERS_PER_INCH / params.getYPpi(); 51 | } 52 | if (params.hasBottomBezelHeight()) { 53 | screenParams.mBorderSizeMeters = params.getBottomBezelHeight(); 54 | } 55 | return screenParams; 56 | } 57 | 58 | public ScreenParams(final ScreenParams params) { 59 | super(); 60 | this.mWidth = params.mWidth; 61 | this.mHeight = params.mHeight; 62 | this.mXMetersPerPixel = params.mXMetersPerPixel; 63 | this.mYMetersPerPixel = params.mYMetersPerPixel; 64 | this.mBorderSizeMeters = params.mBorderSizeMeters; 65 | } 66 | 67 | public void setWidth(final int width) { 68 | this.mWidth = width; 69 | } 70 | 71 | public int getWidth() { 72 | return this.mWidth; 73 | } 74 | 75 | public void setHeight(final int height) { 76 | this.mHeight = height; 77 | } 78 | 79 | public int getHeight() { 80 | return this.mHeight; 81 | } 82 | 83 | public float getWidthMeters() { 84 | return this.mWidth * this.mXMetersPerPixel; 85 | } 86 | 87 | public float getHeightMeters() { 88 | return this.mHeight * this.mYMetersPerPixel; 89 | } 90 | 91 | public void setBorderSizeMeters(final float screenBorderSize) { 92 | this.mBorderSizeMeters = screenBorderSize; 93 | } 94 | 95 | public float getBorderSizeMeters() { 96 | return this.mBorderSizeMeters; 97 | } 98 | 99 | @Override 100 | public boolean equals(final Object other) { 101 | if (other == null) { 102 | return false; 103 | } 104 | if (other == this) { 105 | return true; 106 | } 107 | if (!(other instanceof ScreenParams)) { 108 | return false; 109 | } 110 | final ScreenParams o = (ScreenParams)other; 111 | return this.mWidth == o.mWidth && this.mHeight == o.mHeight && this.mXMetersPerPixel == o.mXMetersPerPixel && this.mYMetersPerPixel == o.mYMetersPerPixel && this.mBorderSizeMeters == o.mBorderSizeMeters; 112 | } 113 | 114 | @Override 115 | public String toString() { 116 | return "{\n" + new StringBuilder(22).append(" width: ").append(this.mWidth).append(",\n").toString() + new StringBuilder(23).append(" height: ").append(this.mHeight).append(",\n").toString() + new StringBuilder(39).append(" x_meters_per_pixel: ").append(this.mXMetersPerPixel).append(",\n").toString() + new StringBuilder(39).append(" y_meters_per_pixel: ").append(this.mYMetersPerPixel).append(",\n").toString() + new StringBuilder(39).append(" border_size_meters: ").append(this.mBorderSizeMeters).append(",\n").toString() + "}"; 117 | } 118 | 119 | public static ScreenParams createFromInputStream(final Display display, final InputStream inputStream) { 120 | final Phone.PhoneParams phoneParams = PhoneParams.readFromInputStream(inputStream); 121 | if (phoneParams == null) { 122 | return null; 123 | } 124 | return fromProto(display, phoneParams); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/UiUtils.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import android.content.pm.*; 4 | import java.util.*; 5 | import android.net.*; 6 | import android.widget.*; 7 | import android.content.*; 8 | import android.os.*; 9 | import android.app.*; 10 | 11 | class UiUtils { 12 | private static final String CARDBOARD_WEBSITE = "http://google.com/cardboard/cfg?vrtoolkit_version=0.5.1"; 13 | private static final String CARDBOARD_CONFIGURE_ACTION = "com.google.vrtoolkit.cardboard.CONFIGURE"; 14 | private static final String INTENT_EXTRAS_VERSION_KEY = "VERSION"; 15 | private static final String NO_BROWSER_TEXT = "No browser to open website."; 16 | 17 | static void launchOrInstallCardboard(final Context context) { 18 | final PackageManager pm = context.getPackageManager(); 19 | final Intent settingsIntent = new Intent(); 20 | settingsIntent.setAction("com.google.vrtoolkit.cardboard.CONFIGURE"); 21 | settingsIntent.putExtra("VERSION", "0.5.1"); 22 | final List resolveInfos = (List)pm.queryIntentActivities(settingsIntent, 0); 23 | final List intentsToGoogleCardboard = new ArrayList(); 24 | for (final ResolveInfo info : resolveInfos) { 25 | final String pkgName = info.activityInfo.packageName; 26 | if (pkgName.startsWith("com.google.")) { 27 | final Intent intent = new Intent(settingsIntent); 28 | intent.setClassName(pkgName, info.activityInfo.name); 29 | intentsToGoogleCardboard.add(intent); 30 | } 31 | } 32 | if (intentsToGoogleCardboard.isEmpty()) { 33 | showInstallDialog(context); 34 | } 35 | else if (intentsToGoogleCardboard.size() == 1) { 36 | showConfigureDialog(context, intentsToGoogleCardboard.get(0)); 37 | } 38 | else { 39 | showConfigureDialog(context, settingsIntent); 40 | } 41 | } 42 | 43 | private static void showInstallDialog(final Context context) { 44 | final DialogInterface.OnClickListener listener = (DialogInterface.OnClickListener)new DialogInterface.OnClickListener() { 45 | public void onClick(final DialogInterface dialog, final int id) { 46 | try { 47 | context.startActivity(new Intent("android.intent.action.VIEW", Uri.parse("http://google.com/cardboard/cfg?vrtoolkit_version=0.5.1"))); 48 | } 49 | catch (ActivityNotFoundException e) { 50 | Toast.makeText(context.getApplicationContext(), (CharSequence)"No browser to open website.", 1).show(); 51 | } 52 | } 53 | }; 54 | final FragmentManager fragmentManager = ((Activity)context).getFragmentManager(); 55 | final DialogFragment dialog = new SettingsDialogFragment((DialogStrings)new InstallDialogStrings(), listener); 56 | dialog.show(fragmentManager, "InstallCardboardDialog"); 57 | } 58 | 59 | private static void showConfigureDialog(final Context context, final Intent intent) { 60 | final DialogInterface.OnClickListener listener = (DialogInterface.OnClickListener)new DialogInterface.OnClickListener() { 61 | public void onClick(final DialogInterface dialog, final int id) { 62 | try { 63 | context.startActivity(intent); 64 | } 65 | catch (ActivityNotFoundException e) { 66 | showInstallDialog(context); 67 | } 68 | } 69 | }; 70 | final FragmentManager fragmentManager = ((Activity)context).getFragmentManager(); 71 | final DialogFragment dialog = new SettingsDialogFragment((DialogStrings)new ConfigureDialogStrings(), listener); 72 | dialog.show(fragmentManager, "ConfigureCardboardDialog"); 73 | } 74 | 75 | private static class DialogStrings { 76 | String mTitle; 77 | String mMessage; 78 | String mPositiveButtonText; 79 | String mNegativeButtonText; 80 | } 81 | 82 | private static class InstallDialogStrings extends DialogStrings { 83 | InstallDialogStrings() { 84 | super(); 85 | this.mTitle = "Configure"; 86 | this.mMessage = "Get the Cardboard app in order to configure your viewer."; 87 | this.mPositiveButtonText = "Go to Play Store"; 88 | this.mNegativeButtonText = "Cancel"; 89 | } 90 | } 91 | 92 | private static class ConfigureDialogStrings extends DialogStrings { 93 | ConfigureDialogStrings() { 94 | super(); 95 | this.mTitle = "Configure"; 96 | this.mMessage = "Set up your viewer for the best experience."; 97 | this.mPositiveButtonText = "Setup"; 98 | this.mNegativeButtonText = "Cancel"; 99 | } 100 | } 101 | 102 | private static class SettingsDialogFragment extends DialogFragment { 103 | private DialogStrings mDialogStrings; 104 | private DialogInterface.OnClickListener mPositiveButtonListener; 105 | 106 | private SettingsDialogFragment(final DialogStrings dialogStrings, final DialogInterface.OnClickListener positiveButtonListener) { 107 | super(); 108 | this.mDialogStrings = dialogStrings; 109 | this.mPositiveButtonListener = positiveButtonListener; 110 | } 111 | 112 | public Dialog onCreateDialog(final Bundle savedInstanceState) { 113 | final AlertDialog.Builder builder = new AlertDialog.Builder((Context)this.getActivity()); 114 | builder.setTitle((CharSequence)this.mDialogStrings.mTitle).setMessage((CharSequence)this.mDialogStrings.mMessage).setPositiveButton((CharSequence)this.mDialogStrings.mPositiveButtonText, this.mPositiveButtonListener).setNegativeButton((CharSequence)this.mDialogStrings.mNegativeButtonText, (DialogInterface.OnClickListener)null); 115 | return (Dialog)builder.create(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/Viewport.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard; 2 | 3 | import android.opengl.*; 4 | 5 | public class Viewport { 6 | public int x; 7 | public int y; 8 | public int width; 9 | public int height; 10 | 11 | public void setViewport(final int x, final int y, final int width, final int height) { 12 | this.x = x; 13 | this.y = y; 14 | this.width = width; 15 | this.height = height; 16 | } 17 | 18 | public void setGLViewport() { 19 | GLES20.glViewport(this.x, this.y, this.width, this.height); 20 | } 21 | 22 | public void setGLScissor() { 23 | GLES20.glScissor(this.x, this.y, this.width, this.height); 24 | } 25 | 26 | public void getAsArray(final int[] array, final int offset) { 27 | if (offset + 4 > array.length) { 28 | throw new IllegalArgumentException("Not enough space to write the result"); 29 | } 30 | array[offset] = this.x; 31 | array[offset + 1] = this.y; 32 | array[offset + 2] = this.width; 33 | array[offset + 3] = this.height; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "{\n" + new StringBuilder(18).append(" x: ").append(this.x).append(",\n").toString() + new StringBuilder(18).append(" y: ").append(this.y).append(",\n").toString() + new StringBuilder(22).append(" width: ").append(this.width).append(",\n").toString() + new StringBuilder(23).append(" height: ").append(this.height).append(",\n").toString() + "}"; 39 | } 40 | 41 | @Override 42 | public boolean equals(final Object obj) { 43 | if (obj == this) { 44 | return true; 45 | } 46 | if (!(obj instanceof Viewport)) { 47 | return false; 48 | } 49 | final Viewport other = (Viewport)obj; 50 | return this.x == other.x && this.y == other.y && this.width == other.width && this.height == other.height; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return Integer.valueOf(this.x).hashCode() ^ Integer.valueOf(this.y).hashCode() ^ Integer.valueOf(this.width).hashCode() ^ Integer.valueOf(this.height).hashCode(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/proto/CardboardDevice.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.proto; 2 | 3 | import java.io.*; 4 | import com.google.protobuf.nano.*; 5 | 6 | public interface CardboardDevice { 7 | public static final class DeviceParams extends MessageNano { 8 | private static volatile DeviceParams[] _emptyArray; 9 | private int bitField0_; 10 | private String vendor_; 11 | private String model_; 12 | private float screenToLensDistance_; 13 | private float interLensDistance_; 14 | public float[] leftEyeFieldOfViewAngles; 15 | private float trayBottomToLensHeight_; 16 | public float[] distortionCoefficients; 17 | private boolean hasMagnet_; 18 | 19 | public static DeviceParams[] emptyArray() { 20 | if (DeviceParams._emptyArray == null) { 21 | synchronized (InternalNano.LAZY_INIT_LOCK) { 22 | if (DeviceParams._emptyArray == null) { 23 | DeviceParams._emptyArray = new DeviceParams[0]; 24 | } 25 | } 26 | } 27 | return DeviceParams._emptyArray; 28 | } 29 | 30 | public String getVendor() { 31 | return this.vendor_; 32 | } 33 | 34 | public DeviceParams setVendor(String value) { 35 | if (value == null) { 36 | throw new NullPointerException(); 37 | } 38 | this.vendor_ = value; 39 | this.bitField0_ |= 0x1; 40 | return this; 41 | } 42 | 43 | public boolean hasVendor() { 44 | return (this.bitField0_ & 0x1) != 0x0; 45 | } 46 | 47 | public DeviceParams clearVendor() { 48 | this.vendor_ = ""; 49 | this.bitField0_ &= 0xFFFFFFFE; 50 | return this; 51 | } 52 | 53 | public String getModel() { 54 | return this.model_; 55 | } 56 | 57 | public DeviceParams setModel(String value) { 58 | if (value == null) { 59 | throw new NullPointerException(); 60 | } 61 | this.model_ = value; 62 | this.bitField0_ |= 0x2; 63 | return this; 64 | } 65 | 66 | public boolean hasModel() { 67 | return (this.bitField0_ & 0x2) != 0x0; 68 | } 69 | 70 | public DeviceParams clearModel() { 71 | this.model_ = ""; 72 | this.bitField0_ &= 0xFFFFFFFD; 73 | return this; 74 | } 75 | 76 | public float getScreenToLensDistance() { 77 | return this.screenToLensDistance_; 78 | } 79 | 80 | public DeviceParams setScreenToLensDistance(float value) { 81 | this.screenToLensDistance_ = value; 82 | this.bitField0_ |= 0x4; 83 | return this; 84 | } 85 | 86 | public boolean hasScreenToLensDistance() { 87 | return (this.bitField0_ & 0x4) != 0x0; 88 | } 89 | 90 | public DeviceParams clearScreenToLensDistance() { 91 | this.screenToLensDistance_ = 0.0f; 92 | this.bitField0_ &= 0xFFFFFFFB; 93 | return this; 94 | } 95 | 96 | public float getInterLensDistance() { 97 | return this.interLensDistance_; 98 | } 99 | 100 | public DeviceParams setInterLensDistance(float value) { 101 | this.interLensDistance_ = value; 102 | this.bitField0_ |= 0x8; 103 | return this; 104 | } 105 | 106 | public boolean hasInterLensDistance() { 107 | return (this.bitField0_ & 0x8) != 0x0; 108 | } 109 | 110 | public DeviceParams clearInterLensDistance() { 111 | this.interLensDistance_ = 0.0f; 112 | this.bitField0_ &= 0xFFFFFFF7; 113 | return this; 114 | } 115 | 116 | public float getTrayBottomToLensHeight() { 117 | return this.trayBottomToLensHeight_; 118 | } 119 | 120 | public DeviceParams setTrayBottomToLensHeight(float value) { 121 | this.trayBottomToLensHeight_ = value; 122 | this.bitField0_ |= 0x10; 123 | return this; 124 | } 125 | 126 | public boolean hasTrayBottomToLensHeight() { 127 | return (this.bitField0_ & 0x10) != 0x0; 128 | } 129 | 130 | public DeviceParams clearTrayBottomToLensHeight() { 131 | this.trayBottomToLensHeight_ = 0.0f; 132 | this.bitField0_ &= 0xFFFFFFEF; 133 | return this; 134 | } 135 | 136 | public boolean getHasMagnet() { 137 | return this.hasMagnet_; 138 | } 139 | 140 | public DeviceParams setHasMagnet(boolean value) { 141 | this.hasMagnet_ = value; 142 | this.bitField0_ |= 0x20; 143 | return this; 144 | } 145 | 146 | public boolean hasHasMagnet() { 147 | return (this.bitField0_ & 0x20) != 0x0; 148 | } 149 | 150 | public DeviceParams clearHasMagnet() { 151 | this.hasMagnet_ = false; 152 | this.bitField0_ &= 0xFFFFFFDF; 153 | return this; 154 | } 155 | 156 | public DeviceParams() { 157 | super(); 158 | this.clear(); 159 | } 160 | 161 | public DeviceParams clear() { 162 | this.bitField0_ = 0; 163 | this.vendor_ = ""; 164 | this.model_ = ""; 165 | this.screenToLensDistance_ = 0.0f; 166 | this.interLensDistance_ = 0.0f; 167 | this.leftEyeFieldOfViewAngles = WireFormatNano.EMPTY_FLOAT_ARRAY; 168 | this.trayBottomToLensHeight_ = 0.0f; 169 | this.distortionCoefficients = WireFormatNano.EMPTY_FLOAT_ARRAY; 170 | this.hasMagnet_ = false; 171 | this.cachedSize = -1; 172 | return this; 173 | } 174 | 175 | public void writeTo(CodedOutputByteBufferNano output) throws IOException { 176 | int dataSize; 177 | if ((this.bitField0_ & 0x1) != 0x0) { 178 | output.writeString(1, this.vendor_); 179 | } 180 | if ((this.bitField0_ & 0x2) != 0x0) { 181 | output.writeString(2, this.model_); 182 | } 183 | if ((this.bitField0_ & 0x4) != 0x0) { 184 | output.writeFloat(3, this.screenToLensDistance_); 185 | } 186 | if ((this.bitField0_ & 0x8) != 0x0) { 187 | output.writeFloat(4, this.interLensDistance_); 188 | } 189 | if (this.leftEyeFieldOfViewAngles != null && this.leftEyeFieldOfViewAngles.length > 0) { 190 | dataSize = 4 * this.leftEyeFieldOfViewAngles.length; 191 | output.writeRawVarint32(42); 192 | output.writeRawVarint32(dataSize); 193 | for (int i = 0; i < this.leftEyeFieldOfViewAngles.length; ++i) { 194 | output.writeFloatNoTag(this.leftEyeFieldOfViewAngles[i]); 195 | } 196 | } 197 | if ((this.bitField0_ & 0x10) != 0x0) { 198 | output.writeFloat(6, this.trayBottomToLensHeight_); 199 | } 200 | if (this.distortionCoefficients != null && this.distortionCoefficients.length > 0) { 201 | dataSize = 4 * this.distortionCoefficients.length; 202 | output.writeRawVarint32(58); 203 | output.writeRawVarint32(dataSize); 204 | for (int i = 0; i < this.distortionCoefficients.length; ++i) { 205 | output.writeFloatNoTag(this.distortionCoefficients[i]); 206 | } 207 | } 208 | if ((this.bitField0_ & 0x20) != 0x0) { 209 | output.writeBool(10, this.hasMagnet_); 210 | } 211 | super.writeTo(output); 212 | } 213 | 214 | protected int computeSerializedSize() { 215 | int size; 216 | int dataSize; 217 | size = super.computeSerializedSize(); 218 | if ((this.bitField0_ & 0x1) != 0x0) { 219 | size += CodedOutputByteBufferNano.computeStringSize(1, this.vendor_); 220 | } 221 | if ((this.bitField0_ & 0x2) != 0x0) { 222 | size += CodedOutputByteBufferNano.computeStringSize(2, this.model_); 223 | } 224 | if ((this.bitField0_ & 0x4) != 0x0) { 225 | size += CodedOutputByteBufferNano.computeFloatSize(3, this.screenToLensDistance_); 226 | } 227 | if ((this.bitField0_ & 0x8) != 0x0) { 228 | size += CodedOutputByteBufferNano.computeFloatSize(4, this.interLensDistance_); 229 | } 230 | if (this.leftEyeFieldOfViewAngles != null && this.leftEyeFieldOfViewAngles.length > 0) { 231 | dataSize = 4 * this.leftEyeFieldOfViewAngles.length; 232 | size += dataSize; 233 | size = ++size + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize); 234 | } 235 | if ((this.bitField0_ & 0x10) != 0x0) { 236 | size += CodedOutputByteBufferNano.computeFloatSize(6, this.trayBottomToLensHeight_); 237 | } 238 | if (this.distortionCoefficients != null && this.distortionCoefficients.length > 0) { 239 | dataSize = 4 * this.distortionCoefficients.length; 240 | size += dataSize; 241 | size = ++size + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize); 242 | } 243 | if ((this.bitField0_ & 0x20) != 0x0) { 244 | size += CodedOutputByteBufferNano.computeBoolSize(10, this.hasMagnet_); 245 | } 246 | return size; 247 | } 248 | 249 | public DeviceParams mergeFrom(CodedInputByteBufferNano input) throws IOException { 250 | int tag; 251 | int arrayLength; 252 | int i; 253 | float[] newArray; 254 | int length; 255 | int limit; 256 | int arrayLength2; 257 | int j; 258 | float[] newArray2; 259 | while (true) { 260 | tag = input.readTag(); 261 | switch (tag) { 262 | case 0: { 263 | return this; 264 | } 265 | default: { 266 | if (!WireFormatNano.parseUnknownField(input, tag)) { 267 | return this; 268 | } 269 | continue; 270 | } 271 | case 10: { 272 | this.vendor_ = input.readString(); 273 | this.bitField0_ |= 0x1; 274 | continue; 275 | } 276 | case 18: { 277 | this.model_ = input.readString(); 278 | this.bitField0_ |= 0x2; 279 | continue; 280 | } 281 | case 29: { 282 | this.screenToLensDistance_ = input.readFloat(); 283 | this.bitField0_ |= 0x4; 284 | continue; 285 | } 286 | case 37: { 287 | this.interLensDistance_ = input.readFloat(); 288 | this.bitField0_ |= 0x8; 289 | continue; 290 | } 291 | case 45: { 292 | arrayLength = WireFormatNano.getRepeatedFieldArrayLength(input, 45); 293 | i = ((this.leftEyeFieldOfViewAngles == null) ? 0 : this.leftEyeFieldOfViewAngles.length); 294 | newArray = new float[i + arrayLength]; 295 | if (i != 0) { 296 | System.arraycopy(this.leftEyeFieldOfViewAngles, 0, newArray, 0, i); 297 | } 298 | while (i < newArray.length - 1) { 299 | newArray[i] = input.readFloat(); 300 | input.readTag(); 301 | ++i; 302 | } 303 | newArray[i] = input.readFloat(); 304 | this.leftEyeFieldOfViewAngles = newArray; 305 | continue; 306 | } 307 | case 42: { 308 | length = input.readRawVarint32(); 309 | limit = input.pushLimit(length); 310 | arrayLength2 = length / 4; 311 | j = ((this.leftEyeFieldOfViewAngles == null) ? 0 : this.leftEyeFieldOfViewAngles.length); 312 | newArray2 = new float[j + arrayLength2]; 313 | if (j != 0) { 314 | System.arraycopy(this.leftEyeFieldOfViewAngles, 0, newArray2, 0, j); 315 | } 316 | while (j < newArray2.length) { 317 | newArray2[j] = input.readFloat(); 318 | ++j; 319 | } 320 | this.leftEyeFieldOfViewAngles = newArray2; 321 | input.popLimit(limit); 322 | continue; 323 | } 324 | case 53: { 325 | this.trayBottomToLensHeight_ = input.readFloat(); 326 | this.bitField0_ |= 0x10; 327 | continue; 328 | } 329 | case 61: { 330 | arrayLength = WireFormatNano.getRepeatedFieldArrayLength(input, 61); 331 | i = ((this.distortionCoefficients == null) ? 0 : this.distortionCoefficients.length); 332 | newArray = new float[i + arrayLength]; 333 | if (i != 0) { 334 | System.arraycopy(this.distortionCoefficients, 0, newArray, 0, i); 335 | } 336 | while (i < newArray.length - 1) { 337 | newArray[i] = input.readFloat(); 338 | input.readTag(); 339 | ++i; 340 | } 341 | newArray[i] = input.readFloat(); 342 | this.distortionCoefficients = newArray; 343 | continue; 344 | } 345 | case 58: { 346 | length = input.readRawVarint32(); 347 | limit = input.pushLimit(length); 348 | arrayLength2 = length / 4; 349 | j = ((this.distortionCoefficients == null) ? 0 : this.distortionCoefficients.length); 350 | newArray2 = new float[j + arrayLength2]; 351 | if (j != 0) { 352 | System.arraycopy(this.distortionCoefficients, 0, newArray2, 0, j); 353 | } 354 | while (j < newArray2.length) { 355 | newArray2[j] = input.readFloat(); 356 | ++j; 357 | } 358 | this.distortionCoefficients = newArray2; 359 | input.popLimit(limit); 360 | continue; 361 | } 362 | case 80: { 363 | this.hasMagnet_ = input.readBool(); 364 | this.bitField0_ |= 0x20; 365 | continue; 366 | } 367 | } 368 | } 369 | } 370 | 371 | public static DeviceParams parseFrom(byte[] data) throws InvalidProtocolBufferNanoException { 372 | return (DeviceParams)MessageNano.mergeFrom((MessageNano)new DeviceParams(), data); 373 | } 374 | 375 | public static DeviceParams parseFrom(CodedInputByteBufferNano input) throws IOException { 376 | return new DeviceParams().mergeFrom(input); 377 | } 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/proto/Phone.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.proto; 2 | 3 | import java.io.*; 4 | import com.google.protobuf.nano.*; 5 | 6 | public interface Phone { 7 | public static final class PhoneParams extends MessageNano { 8 | private static volatile PhoneParams[] _emptyArray; 9 | private int bitField0_; 10 | private float xPpi_; 11 | private float yPpi_; 12 | private float bottomBezelHeight_; 13 | public float[] gyroBias; 14 | 15 | public static PhoneParams[] emptyArray() { 16 | if (PhoneParams._emptyArray == null) { 17 | synchronized (InternalNano.LAZY_INIT_LOCK) { 18 | if (PhoneParams._emptyArray == null) { 19 | PhoneParams._emptyArray = new PhoneParams[0]; 20 | } 21 | } 22 | } 23 | return PhoneParams._emptyArray; 24 | } 25 | 26 | public float getXPpi() { 27 | return this.xPpi_; 28 | } 29 | 30 | public PhoneParams setXPpi(float value) { 31 | this.xPpi_ = value; 32 | this.bitField0_ |= 0x1; 33 | return this; 34 | } 35 | 36 | public boolean hasXPpi() { 37 | return (this.bitField0_ & 0x1) != 0x0; 38 | } 39 | 40 | public PhoneParams clearXPpi() { 41 | this.xPpi_ = 0.0f; 42 | this.bitField0_ &= 0xFFFFFFFE; 43 | return this; 44 | } 45 | 46 | public float getYPpi() { 47 | return this.yPpi_; 48 | } 49 | 50 | public PhoneParams setYPpi(float value) { 51 | this.yPpi_ = value; 52 | this.bitField0_ |= 0x2; 53 | return this; 54 | } 55 | 56 | public boolean hasYPpi() { 57 | return (this.bitField0_ & 0x2) != 0x0; 58 | } 59 | 60 | public PhoneParams clearYPpi() { 61 | this.yPpi_ = 0.0f; 62 | this.bitField0_ &= 0xFFFFFFFD; 63 | return this; 64 | } 65 | 66 | public float getBottomBezelHeight() { 67 | return this.bottomBezelHeight_; 68 | } 69 | 70 | public PhoneParams setBottomBezelHeight(float value) { 71 | this.bottomBezelHeight_ = value; 72 | this.bitField0_ |= 0x4; 73 | return this; 74 | } 75 | 76 | public boolean hasBottomBezelHeight() { 77 | return (this.bitField0_ & 0x4) != 0x0; 78 | } 79 | 80 | public PhoneParams clearBottomBezelHeight() { 81 | this.bottomBezelHeight_ = 0.0f; 82 | this.bitField0_ &= 0xFFFFFFFB; 83 | return this; 84 | } 85 | 86 | public PhoneParams() { 87 | super(); 88 | this.clear(); 89 | } 90 | 91 | public PhoneParams clear() { 92 | this.bitField0_ = 0; 93 | this.xPpi_ = 0.0f; 94 | this.yPpi_ = 0.0f; 95 | this.bottomBezelHeight_ = 0.0f; 96 | this.gyroBias = WireFormatNano.EMPTY_FLOAT_ARRAY; 97 | this.cachedSize = -1; 98 | return this; 99 | } 100 | 101 | public void writeTo(CodedOutputByteBufferNano output) throws IOException { 102 | int dataSize; 103 | if ((this.bitField0_ & 0x1) != 0x0) { 104 | output.writeFloat(1, this.xPpi_); 105 | } 106 | if ((this.bitField0_ & 0x2) != 0x0) { 107 | output.writeFloat(2, this.yPpi_); 108 | } 109 | if ((this.bitField0_ & 0x4) != 0x0) { 110 | output.writeFloat(3, this.bottomBezelHeight_); 111 | } 112 | if (this.gyroBias != null && this.gyroBias.length > 0) { 113 | dataSize = 4 * this.gyroBias.length; 114 | output.writeRawVarint32(34); 115 | output.writeRawVarint32(dataSize); 116 | for (int i = 0; i < this.gyroBias.length; ++i) { 117 | output.writeFloatNoTag(this.gyroBias[i]); 118 | } 119 | } 120 | super.writeTo(output); 121 | } 122 | 123 | protected int computeSerializedSize() { 124 | int size; 125 | int dataSize; 126 | size = super.computeSerializedSize(); 127 | if ((this.bitField0_ & 0x1) != 0x0) { 128 | size += CodedOutputByteBufferNano.computeFloatSize(1, this.xPpi_); 129 | } 130 | if ((this.bitField0_ & 0x2) != 0x0) { 131 | size += CodedOutputByteBufferNano.computeFloatSize(2, this.yPpi_); 132 | } 133 | if ((this.bitField0_ & 0x4) != 0x0) { 134 | size += CodedOutputByteBufferNano.computeFloatSize(3, this.bottomBezelHeight_); 135 | } 136 | if (this.gyroBias != null && this.gyroBias.length > 0) { 137 | dataSize = 4 * this.gyroBias.length; 138 | size += dataSize; 139 | size = ++size + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize); 140 | } 141 | return size; 142 | } 143 | 144 | public PhoneParams mergeFrom(CodedInputByteBufferNano input) throws IOException { 145 | int tag; 146 | int arrayLength; 147 | int i; 148 | float[] newArray; 149 | int length; 150 | int limit; 151 | int arrayLength2; 152 | int j; 153 | float[] newArray2; 154 | while (true) { 155 | tag = input.readTag(); 156 | switch (tag) { 157 | case 0: { 158 | return this; 159 | } 160 | default: { 161 | if (!WireFormatNano.parseUnknownField(input, tag)) { 162 | return this; 163 | } 164 | continue; 165 | } 166 | case 13: { 167 | this.xPpi_ = input.readFloat(); 168 | this.bitField0_ |= 0x1; 169 | continue; 170 | } 171 | case 21: { 172 | this.yPpi_ = input.readFloat(); 173 | this.bitField0_ |= 0x2; 174 | continue; 175 | } 176 | case 29: { 177 | this.bottomBezelHeight_ = input.readFloat(); 178 | this.bitField0_ |= 0x4; 179 | continue; 180 | } 181 | case 37: { 182 | arrayLength = WireFormatNano.getRepeatedFieldArrayLength(input, 37); 183 | i = ((this.gyroBias == null) ? 0 : this.gyroBias.length); 184 | newArray = new float[i + arrayLength]; 185 | if (i != 0) { 186 | System.arraycopy(this.gyroBias, 0, newArray, 0, i); 187 | } 188 | while (i < newArray.length - 1) { 189 | newArray[i] = input.readFloat(); 190 | input.readTag(); 191 | ++i; 192 | } 193 | newArray[i] = input.readFloat(); 194 | this.gyroBias = newArray; 195 | continue; 196 | } 197 | case 34: { 198 | length = input.readRawVarint32(); 199 | limit = input.pushLimit(length); 200 | arrayLength2 = length / 4; 201 | j = ((this.gyroBias == null) ? 0 : this.gyroBias.length); 202 | newArray2 = new float[j + arrayLength2]; 203 | if (j != 0) { 204 | System.arraycopy(this.gyroBias, 0, newArray2, 0, j); 205 | } 206 | while (j < newArray2.length) { 207 | newArray2[j] = input.readFloat(); 208 | ++j; 209 | } 210 | this.gyroBias = newArray2; 211 | input.popLimit(limit); 212 | continue; 213 | } 214 | } 215 | } 216 | } 217 | 218 | public static PhoneParams parseFrom(byte[] data) throws InvalidProtocolBufferNanoException { 219 | return (PhoneParams)MessageNano.mergeFrom((MessageNano)new PhoneParams(), data); 220 | } 221 | 222 | public static PhoneParams parseFrom(CodedInputByteBufferNano input) throws IOException { 223 | return new PhoneParams().mergeFrom(input); 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/Clock.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors; 2 | 3 | public interface Clock { 4 | long nanoTime(); 5 | } 6 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/DeviceSensorLooper.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors; 2 | 3 | import java.util.*; 4 | import android.hardware.*; 5 | import android.os.*; 6 | 7 | public class DeviceSensorLooper implements SensorEventProvider { 8 | private boolean mIsRunning; 9 | private SensorManager mSensorManager; 10 | private Looper mSensorLooper; 11 | private SensorEventListener mSensorEventListener; 12 | private final ArrayList mRegisteredListeners; 13 | private static final int[] INPUT_SENSORS; 14 | 15 | public DeviceSensorLooper(final SensorManager sensorManager) { 16 | super(); 17 | this.mRegisteredListeners = new ArrayList(); 18 | this.mSensorManager = sensorManager; 19 | } 20 | 21 | @Override 22 | public void start() { 23 | if (this.mIsRunning) { 24 | return; 25 | } 26 | this.mSensorEventListener = (SensorEventListener)new SensorEventListener() { 27 | public void onSensorChanged(final SensorEvent event) { 28 | for (final SensorEventListener listener : DeviceSensorLooper.this.mRegisteredListeners) { 29 | synchronized (listener) { 30 | listener.onSensorChanged(event); 31 | } 32 | } 33 | } 34 | 35 | public void onAccuracyChanged(final Sensor sensor, final int accuracy) { 36 | for (final SensorEventListener listener : DeviceSensorLooper.this.mRegisteredListeners) { 37 | synchronized (listener) { 38 | listener.onAccuracyChanged(sensor, accuracy); 39 | } 40 | } 41 | } 42 | }; 43 | final HandlerThread sensorThread = new HandlerThread("sensor") { 44 | protected void onLooperPrepared() { 45 | final Handler handler = new Handler(Looper.myLooper()); 46 | for (final int sensorType : DeviceSensorLooper.INPUT_SENSORS) { 47 | final Sensor sensor = DeviceSensorLooper.this.mSensorManager.getDefaultSensor(sensorType); 48 | DeviceSensorLooper.this.mSensorManager.registerListener(DeviceSensorLooper.this.mSensorEventListener, sensor, 0, handler); 49 | } 50 | } 51 | }; 52 | sensorThread.start(); 53 | this.mSensorLooper = sensorThread.getLooper(); 54 | this.mIsRunning = true; 55 | } 56 | 57 | @Override 58 | public void stop() { 59 | if (!this.mIsRunning) { 60 | return; 61 | } 62 | this.mSensorManager.unregisterListener(this.mSensorEventListener); 63 | this.mSensorEventListener = null; 64 | this.mSensorLooper.quit(); 65 | this.mSensorLooper = null; 66 | this.mIsRunning = false; 67 | } 68 | 69 | @Override 70 | public void registerListener(final SensorEventListener listener) { 71 | synchronized (this.mRegisteredListeners) { 72 | this.mRegisteredListeners.add(listener); 73 | } 74 | } 75 | 76 | @Override 77 | public void unregisterListener(final SensorEventListener listener) { 78 | synchronized (this.mRegisteredListeners) { 79 | this.mRegisteredListeners.remove(listener); 80 | } 81 | } 82 | 83 | static { 84 | INPUT_SENSORS = new int[] { 1, 4 }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/HeadTracker.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors; 2 | 3 | import com.google.vrtoolkit.cardboard.sensors.internal.*; 4 | import android.content.*; 5 | import android.view.*; 6 | import android.opengl.*; 7 | import android.hardware.*; 8 | import java.util.concurrent.*; 9 | 10 | public class HeadTracker implements SensorEventListener { 11 | private static final float DEFAULT_NECK_HORIZONTAL_OFFSET = 0.08f; 12 | private static final float DEFAULT_NECK_VERTICAL_OFFSET = 0.075f; 13 | private static final boolean DEFAULT_NECK_MODEL_ENABLED = false; 14 | private final Display mDisplay; 15 | private final float[] mEkfToHeadTracker; 16 | private final float[] mSensorToDisplay; 17 | private float mDisplayRotation; 18 | private final float[] mNeckModelTranslation; 19 | private final float[] mTmpHeadView; 20 | private final float[] mTmpHeadView2; 21 | private boolean mNeckModelEnabled; 22 | private volatile boolean mTracking; 23 | private OrientationEKF mTracker; 24 | private SensorEventProvider mSensorEventProvider; 25 | private Clock mClock; 26 | private long mLatestGyroEventClockTimeNs; 27 | private final Vector3d mGyroBias; 28 | private final Vector3d mLatestGyro; 29 | private final Vector3d mLatestAcc; 30 | 31 | public static HeadTracker createFromContext(final Context context) { 32 | final SensorManager sensorManager = (SensorManager)context.getSystemService("sensor"); 33 | final Display display = ((WindowManager)context.getSystemService("window")).getDefaultDisplay(); 34 | return new HeadTracker(new DeviceSensorLooper(sensorManager), new SystemClock(), display); 35 | } 36 | 37 | public HeadTracker(final SensorEventProvider sensorEventProvider, final Clock clock, final Display display) { 38 | super(); 39 | this.mEkfToHeadTracker = new float[16]; 40 | this.mSensorToDisplay = new float[16]; 41 | this.mDisplayRotation = Float.NaN; 42 | this.mNeckModelTranslation = new float[16]; 43 | this.mTmpHeadView = new float[16]; 44 | this.mTmpHeadView2 = new float[16]; 45 | this.mNeckModelEnabled = DEFAULT_NECK_MODEL_ENABLED; 46 | this.mGyroBias = new Vector3d(); 47 | this.mLatestGyro = new Vector3d(); 48 | this.mLatestAcc = new Vector3d(); 49 | this.mClock = clock; 50 | this.mSensorEventProvider = sensorEventProvider; 51 | this.mTracker = new OrientationEKF(); 52 | this.mDisplay = display; 53 | Matrix.setIdentityM(this.mNeckModelTranslation, 0); 54 | Matrix.translateM(this.mNeckModelTranslation, 0, 55 | 0.0f, -DEFAULT_NECK_VERTICAL_OFFSET, DEFAULT_NECK_HORIZONTAL_OFFSET); 56 | } 57 | 58 | public void onSensorChanged(final SensorEvent event) { 59 | if (event.sensor.getType() == 1) { 60 | this.mLatestAcc.set(event.values[0], event.values[1], event.values[2]); 61 | this.mTracker.processAcc(this.mLatestAcc, event.timestamp); 62 | } 63 | else if (event.sensor.getType() == 4) { 64 | this.mLatestGyroEventClockTimeNs = this.mClock.nanoTime(); 65 | this.mLatestGyro.set(event.values[0], event.values[1], event.values[2]); 66 | Vector3d.sub(this.mLatestGyro, this.mGyroBias, this.mLatestGyro); 67 | this.mTracker.processGyro(this.mLatestGyro, event.timestamp); 68 | } 69 | } 70 | 71 | public void onAccuracyChanged(final Sensor sensor, final int accuracy) { 72 | } 73 | 74 | public void startTracking() { 75 | if (this.mTracking) { 76 | return; 77 | } 78 | this.mTracker.reset(); 79 | this.mSensorEventProvider.registerListener((SensorEventListener)this); 80 | this.mSensorEventProvider.start(); 81 | this.mTracking = true; 82 | } 83 | 84 | public void stopTracking() { 85 | if (!this.mTracking) { 86 | return; 87 | } 88 | this.mSensorEventProvider.unregisterListener((SensorEventListener)this); 89 | this.mSensorEventProvider.stop(); 90 | this.mTracking = false; 91 | } 92 | 93 | public void setGyroBias(final float[] gyroBias) { 94 | if (gyroBias == null) { 95 | this.mGyroBias.setZero(); 96 | return; 97 | } 98 | if (gyroBias.length != 3) { 99 | throw new IllegalArgumentException("Gyro bias should be an array of 3 values"); 100 | } 101 | this.mGyroBias.set(gyroBias[0], gyroBias[1], gyroBias[2]); 102 | } 103 | 104 | public void setNeckModelEnabled(final boolean enabled) { 105 | this.mNeckModelEnabled = enabled; 106 | } 107 | 108 | public void getLastHeadView(final float[] headView, final int offset) { 109 | if (offset + 16 > headView.length) { 110 | throw new IllegalArgumentException("Not enough space to write the result"); 111 | } 112 | float rotation = 0.0f; 113 | switch (this.mDisplay.getRotation()) { 114 | case 0: { 115 | rotation = 0.0f; 116 | break; 117 | } 118 | case 1: { 119 | rotation = 90.0f; 120 | break; 121 | } 122 | case 2: { 123 | rotation = 180.0f; 124 | break; 125 | } 126 | case 3: { 127 | rotation = 270.0f; 128 | break; 129 | } 130 | } 131 | if (rotation != this.mDisplayRotation) { 132 | this.mDisplayRotation = rotation; 133 | Matrix.setRotateEulerM(this.mSensorToDisplay, 0, 0.0f, 0.0f, -rotation); 134 | Matrix.setRotateEulerM(this.mEkfToHeadTracker, 0, -90.0f, 0.0f, rotation); 135 | } 136 | synchronized (this.mTracker) { 137 | final double secondsSinceLastGyroEvent = TimeUnit.NANOSECONDS.toSeconds(this.mClock.nanoTime() - this.mLatestGyroEventClockTimeNs); 138 | final double secondsToPredictForward = secondsSinceLastGyroEvent + 1.0/30; 139 | final double[] mat = this.mTracker.getPredictedGLMatrix(secondsToPredictForward); 140 | for (int i = 0; i < headView.length; ++i) { 141 | this.mTmpHeadView[i] = (float)mat[i]; 142 | } 143 | } 144 | Matrix.multiplyMM(this.mTmpHeadView2, 0, this.mSensorToDisplay, 0, this.mTmpHeadView, 0); 145 | Matrix.multiplyMM(headView, offset, this.mTmpHeadView2, 0, this.mEkfToHeadTracker, 0); 146 | if (this.mNeckModelEnabled) { 147 | Matrix.multiplyMM(this.mTmpHeadView, 0, this.mNeckModelTranslation, 0, headView, offset); 148 | Matrix.translateM(headView, offset, this.mTmpHeadView, 0, 0.0f, DEFAULT_NECK_VERTICAL_OFFSET, 0.0f); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/MagnetSensor.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors; 2 | 3 | import android.content.Context; 4 | import android.hardware.Sensor; 5 | import android.hardware.SensorEvent; 6 | import android.hardware.SensorEventListener; 7 | import android.hardware.SensorManager; 8 | import android.os.Build; 9 | import android.os.Handler; 10 | import android.os.Looper; 11 | 12 | import java.lang.ref.WeakReference; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | 16 | public class MagnetSensor { 17 | private static final String HTC_ONE_MODEL = "HTC One"; 18 | private TriggerDetector mDetector; 19 | private Thread mDetectorThread; 20 | 21 | public MagnetSensor(final Context context) { 22 | super(); 23 | if (HTC_ONE_MODEL.equals(Build.MODEL)) { 24 | mDetector = new VectorTriggerDetector(context); 25 | } else { 26 | mDetector = new ThresholdTriggerDetector(context); 27 | } 28 | } 29 | 30 | public void start() { 31 | mDetectorThread = new Thread(mDetector); 32 | mDetectorThread.start(); 33 | } 34 | 35 | public void stop() { 36 | if (mDetectorThread != null) { 37 | mDetectorThread.interrupt(); 38 | mDetector.stop(); 39 | } 40 | } 41 | 42 | public void setOnCardboardTriggerListener(final OnCardboardTriggerListener listener) { 43 | mDetector.setOnCardboardTriggerListener(listener, new Handler()); 44 | } 45 | 46 | private abstract static class TriggerDetector implements Runnable, SensorEventListener { 47 | protected static final String TAG = "TriggerDetector"; 48 | protected SensorManager mSensorManager; 49 | protected Sensor mMagnetometer; 50 | protected WeakReference mListenerRef; 51 | protected Handler mHandler; 52 | 53 | public TriggerDetector(final Context context) { 54 | super(); 55 | mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); 56 | mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 57 | } 58 | 59 | public synchronized void setOnCardboardTriggerListener( 60 | final OnCardboardTriggerListener listener, final Handler handler) { 61 | mListenerRef = new WeakReference<>(listener); 62 | mHandler = handler; 63 | } 64 | 65 | protected void handleButtonPressed() { 66 | synchronized (this) { 67 | final OnCardboardTriggerListener listener = mListenerRef != null ? 68 | mListenerRef.get() : null; 69 | if (listener != null) { 70 | mHandler.post(new Runnable() { 71 | @Override 72 | public void run() { 73 | listener.onCardboardTrigger(); 74 | } 75 | }); 76 | } 77 | } 78 | } 79 | 80 | @Override 81 | public void run() { 82 | Looper.prepare(); 83 | mSensorManager.registerListener(this, this.mMagnetometer, 0); 84 | Looper.loop(); 85 | } 86 | 87 | public void stop() { 88 | mSensorManager.unregisterListener(this); 89 | } 90 | 91 | public void onSensorChanged(final SensorEvent event) { 92 | } 93 | 94 | public void onAccuracyChanged(final Sensor sensor, final int accuracy) { 95 | } 96 | } 97 | 98 | private static class ThresholdTriggerDetector extends TriggerDetector { 99 | private static final String TAG = "ThresholdTriggerDetector"; 100 | private static final long NS_SEGMENT_SIZE = 200000000L; 101 | private static final long NS_WINDOW_SIZE = 400000000L; 102 | private static final long NS_WAIT_TIME = 350000000L; 103 | private long mLastFiring; 104 | private static int mT1; 105 | private static int mT2; 106 | private ArrayList mSensorData; 107 | private ArrayList mSensorTimes; 108 | 109 | public ThresholdTriggerDetector(final Context context) { 110 | super(context); 111 | mLastFiring = 0L; 112 | mSensorData = new ArrayList<>(); 113 | mSensorTimes = new ArrayList<>(); 114 | } 115 | 116 | public ThresholdTriggerDetector(final Context context, final int t1, final int t2) { 117 | super(context); 118 | mLastFiring = 0L; 119 | mSensorData = new ArrayList<>(); 120 | mSensorTimes = new ArrayList<>(); 121 | ThresholdTriggerDetector.mT1 = t1; 122 | ThresholdTriggerDetector.mT2 = t2; 123 | } 124 | 125 | private void addData(final float[] values, final long time) { 126 | mSensorData.add(values); 127 | mSensorTimes.add(time); 128 | while (mSensorTimes.get(0) < time - NS_WINDOW_SIZE) { 129 | mSensorData.remove(0); 130 | mSensorTimes.remove(0); 131 | } 132 | evaluateModel(time); 133 | } 134 | 135 | private void evaluateModel(final long time) { 136 | if (time - mLastFiring < NS_WAIT_TIME || mSensorData.size() < 2) { 137 | return; 138 | } 139 | final float[] baseline = mSensorData.get(mSensorData.size() - 1); 140 | int startSecondSegment = 0; 141 | for (int i = 0; i < this.mSensorTimes.size(); ++i) { 142 | if (time - mSensorTimes.get(i) < NS_SEGMENT_SIZE) { 143 | startSecondSegment = i; 144 | break; 145 | } 146 | } 147 | final float[] offsets = new float[mSensorData.size()]; 148 | computeOffsets(offsets, baseline); 149 | final float min1 = computeMinimum(Arrays.copyOfRange(offsets, 0, startSecondSegment)); 150 | final float max2 = computeMaximum(Arrays.copyOfRange( 151 | offsets, startSecondSegment, mSensorData.size())); 152 | if (min1 < ThresholdTriggerDetector.mT1 && max2 > ThresholdTriggerDetector.mT2) { 153 | mLastFiring = time; 154 | handleButtonPressed(); 155 | } 156 | } 157 | 158 | private void computeOffsets(final float[] offsets, final float[] baseline) { 159 | for (int i = 0; i < mSensorData.size(); ++i) { 160 | final float[] point = mSensorData.get(i); 161 | final float[] o = { 162 | point[0] - baseline[0], 163 | point[1] - baseline[1], 164 | point[2] - baseline[2] 165 | }; 166 | final float magnitude = (float) Math.sqrt(o[0] * o[0] + o[1] * o[1] + o[2] * o[2]); 167 | offsets[i] = magnitude; 168 | } 169 | } 170 | 171 | private float computeMaximum(final float[] offsets) { 172 | float max = Float.NEGATIVE_INFINITY; 173 | for (final float o : offsets) { 174 | max = Math.max(o, max); 175 | } 176 | return max; 177 | } 178 | 179 | private float computeMinimum(final float[] offsets) { 180 | float min = Float.POSITIVE_INFINITY; 181 | for (final float o : offsets) { 182 | min = Math.min(o, min); 183 | } 184 | return min; 185 | } 186 | 187 | @Override 188 | public void onSensorChanged(final SensorEvent event) { 189 | if (event.sensor.equals(this.mMagnetometer)) { 190 | final float[] values = event.values; 191 | if (values[0] == 0.0f && values[1] == 0.0f && values[2] == 0.0f) { 192 | return; 193 | } 194 | addData(event.values.clone(), event.timestamp); 195 | } 196 | } 197 | 198 | @Override 199 | public void onAccuracyChanged(final Sensor sensor, final int accuracy) { 200 | } 201 | 202 | static { 203 | ThresholdTriggerDetector.mT1 = 30; 204 | ThresholdTriggerDetector.mT2 = 130; 205 | } 206 | } 207 | 208 | private static class VectorTriggerDetector extends TriggerDetector { 209 | private static final String TAG = "ThresholdTriggerDetector"; 210 | private static final long NS_REFRESH_TIME = 350000000L; 211 | private static final long NS_THROWAWAY_SIZE = 500000000L; 212 | private static final long NS_WAIT_SIZE = 100000000L; 213 | private long mLastFiring; 214 | private static int mXThreshold; 215 | private static int mYThreshold; 216 | private static int mZThreshold; 217 | private ArrayList mSensorData; 218 | private ArrayList mSensorTimes; 219 | 220 | public VectorTriggerDetector(final Context context) { 221 | super(context); 222 | mLastFiring = 0L; 223 | mSensorData = new ArrayList<>(); 224 | mSensorTimes = new ArrayList<>(); 225 | VectorTriggerDetector.mXThreshold = -3; 226 | VectorTriggerDetector.mYThreshold = 15; 227 | VectorTriggerDetector.mZThreshold = 6; 228 | } 229 | 230 | public VectorTriggerDetector(final Context context, final int xThreshold, 231 | final int yThreshold, final int zThreshold) { 232 | super(context); 233 | mLastFiring = 0L; 234 | mSensorData = new ArrayList<>(); 235 | mSensorTimes = new ArrayList<>(); 236 | VectorTriggerDetector.mXThreshold = xThreshold; 237 | VectorTriggerDetector.mYThreshold = yThreshold; 238 | VectorTriggerDetector.mZThreshold = zThreshold; 239 | } 240 | 241 | private void addData(final float[] values, final long time) { 242 | mSensorData.add(values); 243 | mSensorTimes.add(time); 244 | while (mSensorTimes.get(0) < time - NS_THROWAWAY_SIZE) { 245 | mSensorData.remove(0); 246 | mSensorTimes.remove(0); 247 | } 248 | evaluateModel(time); 249 | } 250 | 251 | private void evaluateModel(final long time) { 252 | if (time - mLastFiring < NS_REFRESH_TIME || mSensorData.size() < 2) { 253 | return; 254 | } 255 | int baseIndex = 0; 256 | for (int i = 1; i < mSensorTimes.size(); ++i) { 257 | if (time - mSensorTimes.get(i) < NS_WAIT_SIZE) { 258 | baseIndex = i; 259 | break; 260 | } 261 | } 262 | final float[] oldValues = mSensorData.get(baseIndex); 263 | final float[] currentValues = mSensorData.get(mSensorData.size() - 1); 264 | if (currentValues[0] - oldValues[0] < VectorTriggerDetector.mXThreshold 265 | && currentValues[1] - oldValues[1] > VectorTriggerDetector.mYThreshold 266 | && currentValues[2] - oldValues[2] > VectorTriggerDetector.mZThreshold) { 267 | mLastFiring = time; 268 | handleButtonPressed(); 269 | } 270 | } 271 | 272 | @Override 273 | public void onSensorChanged(final SensorEvent event) { 274 | if (event.sensor.equals(mMagnetometer)) { 275 | final float[] values = event.values; 276 | if (values[0] == 0.0f && values[1] == 0.0f && values[2] == 0.0f) { 277 | return; 278 | } 279 | addData(event.values.clone(), event.timestamp); 280 | } 281 | } 282 | 283 | @Override 284 | public void onAccuracyChanged(final Sensor sensor, final int accuracy) { 285 | } 286 | } 287 | 288 | public interface OnCardboardTriggerListener { 289 | void onCardboardTrigger(); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/NfcSensor.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors; 2 | 3 | import android.content.*; 4 | import android.os.*; 5 | import java.io.*; 6 | import android.net.*; 7 | import android.nfc.*; 8 | import android.nfc.tech.*; 9 | import android.util.*; 10 | import android.app.*; 11 | import com.google.vrtoolkit.cardboard.*; 12 | import java.util.*; 13 | 14 | public class NfcSensor { 15 | private static final String TAG = "NfcSensor"; 16 | private static final int MAX_CONNECTION_FAILURES = 1; 17 | private static final long NFC_POLLING_INTERVAL_MS = 250L; 18 | private static NfcSensor sInstance; 19 | private final Context mContext; 20 | private final NfcAdapter mNfcAdapter; 21 | private final Object mTagLock; 22 | private final List mListeners; 23 | private IntentFilter[] mNfcIntentFilters; 24 | private Ndef mCurrentNdef; 25 | private Tag mCurrentTag; 26 | private boolean mCurrentTagIsCardboard; 27 | private Timer mNfcDisconnectTimer; 28 | private int mTagConnectionFailures; 29 | 30 | public static NfcSensor getInstance(final Context context) { 31 | if (NfcSensor.sInstance == null) { 32 | NfcSensor.sInstance = new NfcSensor(context); 33 | } 34 | return NfcSensor.sInstance; 35 | } 36 | 37 | private NfcSensor(final Context context) { 38 | super(); 39 | this.mContext = context.getApplicationContext(); 40 | this.mNfcAdapter = NfcAdapter.getDefaultAdapter(this.mContext); 41 | this.mListeners = new ArrayList(); 42 | this.mTagLock = new Object(); 43 | if (this.mNfcAdapter == null) { 44 | return; 45 | } 46 | final IntentFilter ndefIntentFilter = new IntentFilter("android.nfc.action.NDEF_DISCOVERED"); 47 | ndefIntentFilter.addAction("android.nfc.action.TECH_DISCOVERED"); 48 | ndefIntentFilter.addAction("android.nfc.action.TAG_DISCOVERED"); 49 | this.mNfcIntentFilters = new IntentFilter[] { ndefIntentFilter }; 50 | this.mContext.registerReceiver((BroadcastReceiver)new BroadcastReceiver() { 51 | public void onReceive(final Context context, final Intent intent) { 52 | NfcSensor.this.onNfcIntent(intent); 53 | } 54 | }, ndefIntentFilter); 55 | } 56 | 57 | public void addOnCardboardNfcListener(final OnCardboardNfcListener listener) { 58 | if (listener == null) { 59 | return; 60 | } 61 | synchronized (this.mListeners) { 62 | for (final ListenerHelper helper : this.mListeners) { 63 | if (helper.getListener() == listener) { 64 | return; 65 | } 66 | } 67 | this.mListeners.add(new ListenerHelper(listener, new Handler())); 68 | } 69 | } 70 | 71 | public void removeOnCardboardNfcListener(final OnCardboardNfcListener listener) { 72 | if (listener == null) { 73 | return; 74 | } 75 | synchronized (this.mListeners) { 76 | for (final ListenerHelper helper : this.mListeners) { 77 | if (helper.getListener() == listener) { 78 | this.mListeners.remove(helper); 79 | } 80 | } 81 | } 82 | } 83 | 84 | public boolean isNfcSupported() { 85 | return this.mNfcAdapter != null; 86 | } 87 | 88 | public boolean isNfcEnabled() { 89 | return this.isNfcSupported() && this.mNfcAdapter.isEnabled(); 90 | } 91 | 92 | public boolean isDeviceInCardboard() { 93 | synchronized (this.mTagLock) { 94 | return this.mCurrentTagIsCardboard; 95 | } 96 | } 97 | 98 | public NdefMessage getTagContents() { 99 | synchronized (this.mTagLock) { 100 | return (this.mCurrentNdef != null) ? this.mCurrentNdef.getCachedNdefMessage() : null; 101 | } 102 | } 103 | 104 | public NdefMessage getCurrentTagContents() throws TagLostException, IOException, FormatException { 105 | synchronized (this.mTagLock) { 106 | return (this.mCurrentNdef != null) ? this.mCurrentNdef.getNdefMessage() : null; 107 | } 108 | } 109 | 110 | public int getTagCapacity() { 111 | synchronized (this.mTagLock) { 112 | if (this.mCurrentNdef == null) { 113 | throw new IllegalStateException("No NFC tag"); 114 | } 115 | return this.mCurrentNdef.getMaxSize(); 116 | } 117 | } 118 | 119 | public void writeUri(final Uri uri) throws TagLostException, IOException, IllegalArgumentException { 120 | synchronized (this.mTagLock) { 121 | if (this.mCurrentTag == null) { 122 | throw new IllegalStateException("No NFC tag found"); 123 | } 124 | NdefMessage currentMessage = null; 125 | NdefMessage newMessage = null; 126 | final NdefRecord newRecord = NdefRecord.createUri(uri); 127 | try { 128 | currentMessage = this.getCurrentTagContents(); 129 | } 130 | catch (Exception e3) { 131 | currentMessage = this.getTagContents(); 132 | } 133 | if (currentMessage != null) { 134 | final ArrayList newRecords = new ArrayList(); 135 | boolean recordFound = false; 136 | for (final NdefRecord record : currentMessage.getRecords()) { 137 | if (this.isCardboardNdefRecord(record)) { 138 | if (!recordFound) { 139 | newRecords.add(newRecord); 140 | recordFound = true; 141 | } 142 | } 143 | else { 144 | newRecords.add(record); 145 | } 146 | } 147 | newMessage = new NdefMessage((NdefRecord[])newRecords.toArray(new NdefRecord[newRecords.size()])); 148 | } 149 | if (newMessage == null) { 150 | newMessage = new NdefMessage(new NdefRecord[] { newRecord }); 151 | } 152 | Label_0432: { 153 | if (this.mCurrentNdef != null) { 154 | if (!this.mCurrentNdef.isConnected()) { 155 | this.mCurrentNdef.connect(); 156 | } 157 | if (this.mCurrentNdef.getMaxSize() < newMessage.getByteArrayLength()) { 158 | throw new IllegalArgumentException(new StringBuilder(82).append("Not enough capacity in NFC tag. Capacity: ").append(this.mCurrentNdef.getMaxSize()).append(" bytes, ").append(newMessage.getByteArrayLength()).append(" required.").toString()); 159 | } 160 | try { 161 | this.mCurrentNdef.writeNdefMessage(newMessage); 162 | break Label_0432; 163 | } 164 | catch (FormatException e) { 165 | final String s = "Internal error when writing to NFC tag: "; 166 | final String value = String.valueOf(e.toString()); 167 | throw new RuntimeException((value.length() != 0) ? s.concat(value) : new String(s)); 168 | } 169 | } 170 | final NdefFormatable ndef = NdefFormatable.get(this.mCurrentTag); 171 | if (ndef == null) { 172 | throw new IOException("Could not find a writable technology for the NFC tag"); 173 | } 174 | Log.w("NfcSensor", "Ndef technology not available. Falling back to NdefFormattable."); 175 | try { 176 | ndef.connect(); 177 | ndef.format(newMessage); 178 | ndef.close(); 179 | } 180 | catch (FormatException e2) { 181 | final String s2 = "Internal error when writing to NFC tag: "; 182 | final String value2 = String.valueOf(e2.toString()); 183 | throw new RuntimeException((value2.length() != 0) ? s2.concat(value2) : new String(s2)); 184 | } 185 | } 186 | this.onNewNfcTag(this.mCurrentTag); 187 | } 188 | } 189 | 190 | public void onResume(final Activity activity) { 191 | if (!this.isNfcEnabled()) { 192 | return; 193 | } 194 | final Intent intent = new Intent("android.nfc.action.NDEF_DISCOVERED"); 195 | intent.setPackage(activity.getPackageName()); 196 | final PendingIntent pendingIntent = PendingIntent.getBroadcast(this.mContext, 0, intent, 0); 197 | this.mNfcAdapter.enableForegroundDispatch(activity, pendingIntent, this.mNfcIntentFilters, (String[][])null); 198 | } 199 | 200 | public void onPause(final Activity activity) { 201 | if (!this.isNfcEnabled()) { 202 | return; 203 | } 204 | this.mNfcAdapter.disableForegroundDispatch(activity); 205 | } 206 | 207 | public void onNfcIntent(final Intent intent) { 208 | if (!this.isNfcEnabled() || intent == null || !this.mNfcIntentFilters[0].matchAction(intent.getAction())) { 209 | return; 210 | } 211 | this.onNewNfcTag((Tag)intent.getParcelableExtra("android.nfc.extra.TAG")); 212 | } 213 | 214 | private void onNewNfcTag(final Tag nfcTag) { 215 | if (nfcTag == null) { 216 | return; 217 | } 218 | synchronized (this.mTagLock) { 219 | final Tag previousTag = this.mCurrentTag; 220 | final Ndef previousNdef = this.mCurrentNdef; 221 | final boolean previousTagWasCardboard = this.mCurrentTagIsCardboard; 222 | this.closeCurrentNfcTag(); 223 | this.mCurrentTag = nfcTag; 224 | this.mCurrentNdef = Ndef.get(nfcTag); 225 | if (this.mCurrentNdef == null) { 226 | if (previousTagWasCardboard) { 227 | this.sendDisconnectionEvent(); 228 | } 229 | return; 230 | } 231 | boolean isSameTag = false; 232 | if (previousNdef != null) { 233 | final byte[] tagId1 = this.mCurrentTag.getId(); 234 | final byte[] tagId2 = previousTag.getId(); 235 | isSameTag = (tagId1 != null && tagId2 != null && Arrays.equals(tagId1, tagId2)); 236 | if (!isSameTag && previousTagWasCardboard) { 237 | this.sendDisconnectionEvent(); 238 | } 239 | } 240 | NdefMessage nfcTagContents; 241 | try { 242 | this.mCurrentNdef.connect(); 243 | nfcTagContents = this.mCurrentNdef.getCachedNdefMessage(); 244 | } 245 | catch (Exception e) { 246 | final String s = "NfcSensor"; 247 | final String s2 = "Error reading NFC tag: "; 248 | final String value = String.valueOf(e.toString()); 249 | Log.e(s, (value.length() != 0) ? s2.concat(value) : new String(s2)); 250 | if (isSameTag && previousTagWasCardboard) { 251 | this.sendDisconnectionEvent(); 252 | } 253 | return; 254 | } 255 | this.mCurrentTagIsCardboard = this.isCardboardNdefMessage(nfcTagContents); 256 | if (!isSameTag && this.mCurrentTagIsCardboard) { 257 | synchronized (this.mListeners) { 258 | for (final ListenerHelper listener : this.mListeners) { 259 | listener.onInsertedIntoCardboard(CardboardDeviceParams.createFromNfcContents(nfcTagContents)); 260 | } 261 | } 262 | } 263 | if (this.mCurrentTagIsCardboard) { 264 | this.mTagConnectionFailures = 0; 265 | (this.mNfcDisconnectTimer = new Timer("NFC disconnect timer")).schedule(new TimerTask() { 266 | @Override 267 | public void run() { 268 | synchronized (NfcSensor.this.mTagLock) { 269 | if (!NfcSensor.this.mCurrentNdef.isConnected()) { 270 | ++NfcSensor.this.mTagConnectionFailures; 271 | if (NfcSensor.this.mTagConnectionFailures > 1) { 272 | NfcSensor.this.closeCurrentNfcTag(); 273 | NfcSensor.this.sendDisconnectionEvent(); 274 | } 275 | } 276 | } 277 | } 278 | }, 250L, 250L); 279 | } 280 | } 281 | } 282 | 283 | private void closeCurrentNfcTag() { 284 | if (this.mNfcDisconnectTimer != null) { 285 | this.mNfcDisconnectTimer.cancel(); 286 | } 287 | if (this.mCurrentNdef == null) { 288 | return; 289 | } 290 | try { 291 | this.mCurrentNdef.close(); 292 | } 293 | catch (IOException e) { 294 | Log.w("NfcSensor", e.toString()); 295 | } 296 | this.mCurrentTag = null; 297 | this.mCurrentNdef = null; 298 | this.mCurrentTagIsCardboard = false; 299 | } 300 | 301 | private void sendDisconnectionEvent() { 302 | synchronized (this.mListeners) { 303 | for (final ListenerHelper listener : this.mListeners) { 304 | listener.onRemovedFromCardboard(); 305 | } 306 | } 307 | } 308 | 309 | private boolean isCardboardNdefMessage(final NdefMessage message) { 310 | if (message == null) { 311 | return false; 312 | } 313 | for (final NdefRecord record : message.getRecords()) { 314 | if (this.isCardboardNdefRecord(record)) { 315 | return true; 316 | } 317 | } 318 | return false; 319 | } 320 | 321 | private boolean isCardboardNdefRecord(final NdefRecord record) { 322 | if (record == null) { 323 | return false; 324 | } 325 | final Uri uri = record.toUri(); 326 | return uri != null && CardboardDeviceParams.isCardboardUri(uri); 327 | } 328 | 329 | private static class ListenerHelper implements OnCardboardNfcListener 330 | { 331 | private OnCardboardNfcListener mListener; 332 | private Handler mHandler; 333 | 334 | public ListenerHelper(final OnCardboardNfcListener listener, final Handler handler) { 335 | super(); 336 | this.mListener = listener; 337 | this.mHandler = handler; 338 | } 339 | 340 | public OnCardboardNfcListener getListener() { 341 | return this.mListener; 342 | } 343 | 344 | @Override 345 | public void onInsertedIntoCardboard(final CardboardDeviceParams deviceParams) { 346 | this.mHandler.post((Runnable)new Runnable() { 347 | @Override 348 | public void run() { 349 | ListenerHelper.this.mListener.onInsertedIntoCardboard(deviceParams); 350 | } 351 | }); 352 | } 353 | 354 | @Override 355 | public void onRemovedFromCardboard() { 356 | this.mHandler.post((Runnable)new Runnable() { 357 | @Override 358 | public void run() { 359 | ListenerHelper.this.mListener.onRemovedFromCardboard(); 360 | } 361 | }); 362 | } 363 | } 364 | 365 | public interface OnCardboardNfcListener 366 | { 367 | void onInsertedIntoCardboard(CardboardDeviceParams p0); 368 | 369 | void onRemovedFromCardboard(); 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/SensorEventProvider.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors; 2 | 3 | import android.hardware.*; 4 | 5 | public interface SensorEventProvider { 6 | void start(); 7 | 8 | void stop(); 9 | 10 | void registerListener(SensorEventListener p0); 11 | 12 | void unregisterListener(SensorEventListener p0); 13 | } 14 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/SystemClock.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors; 2 | 3 | public class SystemClock implements Clock { 4 | @Override 5 | public long nanoTime() { 6 | return System.nanoTime(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/GyroBiasEstimator.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors.internal; 2 | 3 | public class GyroBiasEstimator { 4 | private static final float GYRO_SMOOTHING_FACTOR = 0.01f; 5 | private static final float ACC_SMOOTHING_FACTOR = 0.1f; 6 | private static final Vector3d UP_VECTOR; 7 | private static final float MIN_ACCEL_DOT_WITH_UP; 8 | private static final float MIN_ACCEL_LENGTH = 1.0E-4f; 9 | private static final float MAX_GYRO_DIFF = 0.01f; 10 | private static final long CALIBRATION_DURATION_NS = 5000000000L; 11 | private static final long MAX_DELAY_BETWEEN_EVENTS_NS = 100000000L; 12 | private final Vector3d mLastGyro; 13 | private final Vector3d mCurrGyro; 14 | private final Vector3d mGyroDiff; 15 | private final Vector3d mCurrAcc; 16 | private final Vector3d mAccSmoothed; 17 | private final Vector3d mAccNormalizedTmp; 18 | private float mGyroMagnitudeDiffSmoothed; 19 | private final Estimate mBiasEstimate; 20 | private long mCalibrationStartTimeNs; 21 | private long mLastGyroTimeNs; 22 | private long mLastAccTimeNs; 23 | 24 | public GyroBiasEstimator() { 25 | super(); 26 | this.mLastGyro = new Vector3d(); 27 | this.mCurrGyro = new Vector3d(); 28 | this.mGyroDiff = new Vector3d(); 29 | this.mCurrAcc = new Vector3d(); 30 | this.mAccSmoothed = new Vector3d(); 31 | this.mAccNormalizedTmp = new Vector3d(); 32 | this.mBiasEstimate = new Estimate(); 33 | this.mCalibrationStartTimeNs = -1L; 34 | this.mLastGyroTimeNs = -1L; 35 | this.mLastAccTimeNs = -1L; 36 | } 37 | 38 | public void processGyroscope(final Vector3d gyro, final long sensorTimeStamp) { 39 | if (this.mBiasEstimate.mState == Estimate.State.CALIBRATED) { 40 | return; 41 | } 42 | this.mCurrGyro.set(gyro); 43 | Vector3d.sub(this.mCurrGyro, this.mLastGyro, this.mGyroDiff); 44 | final float mCurrDiff = (float)this.mGyroDiff.length(); 45 | this.mGyroMagnitudeDiffSmoothed = MAX_GYRO_DIFF * mCurrDiff + (1-MAX_GYRO_DIFF) * this.mGyroMagnitudeDiffSmoothed; 46 | this.mLastGyro.set(this.mCurrGyro); 47 | final boolean eventIsDelayed = sensorTimeStamp > this.mLastGyroTimeNs + MAX_DELAY_BETWEEN_EVENTS_NS; 48 | this.mLastGyroTimeNs = sensorTimeStamp; 49 | if (eventIsDelayed) { 50 | this.resetCalibration(); 51 | return; 52 | } 53 | if (this.mBiasEstimate.mState == Estimate.State.CALIBRATING && sensorTimeStamp > this.mCalibrationStartTimeNs + CALIBRATION_DURATION_NS) { 54 | this.mBiasEstimate.mState = Estimate.State.CALIBRATED; 55 | return; 56 | } 57 | if (!this.canCalibrateGyro()) { 58 | this.resetCalibration(); 59 | return; 60 | } 61 | this.startCalibration(sensorTimeStamp); 62 | } 63 | 64 | private void resetCalibration() { 65 | this.mBiasEstimate.mState = Estimate.State.UNCALIBRATED; 66 | this.mBiasEstimate.mBias.set(0.0, 0.0, 0.0); 67 | this.mCalibrationStartTimeNs = -1L; 68 | } 69 | 70 | private void startCalibration(final long gyroTimeStamp) { 71 | if (this.mBiasEstimate.mState != Estimate.State.CALIBRATING) { 72 | this.mBiasEstimate.mBias.set(this.mCurrGyro); 73 | this.mBiasEstimate.mState = Estimate.State.CALIBRATING; 74 | this.mCalibrationStartTimeNs = gyroTimeStamp; 75 | } 76 | else { 77 | smooth(this.mBiasEstimate.mBias, this.mCurrGyro, GYRO_SMOOTHING_FACTOR); 78 | } 79 | } 80 | 81 | public void processAccelerometer(final Vector3d acc, final long sensorTimeStamp) { 82 | if (this.mBiasEstimate.mState == Estimate.State.CALIBRATED) { 83 | return; 84 | } 85 | this.mCurrAcc.set(acc); 86 | final boolean eventIsDelayed = sensorTimeStamp > this.mLastAccTimeNs + MAX_DELAY_BETWEEN_EVENTS_NS; 87 | this.mLastAccTimeNs = sensorTimeStamp; 88 | if (eventIsDelayed) { 89 | this.resetCalibration(); 90 | return; 91 | } 92 | smooth(this.mAccSmoothed, this.mCurrAcc, ACC_SMOOTHING_FACTOR); 93 | } 94 | 95 | public void getEstimate(final Estimate output) { 96 | output.set(this.mBiasEstimate); 97 | } 98 | 99 | private boolean canCalibrateGyro() { 100 | if (this.mAccSmoothed.length() < MIN_ACCEL_LENGTH) { 101 | return false; 102 | } 103 | this.mAccNormalizedTmp.set(this.mAccSmoothed); 104 | this.mAccNormalizedTmp.normalize(); 105 | return Vector3d.dot(this.mAccNormalizedTmp, GyroBiasEstimator.UP_VECTOR) >= GyroBiasEstimator.MIN_ACCEL_DOT_WITH_UP && this.mGyroMagnitudeDiffSmoothed <= 0.01f; 106 | } 107 | 108 | private static void smooth(final Vector3d smoothed, final Vector3d newValue, final float smoothingFactor) { 109 | smoothed.x = smoothingFactor * newValue.x + (1.0f - smoothingFactor) * smoothed.x; 110 | smoothed.y = smoothingFactor * newValue.y + (1.0f - smoothingFactor) * smoothed.y; 111 | smoothed.z = smoothingFactor * newValue.z + (1.0f - smoothingFactor) * smoothed.z; 112 | } 113 | 114 | static { 115 | UP_VECTOR = new Vector3d(0.0, 0.0, 1.0); 116 | MIN_ACCEL_DOT_WITH_UP = (float)Math.cos(Math.toRadians(10.0)); 117 | } 118 | 119 | public static class Estimate 120 | { 121 | public State mState; 122 | public final Vector3d mBias; 123 | 124 | public Estimate() { 125 | super(); 126 | this.mState = State.UNCALIBRATED; 127 | this.mBias = new Vector3d(); 128 | } 129 | 130 | public void set(final Estimate from) { 131 | this.mState = from.mState; 132 | this.mBias.set(from.mBias); 133 | } 134 | 135 | public enum State 136 | { 137 | UNCALIBRATED, 138 | CALIBRATING, 139 | CALIBRATED; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/Matrix3x3d.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors.internal; 2 | 3 | public class Matrix3x3d { 4 | public double[] m; 5 | 6 | public Matrix3x3d() { 7 | super(); 8 | this.m = new double[9]; 9 | } 10 | 11 | public Matrix3x3d(final double m00, final double m01, final double m02, final double m10, final double m11, final double m12, final double m20, final double m21, final double m22) { 12 | super(); 13 | (this.m = new double[9])[0] = m00; 14 | this.m[1] = m01; 15 | this.m[2] = m02; 16 | this.m[3] = m10; 17 | this.m[4] = m11; 18 | this.m[5] = m12; 19 | this.m[6] = m20; 20 | this.m[7] = m21; 21 | this.m[8] = m22; 22 | } 23 | 24 | public Matrix3x3d(final Matrix3x3d o) { 25 | super(); 26 | (this.m = new double[9])[0] = o.m[0]; 27 | this.m[1] = o.m[1]; 28 | this.m[2] = o.m[2]; 29 | this.m[3] = o.m[3]; 30 | this.m[4] = o.m[4]; 31 | this.m[5] = o.m[5]; 32 | this.m[6] = o.m[6]; 33 | this.m[7] = o.m[7]; 34 | this.m[8] = o.m[8]; 35 | } 36 | 37 | public void set(final double m00, final double m01, final double m02, final double m10, final double m11, final double m12, final double m20, final double m21, final double m22) { 38 | this.m[0] = m00; 39 | this.m[1] = m01; 40 | this.m[2] = m02; 41 | this.m[3] = m10; 42 | this.m[4] = m11; 43 | this.m[5] = m12; 44 | this.m[6] = m20; 45 | this.m[7] = m21; 46 | this.m[8] = m22; 47 | } 48 | 49 | public void set(final Matrix3x3d o) { 50 | this.m[0] = o.m[0]; 51 | this.m[1] = o.m[1]; 52 | this.m[2] = o.m[2]; 53 | this.m[3] = o.m[3]; 54 | this.m[4] = o.m[4]; 55 | this.m[5] = o.m[5]; 56 | this.m[6] = o.m[6]; 57 | this.m[7] = o.m[7]; 58 | this.m[8] = o.m[8]; 59 | } 60 | 61 | public void setZero() { 62 | this.m[0] = 0; 63 | this.m[1] = 0; 64 | this.m[2] = 0; 65 | this.m[3] = 0; 66 | this.m[4] = 0; 67 | this.m[5] = 0; 68 | this.m[6] = 0; 69 | this.m[7] = 0; 70 | this.m[8] = 0; 71 | } 72 | 73 | public void setIdentity() { 74 | this.m[0] = 1; 75 | this.m[1] = 0; 76 | this.m[2] = 0; 77 | this.m[3] = 0; 78 | this.m[4] = 1; 79 | this.m[5] = 0; 80 | this.m[6] = 0; 81 | this.m[7] = 0; 82 | this.m[8] = 1; 83 | } 84 | 85 | public void setSameDiagonal(final double d) { 86 | this.m[0] = d; 87 | this.m[4] = d; 88 | this.m[8] = d; 89 | } 90 | 91 | public double get(final int row, final int col) { 92 | return this.m[3 * row + col]; 93 | } 94 | 95 | public void set(final int row, final int col, final double value) { 96 | this.m[3 * row + col] = value; 97 | } 98 | 99 | public void getColumn(final int col, final Vector3d v) { 100 | v.x = this.m[col]; 101 | v.y = this.m[col + 3]; 102 | v.z = this.m[col + 6]; 103 | } 104 | 105 | public void setColumn(final int col, final Vector3d v) { 106 | this.m[col] = v.x; 107 | this.m[col + 3] = v.y; 108 | this.m[col + 6] = v.z; 109 | } 110 | 111 | public void scale(final double s) { 112 | for (int i = 0; i < 9; i++) { 113 | this.m[i] *= s; 114 | } 115 | } 116 | 117 | public void plusEquals(final Matrix3x3d b) { 118 | for (int i = 0; i < 9; i++) { 119 | this.m[i] += b.m[i]; 120 | } 121 | } 122 | 123 | public void minusEquals(final Matrix3x3d b) { 124 | for (int i = 0; i < 9; i++) { 125 | this.m[i] -= b.m[i]; 126 | } 127 | } 128 | 129 | public void transpose() { 130 | double tmp = this.m[1]; 131 | this.m[1] = this.m[3]; 132 | this.m[3] = tmp; 133 | tmp = this.m[2]; 134 | this.m[2] = this.m[6]; 135 | this.m[6] = tmp; 136 | tmp = this.m[5]; 137 | this.m[5] = this.m[7]; 138 | this.m[7] = tmp; 139 | } 140 | 141 | public void transpose(final Matrix3x3d result) { 142 | final double m1 = this.m[1]; 143 | final double m2 = this.m[2]; 144 | final double m3 = this.m[5]; 145 | result.m[0] = this.m[0]; 146 | result.m[1] = this.m[3]; 147 | result.m[2] = this.m[6]; 148 | result.m[3] = m1; 149 | result.m[4] = this.m[4]; 150 | result.m[5] = this.m[7]; 151 | result.m[6] = m2; 152 | result.m[7] = m3; 153 | result.m[8] = this.m[8]; 154 | } 155 | 156 | public static void add(final Matrix3x3d a, final Matrix3x3d b, final Matrix3x3d result) { 157 | result.m[0] = a.m[0] + b.m[0]; 158 | result.m[1] = a.m[1] + b.m[1]; 159 | result.m[2] = a.m[2] + b.m[2]; 160 | result.m[3] = a.m[3] + b.m[3]; 161 | result.m[4] = a.m[4] + b.m[4]; 162 | result.m[5] = a.m[5] + b.m[5]; 163 | result.m[6] = a.m[6] + b.m[6]; 164 | result.m[7] = a.m[7] + b.m[7]; 165 | result.m[8] = a.m[8] + b.m[8]; 166 | } 167 | 168 | public static void mult(final Matrix3x3d a, final Matrix3x3d b, final Matrix3x3d result) { 169 | result.set(a.m[0] * b.m[0] + a.m[1] * b.m[3] + a.m[2] * b.m[6], 170 | a.m[0] * b.m[1] + a.m[1] * b.m[4] + a.m[2] * b.m[7], 171 | a.m[0] * b.m[2] + a.m[1] * b.m[5] + a.m[2] * b.m[8], 172 | a.m[3] * b.m[0] + a.m[4] * b.m[3] + a.m[5] * b.m[6], 173 | a.m[3] * b.m[1] + a.m[4] * b.m[4] + a.m[5] * b.m[7], 174 | a.m[3] * b.m[2] + a.m[4] * b.m[5] + a.m[5] * b.m[8], 175 | a.m[6] * b.m[0] + a.m[7] * b.m[3] + a.m[8] * b.m[6], 176 | a.m[6] * b.m[1] + a.m[7] * b.m[4] + a.m[8] * b.m[7], 177 | a.m[6] * b.m[2] + a.m[7] * b.m[5] + a.m[8] * b.m[8]); 178 | } 179 | 180 | public static void mult(final Matrix3x3d a, final Vector3d v, final Vector3d result) { 181 | final double x = a.m[0] * v.x + a.m[1] * v.y + a.m[2] * v.z; 182 | final double y = a.m[3] * v.x + a.m[4] * v.y + a.m[5] * v.z; 183 | final double z = a.m[6] * v.x + a.m[7] * v.y + a.m[8] * v.z; 184 | result.x = x; 185 | result.y = y; 186 | result.z = z; 187 | } 188 | 189 | public double determinant() { 190 | return this.get(0, 0) * (this.get(1, 1) * this.get(2, 2) - this.get(2, 1) * this.get(1, 2)) 191 | - this.get(0, 1) * (this.get(1, 0) * this.get(2, 2) - this.get(1, 2) * this.get(2, 0)) 192 | + this.get(0, 2) * (this.get(1, 0) * this.get(2, 1) - this.get(1, 1) * this.get(2, 0)); 193 | } 194 | 195 | public boolean invert(final Matrix3x3d result) { 196 | final double d = this.determinant(); 197 | if (d == 0.0) { 198 | return false; 199 | } 200 | final double invdet = 1.0 / d; 201 | result.set((this.m[4] * this.m[8] - this.m[7] * this.m[5]) * invdet, 202 | -(this.m[1] * this.m[8] - this.m[2] * this.m[7]) * invdet, 203 | (this.m[1] * this.m[5] - this.m[2] * this.m[4]) * invdet, 204 | -(this.m[3] * this.m[8] - this.m[5] * this.m[6]) * invdet, 205 | (this.m[0] * this.m[8] - this.m[2] * this.m[6]) * invdet, 206 | -(this.m[0] * this.m[5] - this.m[3] * this.m[2]) * invdet, 207 | (this.m[3] * this.m[7] - this.m[6] * this.m[4]) * invdet, 208 | -(this.m[0] * this.m[7] - this.m[6] * this.m[1]) * invdet, 209 | (this.m[0] * this.m[4] - this.m[3] * this.m[1]) * invdet); 210 | return true; 211 | } 212 | 213 | @Override 214 | public String toString() { 215 | final StringBuilder builder = new StringBuilder().append("{ "); 216 | for (int i = 0; i < 9; ++i) 217 | { 218 | builder.append(Double.toString(this.m[i])); 219 | if (i < 9 - 1) 220 | { 221 | builder.append(", "); 222 | } 223 | } 224 | builder.append(" }"); 225 | return builder.toString(); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/So3Util.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors.internal; 2 | 3 | public class So3Util { 4 | private static final double M_SQRT1_2 = 0.7071067811865476; 5 | private static final double ONE_6TH = 0.1666666716337204; 6 | private static final double ONE_20TH = 0.1666666716337204; 7 | private static Vector3d temp31; 8 | private static Vector3d sO3FromTwoVecN; 9 | private static Vector3d sO3FromTwoVecA; 10 | private static Vector3d sO3FromTwoVecB; 11 | private static Vector3d sO3FromTwoVecRotationAxis; 12 | private static Matrix3x3d sO3FromTwoVec33R1; 13 | private static Matrix3x3d sO3FromTwoVec33R2; 14 | private static Vector3d muFromSO3R2; 15 | private static Vector3d rotationPiAboutAxisTemp; 16 | 17 | public static void sO3FromTwoVec(final Vector3d a, final Vector3d b, final Matrix3x3d result) { 18 | Vector3d.cross(a, b, So3Util.sO3FromTwoVecN); 19 | if (So3Util.sO3FromTwoVecN.length() == 0.0) { 20 | final double dot = Vector3d.dot(a, b); 21 | if (dot >= 0.0) { 22 | result.setIdentity(); 23 | } 24 | else { 25 | Vector3d.ortho(a, So3Util.sO3FromTwoVecRotationAxis); 26 | rotationPiAboutAxis(So3Util.sO3FromTwoVecRotationAxis, result); 27 | } 28 | return; 29 | } 30 | So3Util.sO3FromTwoVecA.set(a); 31 | So3Util.sO3FromTwoVecB.set(b); 32 | So3Util.sO3FromTwoVecN.normalize(); 33 | So3Util.sO3FromTwoVecA.normalize(); 34 | So3Util.sO3FromTwoVecB.normalize(); 35 | final Matrix3x3d r1 = So3Util.sO3FromTwoVec33R1; 36 | r1.setColumn(0, So3Util.sO3FromTwoVecA); 37 | r1.setColumn(1, So3Util.sO3FromTwoVecN); 38 | Vector3d.cross(So3Util.sO3FromTwoVecN, So3Util.sO3FromTwoVecA, So3Util.temp31); 39 | r1.setColumn(2, So3Util.temp31); 40 | final Matrix3x3d r2 = So3Util.sO3FromTwoVec33R2; 41 | r2.setColumn(0, So3Util.sO3FromTwoVecB); 42 | r2.setColumn(1, So3Util.sO3FromTwoVecN); 43 | Vector3d.cross(So3Util.sO3FromTwoVecN, So3Util.sO3FromTwoVecB, So3Util.temp31); 44 | r2.setColumn(2, So3Util.temp31); 45 | r1.transpose(); 46 | Matrix3x3d.mult(r2, r1, result); 47 | } 48 | 49 | private static void rotationPiAboutAxis(final Vector3d v, final Matrix3x3d result) { 50 | So3Util.rotationPiAboutAxisTemp.set(v); 51 | So3Util.rotationPiAboutAxisTemp.scale(3.141592653589793 / So3Util.rotationPiAboutAxisTemp.length()); 52 | final double invTheta = 0.3183098861837907; 53 | final double kA = 0.0; 54 | final double kB = 0.20264236728467558; 55 | rodriguesSo3Exp(So3Util.rotationPiAboutAxisTemp, kA, kB, result); 56 | } 57 | 58 | public static void sO3FromMu(final Vector3d w, final Matrix3x3d result) { 59 | final double thetaSq = Vector3d.dot(w, w); 60 | final double theta = Math.sqrt(thetaSq); 61 | double kA; 62 | double kB; 63 | if (thetaSq < 1.0E-8) { 64 | kA = 1.0 - 0.1666666716337204 * thetaSq; 65 | kB = 0.5; 66 | } 67 | else if (thetaSq < 1.0E-6) { 68 | kB = 0.5 - 0.0416666679084301 * thetaSq; 69 | kA = 1.0 - thetaSq * 0.1666666716337204 * (1.0 - 0.1666666716337204 * thetaSq); 70 | } 71 | else { 72 | final double invTheta = 1.0 / theta; 73 | kA = Math.sin(theta) * invTheta; 74 | kB = (1.0 - Math.cos(theta)) * (invTheta * invTheta); 75 | } 76 | rodriguesSo3Exp(w, kA, kB, result); 77 | } 78 | 79 | public static void muFromSO3(final Matrix3x3d so3, final Vector3d result) { 80 | final double cosAngle = (so3.get(0, 0) + so3.get(1, 1) + so3.get(2, 2) - 1.0) * 0.5; 81 | result.set((so3.get(2, 1) - so3.get(1, 2)) / 2.0, (so3.get(0, 2) - so3.get(2, 0)) / 2.0, (so3.get(1, 0) - so3.get(0, 1)) / 2.0); 82 | final double sinAngleAbs = result.length(); 83 | if (cosAngle > 0.7071067811865476) { 84 | if (sinAngleAbs > 0.0) { 85 | result.scale(Math.asin(sinAngleAbs) / sinAngleAbs); 86 | } 87 | } 88 | else if (cosAngle > -0.7071067811865476) { 89 | final double angle = Math.acos(cosAngle); 90 | result.scale(angle / sinAngleAbs); 91 | } 92 | else { 93 | final double angle = 3.141592653589793 - Math.asin(sinAngleAbs); 94 | final double d0 = so3.get(0, 0) - cosAngle; 95 | final double d = so3.get(1, 1) - cosAngle; 96 | final double d2 = so3.get(2, 2) - cosAngle; 97 | final Vector3d r2 = So3Util.muFromSO3R2; 98 | if (d0 * d0 > d * d && d0 * d0 > d2 * d2) { 99 | r2.set(d0, (so3.get(1, 0) + so3.get(0, 1)) / 2.0, (so3.get(0, 2) + so3.get(2, 0)) / 2.0); 100 | } 101 | else if (d * d > d2 * d2) { 102 | r2.set((so3.get(1, 0) + so3.get(0, 1)) / 2.0, d, (so3.get(2, 1) + so3.get(1, 2)) / 2.0); 103 | } 104 | else { 105 | r2.set((so3.get(0, 2) + so3.get(2, 0)) / 2.0, (so3.get(2, 1) + so3.get(1, 2)) / 2.0, d2); 106 | } 107 | if (Vector3d.dot(r2, result) < 0.0) { 108 | r2.scale(-1.0); 109 | } 110 | r2.normalize(); 111 | r2.scale(angle); 112 | result.set(r2); 113 | } 114 | } 115 | 116 | private static void rodriguesSo3Exp(final Vector3d w, final double kA, final double kB, final Matrix3x3d result) { 117 | final double wx2 = w.x * w.x; 118 | final double wy2 = w.y * w.y; 119 | final double wz2 = w.z * w.z; 120 | result.set(0, 0, 1.0 - kB * (wy2 + wz2)); 121 | result.set(1, 1, 1.0 - kB * (wx2 + wz2)); 122 | result.set(2, 2, 1.0 - kB * (wx2 + wy2)); 123 | double a = kA * w.z; 124 | double b = kB * (w.x * w.y); 125 | result.set(0, 1, b - a); 126 | result.set(1, 0, b + a); 127 | a = kA * w.y; 128 | b = kB * (w.x * w.z); 129 | result.set(0, 2, b + a); 130 | result.set(2, 0, b - a); 131 | a = kA * w.x; 132 | b = kB * (w.y * w.z); 133 | result.set(1, 2, b - a); 134 | result.set(2, 1, b + a); 135 | } 136 | 137 | public static void generatorField(final int i, final Matrix3x3d pos, final Matrix3x3d result) { 138 | result.set(i, 0, 0.0); 139 | result.set((i + 1) % 3, 140 | 0, 141 | -pos.get((i + 2) % 3, 0)); 142 | result.set((i + 2) % 3, 143 | 0, pos.get((i + 1) % 3,0)); 144 | } 145 | 146 | static { 147 | So3Util.temp31 = new Vector3d(); 148 | So3Util.sO3FromTwoVecN = new Vector3d(); 149 | So3Util.sO3FromTwoVecA = new Vector3d(); 150 | So3Util.sO3FromTwoVecB = new Vector3d(); 151 | So3Util.sO3FromTwoVecRotationAxis = new Vector3d(); 152 | So3Util.sO3FromTwoVec33R1 = new Matrix3x3d(); 153 | So3Util.sO3FromTwoVec33R2 = new Matrix3x3d(); 154 | So3Util.muFromSO3R2 = new Vector3d(); 155 | So3Util.rotationPiAboutAxisTemp = new Vector3d(); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/Vector3d.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.sensors.internal; 2 | 3 | public class Vector3d { 4 | public double x; 5 | public double y; 6 | public double z; 7 | 8 | public Vector3d() { 9 | super(); 10 | } 11 | 12 | public Vector3d(final double xx, final double yy, final double zz) { 13 | super(); 14 | this.set(xx, yy, zz); 15 | } 16 | 17 | public void set(final double xx, final double yy, final double zz) { 18 | this.x = xx; 19 | this.y = yy; 20 | this.z = zz; 21 | } 22 | 23 | public void setComponent(final int i, final double val) { 24 | if (i == 0) { 25 | this.x = val; 26 | } 27 | else if (i == 1) { 28 | this.y = val; 29 | } 30 | else { 31 | this.z = val; 32 | } 33 | } 34 | 35 | public void setZero() { 36 | final double x = 0.0; 37 | this.z = x; 38 | this.y = x; 39 | this.x = x; 40 | } 41 | 42 | public void set(final Vector3d other) { 43 | this.x = other.x; 44 | this.y = other.y; 45 | this.z = other.z; 46 | } 47 | 48 | public void scale(final double s) { 49 | this.x *= s; 50 | this.y *= s; 51 | this.z *= s; 52 | } 53 | 54 | public void normalize() { 55 | final double d = this.length(); 56 | if (d != 0.0) { 57 | this.scale(1.0 / d); 58 | } 59 | } 60 | 61 | public static double dot(final Vector3d a, final Vector3d b) { 62 | return a.x * b.x + a.y * b.y + a.z * b.z; 63 | } 64 | 65 | public double length() { 66 | return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 67 | } 68 | 69 | public boolean sameValues(final Vector3d other) { 70 | return this.x == other.x && this.y == other.y && this.z == other.z; 71 | } 72 | 73 | public static void add(final Vector3d a, final Vector3d b, final Vector3d result) { 74 | result.set(a.x + b.x, a.y + b.y, a.z + b.z); 75 | } 76 | 77 | public static void sub(final Vector3d a, final Vector3d b, final Vector3d result) { 78 | result.set(a.x - b.x, a.y - b.y, a.z - b.z); 79 | } 80 | 81 | public static void cross(final Vector3d a, final Vector3d b, final Vector3d result) { 82 | result.set(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); 83 | } 84 | 85 | public static void ortho(final Vector3d v, final Vector3d result) { 86 | int k = largestAbsComponent(v) - 1; 87 | if (k < 0) { 88 | k = 2; 89 | } 90 | result.setZero(); 91 | result.setComponent(k, 1.0); 92 | cross(v, result, result); 93 | result.normalize(); 94 | } 95 | 96 | public static int largestAbsComponent(final Vector3d v) { 97 | final double xAbs = Math.abs(v.x); 98 | final double yAbs = Math.abs(v.y); 99 | final double zAbs = Math.abs(v.z); 100 | if (xAbs > yAbs) { 101 | if (xAbs > zAbs) { 102 | return 0; 103 | } 104 | return 2; 105 | } 106 | else { 107 | if (yAbs > zAbs) { 108 | return 1; 109 | } 110 | return 2; 111 | } 112 | } 113 | 114 | @Override 115 | public String toString() { 116 | final StringBuilder builder = new StringBuilder().append("{ "); 117 | builder.append(Double.toString(this.x)); 118 | builder.append(", "); 119 | builder.append(Double.toString(this.y)); 120 | builder.append(", "); 121 | builder.append(Double.toString(this.z)); 122 | builder.append(" }"); 123 | return builder.toString(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "21.1.2" 6 | 7 | defaultConfig { 8 | applicationId "com.google.vrtoolkit.cardboard.samples.treasurehunt" 9 | minSdkVersion 16 10 | targetSdkVersion 21 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | 15 | compileOptions { 16 | sourceCompatibility JavaVersion.VERSION_1_7 17 | targetCompatibility JavaVersion.VERSION_1_7 18 | } 19 | 20 | lintOptions { 21 | abortOnError false 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | compile project(':library') 34 | } 35 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add any project specific keep options here: 2 | 3 | # If your project uses WebView with JS, uncomment the following 4 | # and specify the fully qualified class name to the JavaScript interface 5 | # class: 6 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 7 | # public *; 8 | #} 9 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/google/vrtoolkit/cardboard/samples/treasurehunt/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.google.vrtoolkit.cardboard.samples.treasurehunt; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/vrtoolkit/cardboard/samples/treasurehunt/CardboardOverlayView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All Rights Reserved. 3 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.vrtoolkit.cardboard.samples.treasurehunt; 18 | 19 | import android.content.Context; 20 | import android.graphics.Color; 21 | import android.graphics.Typeface; 22 | import android.util.AttributeSet; 23 | import android.util.TypedValue; 24 | import android.view.Gravity; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.view.animation.AlphaAnimation; 28 | import android.view.animation.Animation; 29 | import android.widget.ImageView; 30 | import android.widget.LinearLayout; 31 | import android.widget.TextView; 32 | 33 | /** 34 | * Contains two sub-views to provide a simple stereo HUD. 35 | */ 36 | public class CardboardOverlayView extends LinearLayout { 37 | private static final String TAG = CardboardOverlayView.class.getSimpleName(); 38 | private final CardboardOverlayEyeView mLeftView; 39 | private final CardboardOverlayEyeView mRightView; 40 | private AlphaAnimation mTextFadeAnimation; 41 | 42 | public CardboardOverlayView(Context context, AttributeSet attrs) { 43 | super(context, attrs); 44 | setOrientation(HORIZONTAL); 45 | 46 | LayoutParams params = new LayoutParams( 47 | LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f); 48 | params.setMargins(0, 0, 0, 0); 49 | 50 | mLeftView = new CardboardOverlayEyeView(context, attrs); 51 | mLeftView.setLayoutParams(params); 52 | addView(mLeftView); 53 | 54 | mRightView = new CardboardOverlayEyeView(context, attrs); 55 | mRightView.setLayoutParams(params); 56 | addView(mRightView); 57 | 58 | // Set some reasonable defaults. 59 | setDepthOffset(0.016f); 60 | setColor(Color.rgb(150, 255, 180)); 61 | setVisibility(View.VISIBLE); 62 | 63 | mTextFadeAnimation = new AlphaAnimation(1.0f, 0.0f); 64 | mTextFadeAnimation.setDuration(5000); 65 | } 66 | 67 | public void show3DToast(String message) { 68 | setText(message); 69 | setTextAlpha(1f); 70 | mTextFadeAnimation.setAnimationListener(new EndAnimationListener() { 71 | @Override 72 | public void onAnimationEnd(Animation animation) { 73 | setTextAlpha(0f); 74 | } 75 | }); 76 | startAnimation(mTextFadeAnimation); 77 | } 78 | 79 | private abstract class EndAnimationListener implements Animation.AnimationListener { 80 | @Override public void onAnimationRepeat(Animation animation) {} 81 | @Override public void onAnimationStart(Animation animation) {} 82 | } 83 | 84 | private void setDepthOffset(float offset) { 85 | mLeftView.setOffset(offset); 86 | mRightView.setOffset(-offset); 87 | } 88 | 89 | private void setText(String text) { 90 | mLeftView.setText(text); 91 | mRightView.setText(text); 92 | } 93 | 94 | private void setTextAlpha(float alpha) { 95 | mLeftView.setTextViewAlpha(alpha); 96 | mRightView.setTextViewAlpha(alpha); 97 | } 98 | 99 | private void setColor(int color) { 100 | mLeftView.setColor(color); 101 | mRightView.setColor(color); 102 | } 103 | 104 | /** 105 | * A simple view group containing some horizontally centered text underneath a horizontally 106 | * centered image. 107 | * 108 | * This is a helper class for CardboardOverlayView. 109 | */ 110 | private class CardboardOverlayEyeView extends ViewGroup { 111 | private final ImageView imageView; 112 | private final TextView textView; 113 | private float offset; 114 | 115 | public CardboardOverlayEyeView(Context context, AttributeSet attrs) { 116 | super(context, attrs); 117 | imageView = new ImageView(context, attrs); 118 | imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 119 | imageView.setAdjustViewBounds(true); // Preserve aspect ratio. 120 | addView(imageView); 121 | 122 | textView = new TextView(context, attrs); 123 | textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14.0f); 124 | textView.setTypeface(textView.getTypeface(), Typeface.BOLD); 125 | textView.setGravity(Gravity.CENTER); 126 | textView.setShadowLayer(3.0f, 0.0f, 0.0f, Color.DKGRAY); 127 | addView(textView); 128 | } 129 | 130 | public void setColor(int color) { 131 | imageView.setColorFilter(color); 132 | textView.setTextColor(color); 133 | } 134 | 135 | public void setText(String text) { 136 | textView.setText(text); 137 | } 138 | 139 | public void setTextViewAlpha(float alpha) { 140 | textView.setAlpha(alpha); 141 | } 142 | 143 | public void setOffset(float offset) { 144 | this.offset = offset; 145 | } 146 | 147 | @Override 148 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 149 | // Width and height of this ViewGroup. 150 | final int width = right - left; 151 | final int height = bottom - top; 152 | 153 | // The size of the image, given as a fraction of the dimension as a ViewGroup. 154 | // We multiply both width and heading with this number to compute the image's bounding 155 | // box. Inside the box, the image is the horizontally and vertically centered. 156 | final float imageSize = 0.12f; 157 | 158 | // The fraction of this ViewGroup's height by which we shift the image off the 159 | // ViewGroup's center. Positive values shift downwards, negative values shift upwards. 160 | final float verticalImageOffset = -0.07f; 161 | 162 | // Vertical position of the text, specified in fractions of this ViewGroup's height. 163 | final float verticalTextPos = 0.52f; 164 | 165 | // Layout ImageView 166 | float imageMargin = (1.0f - imageSize) / 2.0f; 167 | float leftMargin = (int) (width * (imageMargin + offset)); 168 | float topMargin = (int) (height * (imageMargin + verticalImageOffset)); 169 | imageView.layout( 170 | (int) leftMargin, (int) topMargin, 171 | (int) (leftMargin + width * imageSize), (int) (topMargin + height * imageSize)); 172 | 173 | // Layout TextView 174 | leftMargin = offset * width; 175 | topMargin = height * verticalTextPos; 176 | textView.layout( 177 | (int) leftMargin, (int) topMargin, 178 | (int) (leftMargin + width), (int) (topMargin + height * (1.0f - verticalTextPos))); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/vrtoolkit/cardboard/samples/treasurehunt/WorldLayoutData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All Rights Reserved. 3 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.vrtoolkit.cardboard.samples.treasurehunt; 18 | 19 | /** 20 | * Contains vertex, normal and color data. 21 | */ 22 | public final class WorldLayoutData { 23 | 24 | public static final float[] CUBE_COORDS = new float[] { 25 | // Front face 26 | -1.0f, 1.0f, 1.0f, 27 | -1.0f, -1.0f, 1.0f, 28 | 1.0f, 1.0f, 1.0f, 29 | -1.0f, -1.0f, 1.0f, 30 | 1.0f, -1.0f, 1.0f, 31 | 1.0f, 1.0f, 1.0f, 32 | 33 | // Right face 34 | 1.0f, 1.0f, 1.0f, 35 | 1.0f, -1.0f, 1.0f, 36 | 1.0f, 1.0f, -1.0f, 37 | 1.0f, -1.0f, 1.0f, 38 | 1.0f, -1.0f, -1.0f, 39 | 1.0f, 1.0f, -1.0f, 40 | 41 | // Back face 42 | 1.0f, 1.0f, -1.0f, 43 | 1.0f, -1.0f, -1.0f, 44 | -1.0f, 1.0f, -1.0f, 45 | 1.0f, -1.0f, -1.0f, 46 | -1.0f, -1.0f, -1.0f, 47 | -1.0f, 1.0f, -1.0f, 48 | 49 | // Left face 50 | -1.0f, 1.0f, -1.0f, 51 | -1.0f, -1.0f, -1.0f, 52 | -1.0f, 1.0f, 1.0f, 53 | -1.0f, -1.0f, -1.0f, 54 | -1.0f, -1.0f, 1.0f, 55 | -1.0f, 1.0f, 1.0f, 56 | 57 | // Top face 58 | -1.0f, 1.0f, -1.0f, 59 | -1.0f, 1.0f, 1.0f, 60 | 1.0f, 1.0f, -1.0f, 61 | -1.0f, 1.0f, 1.0f, 62 | 1.0f, 1.0f, 1.0f, 63 | 1.0f, 1.0f, -1.0f, 64 | 65 | // Bottom face 66 | 1.0f, -1.0f, -1.0f, 67 | 1.0f, -1.0f, 1.0f, 68 | -1.0f, -1.0f, -1.0f, 69 | 1.0f, -1.0f, 1.0f, 70 | -1.0f, -1.0f, 1.0f, 71 | -1.0f, -1.0f, -1.0f, 72 | }; 73 | 74 | public static final float[] CUBE_COLORS = new float[] { 75 | // front, green 76 | 0f, 0.5273f, 0.2656f, 1.0f, 77 | 0f, 0.5273f, 0.2656f, 1.0f, 78 | 0f, 0.5273f, 0.2656f, 1.0f, 79 | 0f, 0.5273f, 0.2656f, 1.0f, 80 | 0f, 0.5273f, 0.2656f, 1.0f, 81 | 0f, 0.5273f, 0.2656f, 1.0f, 82 | 83 | // right, blue 84 | 0.0f, 0.3398f, 0.9023f, 1.0f, 85 | 0.0f, 0.3398f, 0.9023f, 1.0f, 86 | 0.0f, 0.3398f, 0.9023f, 1.0f, 87 | 0.0f, 0.3398f, 0.9023f, 1.0f, 88 | 0.0f, 0.3398f, 0.9023f, 1.0f, 89 | 0.0f, 0.3398f, 0.9023f, 1.0f, 90 | 91 | // back, also green 92 | 0f, 0.5273f, 0.2656f, 1.0f, 93 | 0f, 0.5273f, 0.2656f, 1.0f, 94 | 0f, 0.5273f, 0.2656f, 1.0f, 95 | 0f, 0.5273f, 0.2656f, 1.0f, 96 | 0f, 0.5273f, 0.2656f, 1.0f, 97 | 0f, 0.5273f, 0.2656f, 1.0f, 98 | 99 | // left, also blue 100 | 0.0f, 0.3398f, 0.9023f, 1.0f, 101 | 0.0f, 0.3398f, 0.9023f, 1.0f, 102 | 0.0f, 0.3398f, 0.9023f, 1.0f, 103 | 0.0f, 0.3398f, 0.9023f, 1.0f, 104 | 0.0f, 0.3398f, 0.9023f, 1.0f, 105 | 0.0f, 0.3398f, 0.9023f, 1.0f, 106 | 107 | // top, red 108 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 109 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 110 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 111 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 112 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 113 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 114 | 115 | // bottom, also red 116 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 117 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 118 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 119 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 120 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 121 | 0.8359375f, 0.17578125f, 0.125f, 1.0f, 122 | }; 123 | 124 | public static final float[] CUBE_FOUND_COLORS = new float[] { 125 | // front, yellow 126 | 1.0f, 0.6523f, 0.0f, 1.0f, 127 | 1.0f, 0.6523f, 0.0f, 1.0f, 128 | 1.0f, 0.6523f, 0.0f, 1.0f, 129 | 1.0f, 0.6523f, 0.0f, 1.0f, 130 | 1.0f, 0.6523f, 0.0f, 1.0f, 131 | 1.0f, 0.6523f, 0.0f, 1.0f, 132 | 133 | // right, yellow 134 | 1.0f, 0.6523f, 0.0f, 1.0f, 135 | 1.0f, 0.6523f, 0.0f, 1.0f, 136 | 1.0f, 0.6523f, 0.0f, 1.0f, 137 | 1.0f, 0.6523f, 0.0f, 1.0f, 138 | 1.0f, 0.6523f, 0.0f, 1.0f, 139 | 1.0f, 0.6523f, 0.0f, 1.0f, 140 | 141 | // back, yellow 142 | 1.0f, 0.6523f, 0.0f, 1.0f, 143 | 1.0f, 0.6523f, 0.0f, 1.0f, 144 | 1.0f, 0.6523f, 0.0f, 1.0f, 145 | 1.0f, 0.6523f, 0.0f, 1.0f, 146 | 1.0f, 0.6523f, 0.0f, 1.0f, 147 | 1.0f, 0.6523f, 0.0f, 1.0f, 148 | 149 | // left, yellow 150 | 1.0f, 0.6523f, 0.0f, 1.0f, 151 | 1.0f, 0.6523f, 0.0f, 1.0f, 152 | 1.0f, 0.6523f, 0.0f, 1.0f, 153 | 1.0f, 0.6523f, 0.0f, 1.0f, 154 | 1.0f, 0.6523f, 0.0f, 1.0f, 155 | 1.0f, 0.6523f, 0.0f, 1.0f, 156 | 157 | // top, yellow 158 | 1.0f, 0.6523f, 0.0f, 1.0f, 159 | 1.0f, 0.6523f, 0.0f, 1.0f, 160 | 1.0f, 0.6523f, 0.0f, 1.0f, 161 | 1.0f, 0.6523f, 0.0f, 1.0f, 162 | 1.0f, 0.6523f, 0.0f, 1.0f, 163 | 1.0f, 0.6523f, 0.0f, 1.0f, 164 | 165 | // bottom, yellow 166 | 1.0f, 0.6523f, 0.0f, 1.0f, 167 | 1.0f, 0.6523f, 0.0f, 1.0f, 168 | 1.0f, 0.6523f, 0.0f, 1.0f, 169 | 1.0f, 0.6523f, 0.0f, 1.0f, 170 | 1.0f, 0.6523f, 0.0f, 1.0f, 171 | 1.0f, 0.6523f, 0.0f, 1.0f, 172 | }; 173 | 174 | public static final float[] CUBE_NORMALS = new float[] { 175 | // Front face 176 | 0.0f, 0.0f, 1.0f, 177 | 0.0f, 0.0f, 1.0f, 178 | 0.0f, 0.0f, 1.0f, 179 | 0.0f, 0.0f, 1.0f, 180 | 0.0f, 0.0f, 1.0f, 181 | 0.0f, 0.0f, 1.0f, 182 | 183 | // Right face 184 | 1.0f, 0.0f, 0.0f, 185 | 1.0f, 0.0f, 0.0f, 186 | 1.0f, 0.0f, 0.0f, 187 | 1.0f, 0.0f, 0.0f, 188 | 1.0f, 0.0f, 0.0f, 189 | 1.0f, 0.0f, 0.0f, 190 | 191 | // Back face 192 | 0.0f, 0.0f, -1.0f, 193 | 0.0f, 0.0f, -1.0f, 194 | 0.0f, 0.0f, -1.0f, 195 | 0.0f, 0.0f, -1.0f, 196 | 0.0f, 0.0f, -1.0f, 197 | 0.0f, 0.0f, -1.0f, 198 | 199 | // Left face 200 | -1.0f, 0.0f, 0.0f, 201 | -1.0f, 0.0f, 0.0f, 202 | -1.0f, 0.0f, 0.0f, 203 | -1.0f, 0.0f, 0.0f, 204 | -1.0f, 0.0f, 0.0f, 205 | -1.0f, 0.0f, 0.0f, 206 | 207 | // Top face 208 | 0.0f, 1.0f, 0.0f, 209 | 0.0f, 1.0f, 0.0f, 210 | 0.0f, 1.0f, 0.0f, 211 | 0.0f, 1.0f, 0.0f, 212 | 0.0f, 1.0f, 0.0f, 213 | 0.0f, 1.0f, 0.0f, 214 | 215 | // Bottom face 216 | 0.0f, -1.0f, 0.0f, 217 | 0.0f, -1.0f, 0.0f, 218 | 0.0f, -1.0f, 0.0f, 219 | 0.0f, -1.0f, 0.0f, 220 | 0.0f, -1.0f, 0.0f, 221 | 0.0f, -1.0f, 0.0f 222 | }; 223 | 224 | public static final float[] FLOOR_COORDS = new float[] { 225 | 200f, 0, -200f, 226 | -200f, 0, -200f, 227 | -200f, 0, 200f, 228 | 200f, 0, -200f, 229 | -200f, 0, 200f, 230 | 200f, 0, 200f, 231 | }; 232 | 233 | public static final float[] FLOOR_NORMALS = new float[] { 234 | 0.0f, 1.0f, 0.0f, 235 | 0.0f, 1.0f, 0.0f, 236 | 0.0f, 1.0f, 0.0f, 237 | 0.0f, 1.0f, 0.0f, 238 | 0.0f, 1.0f, 0.0f, 239 | 0.0f, 1.0f, 0.0f, 240 | }; 241 | 242 | public static final float[] FLOOR_COLORS = new float[] { 243 | 0.0f, 0.3398f, 0.9023f, 1.0f, 244 | 0.0f, 0.3398f, 0.9023f, 1.0f, 245 | 0.0f, 0.3398f, 0.9023f, 1.0f, 246 | 0.0f, 0.3398f, 0.9023f, 1.0f, 247 | 0.0f, 0.3398f, 0.9023f, 1.0f, 248 | 0.0f, 0.3398f, 0.9023f, 1.0f, 249 | }; 250 | } 251 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/layout/common_ui.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 15 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsanchezsaez/cardboard-java/df049c9024d275189893330eeb6ad6cf5b32275d/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/raw/grid_fragment.shader: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec4 v_Color; 3 | varying vec3 v_Grid; 4 | 5 | void main() { 6 | float depth = gl_FragCoord.z / gl_FragCoord.w; // Calculate world-space distance. 7 | 8 | if ((mod(abs(v_Grid.x), 10.0) < 0.1) || (mod(abs(v_Grid.z), 10.0) < 0.1)) { 9 | gl_FragColor = max(0.0, (90.0-depth) / 90.0) * vec4(1.0, 1.0, 1.0, 1.0) 10 | + min(1.0, depth / 90.0) * v_Color; 11 | } else { 12 | gl_FragColor = v_Color; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/src/main/res/raw/light_vertex.shader: -------------------------------------------------------------------------------- 1 | uniform mat4 u_Model; 2 | uniform mat4 u_MVP; 3 | uniform mat4 u_MVMatrix; 4 | uniform vec3 u_LightPos; 5 | 6 | attribute vec4 a_Position; 7 | attribute vec4 a_Color; 8 | attribute vec3 a_Normal; 9 | 10 | varying vec4 v_Color; 11 | varying vec3 v_Grid; 12 | 13 | void main() { 14 | v_Grid = vec3(u_Model * a_Position); 15 | 16 | vec3 modelViewVertex = vec3(u_MVMatrix * a_Position); 17 | vec3 modelViewNormal = vec3(u_MVMatrix * vec4(a_Normal, 0.0)); 18 | 19 | float distance = length(u_LightPos - modelViewVertex); 20 | vec3 lightVector = normalize(u_LightPos - modelViewVertex); 21 | float diffuse = max(dot(modelViewNormal, lightVector), 0.5); 22 | 23 | diffuse = diffuse * (1.0 / (1.0 + (0.00001 * distance * distance))); 24 | v_Color = a_Color * diffuse; 25 | gl_Position = u_MVP * a_Position; 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/res/raw/passthrough_fragment.shader: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec4 v_Color; 3 | 4 | void main() { 5 | gl_FragColor = v_Color; 6 | } 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cardboard Sample 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':library', ':sample' 2 | --------------------------------------------------------------------------------