Merge pull request #14803 from highfidelity/feature/quest

Case 20624: Final merge of Quest Interface application into master
This commit is contained in:
Sam Gateau 2019-02-20 11:53:24 -08:00 committed by GitHub
commit 4dbae787cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 864 additions and 212 deletions

1
.gitignore vendored
View file

@ -24,6 +24,7 @@ android/**/bin
android/**/src/main/res/values/libs.xml
android/**/src/main/assets
android/**/gradle*
*.class
# VSCode
# List taken from Github Global Ignores master@435c4d92

65
BUILD_QUEST.md Normal file
View file

@ -0,0 +1,65 @@
Please read the [general build guide](BUILD.md) for information on building other platform. Only Quest specific instructions are found in this file.
# Dependencies
Building is currently supported on OSX, Windows and Linux platforms, but developers intending to do work on the library dependencies are strongly urged to use 64 bit Linux as a build platform
You will need the following tools to build Android targets.
* [Android Studio](https://developer.android.com/studio/index.html)
### Android Studio
Download the Android Studio installer and run it. Once installed, at the welcome screen, click configure in the lower right corner and select SDK manager
From the SDK Platforms tab, select API levels 24 and 26.
From the SDK Tools tab select the following
* Android SDK Build-Tools
* GPU Debugging Tools
* CMake (even if you have a separate CMake installation)
* LLDB
* Android SDK Platform-Tools
* Android SDK Tools
* NDK (even if you have the NDK installed separately)
Make sure the NDK installed version is 18 (or higher)
# Environment
Setting up the environment for android builds requires some additional steps
#### Set up machine specific Gradle properties
Create a `gradle.properties` file in $HOME/.gradle. Edit the file to contain the following
HIFI_ANDROID_PRECOMPILED=<your_home_directory>/Android/hifi_externals
HIFI_ANDROID_KEYSTORE=<key_store_directory>/<keystore_name>.jks
HIFI_ANDROID_KEYSTORE_PASSWORD=<password>
HIFI_ANDROID_KEY_ALIAS=<key_alias>
HIFI_ANDROID_KEY_PASSWORD=<key_password>
Note, do not use `$HOME` for the path. It must be a fully qualified path name.
### Setup the repository
Clone the repository
`git clone https://github.com/highfidelity/hifi.git`
Enter the repository `android` directory
`cd hifi/android`
# Building & Running
* Open Android Studio
* Choose _Open Existing Android Studio Project_
* Navigate to the `hifi` repository and choose the `android` folder and select _OK_
* Open Gradle.settings and comment out any projects not necessary
* From _File_ menu select _Sync with File System_ to resync Gradle settings
* From the _Build_ menu select _Make Project_
* From
* Once the build completes, from the _Run_ menu select _Run App_

View file

@ -100,6 +100,13 @@ if (ANDROID)
add_definitions(-DCUSTOM_DISPLAY_PLUGINS)
set(PLATFORM_PLUGIN_LIBRARIES oculusMobile oculusMobilePlugin)
endif()
# Allow client code to use preprocessor macros to distinguish between quest and non-quest builds
if (${HIFI_ANDROID_APP} STREQUAL "questInterface")
add_definitions(-DANDROID_APP_QUEST_INTERFACE)
elseif(${HIFI_ANDROID_APP} STREQUAL "interface")
add_definitions(-DANDROID_APP_INTERFACE)
endif()
else ()
set(PLATFORM_QT_COMPONENTS WebEngine Xml)
endif ()

View file

@ -81,6 +81,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
private boolean nativeEnterBackgroundCallEnqueued = false;
private SlidingDrawer mWebSlidingDrawer;
private boolean mStartInDomain;
private boolean isLoading;
// private GvrApi gvrApi;
// Opaque native pointer to the Application C++ object.
// This object is owned by the InterfaceActivity instance and passed to the native methods.
@ -94,7 +95,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
@Override
public void onCreate(Bundle savedInstanceState) {
super.isLoading = true;
isLoading = true;
Intent intent = getIntent();
if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) {
intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL));
@ -145,7 +146,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
@Override
protected void onPause() {
super.onPause();
if (super.isLoading) {
if (isLoading) {
nativeEnterBackgroundCallEnqueued = true;
} else {
nativeEnterBackground();
@ -172,7 +173,6 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
super.onResume();
nativeEnterForeground();
surfacesWorkaround();
keepInterfaceRunning = false;
registerReceiver(headsetStateReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
//gvrApi.resumeTracking();
}
@ -382,7 +382,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
}
public void onAppLoadedComplete() {
super.isLoading = false;
isLoading = false;
if (nativeEnterBackgroundCallEnqueued) {
nativeEnterBackground();
}
@ -413,7 +413,6 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
@Override
public void onExpand() {
keepInterfaceRunning = true;
}
@Override

View file

@ -1,7 +1,7 @@
set(TARGET_NAME questFramePlayer)
setup_hifi_library(AndroidExtras)
link_hifi_libraries(shared ktx shaders gpu gl oculusMobile ${PLATFORM_GL_BACKEND})
target_include_directories(${TARGET_NAME} PRIVATE ${HIFI_ANDROID_PRECOMPILED}/ovr/VrApi/Include)
target_link_libraries(${TARGET_NAME} android log m)
target_opengl()
target_oculus_mobile()

View file

@ -19,24 +19,6 @@
android:name="org.qtproject.qt5.android.bindings.QtApplication"
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity
android:name=".QuestQtActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:excludeFromRecents="false"
android:alwaysRetainTaskState="true"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
>
<!-- JNI nonsense -->
<meta-data android:name="android.app.lib_name" android:value="questFramePlayer"/>
<!-- Qt nonsense -->
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
</activity>
<activity
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
@ -50,6 +32,13 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- JNI nonsense -->
<meta-data android:name="android.app.lib_name" android:value="questFramePlayer"/>
<!-- Qt nonsense -->
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,30 @@
//
// Created by Bradley Austin Davis on 2019/02/15
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AndroidHelper.h"
#include <QtCore/QDebug>
#include <QtGui/QGuiApplication>
AndroidHelper::AndroidHelper() {
}
AndroidHelper::~AndroidHelper() {
}
void AndroidHelper::notifyLoadComplete() {
emit qtAppLoadComplete();
}
void AndroidHelper::notifyEnterForeground() {
emit enterForeground();
}
void AndroidHelper::notifyEnterBackground() {
emit enterBackground();
}

View file

@ -0,0 +1,43 @@
//
// Created by Bradley Austin Davis on 2019/02/15
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Android_Helper_h
#define hifi_Android_Helper_h
#include <QtCore/QObject>
#include <QtCore/QMap>
#include <QtCore/QUrl>
#include <QtCore/QEventLoop>
class AndroidHelper : public QObject {
Q_OBJECT
public:
AndroidHelper(AndroidHelper const&) = delete;
void operator=(AndroidHelper const&) = delete;
static AndroidHelper& instance() {
static AndroidHelper instance;
return instance;
}
void notifyLoadComplete();
void notifyEnterForeground();
void notifyEnterBackground();
signals:
void qtAppLoadComplete();
void enterForeground();
void enterBackground();
private:
AndroidHelper();
~AndroidHelper();
};
#endif

View file

@ -11,15 +11,11 @@
#include <QtWidgets/QFileDialog>
PlayerWindow::PlayerWindow() {
installEventFilter(this);
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
setFlags(Qt::Window);
setSurfaceType(QSurface::OpenGLSurface);
create();
showFullScreen();
// Ensure the window is visible and the GL context is valid
QCoreApplication::processEvents();
_renderThread.initialize(this);
}
PlayerWindow::~PlayerWindow() {
_renderThread.initialize();
}

View file

@ -8,22 +8,13 @@
#pragma once
#include <QtGui/QWindow>
#include <QtCore/QSettings>
#include <gpu/Forward.h>
#include "RenderThread.h"
// Create a simple OpenGL window that renders text in various ways
class PlayerWindow : public QWindow {
public:
PlayerWindow();
virtual ~PlayerWindow();
protected:
//bool eventFilter(QObject* obj, QEvent* event) override;
//void keyPressEvent(QKeyEvent* event) override;
virtual ~PlayerWindow() {}
private:
QSettings _settings;
RenderThread _renderThread;
};

View file

@ -16,6 +16,7 @@
#include <QtCore/QFileInfo>
#include <QtGui/QWindow>
#include <QtGui/QImageReader>
#include <QtAndroidExtras/QAndroidJniObject>
#include <gl/QOpenGLContextWrapper.h>
#include <gpu/FrameIO.h>
@ -29,9 +30,7 @@
#include <VrApi.h>
#include <VrApi_Input.h>
static JNIEnv* _env { nullptr };
static JavaVM* _vm { nullptr };
static jobject _activity { nullptr };
#include "AndroidHelper.h"
struct HandController{
ovrInputTrackedRemoteCapabilities caps {};
@ -48,21 +47,43 @@ struct HandController{
};
std::vector<HandController> devices;
QAndroidJniObject __interfaceActivity;
extern "C" {
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) {
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void *) {
__android_log_write(ANDROID_LOG_WARN, "QQQ", __FUNCTION__);
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnCreate(JNIEnv *env, jobject obj) {
__android_log_print(ANDROID_LOG_INFO, "QQQ", __FUNCTION__);
__interfaceActivity = QAndroidJniObject(obj);
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() {
__interfaceActivity.callMethod<void>("onAppLoadedComplete", "()V");
QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, nullptr, nullptr);
});
}
JNIEXPORT void JNICALL Java_io_highfidelity_frameplayer_QuestQtActivity_nativeOnCreate(JNIEnv* env, jobject obj) {
env->GetJavaVM(&_vm);
_activity = env->NewGlobalRef(obj);
JNIEXPORT void
Java_io_highfidelity_oculus_OculusMobileActivity_questOnAppAfterLoad(JNIEnv *env, jobject obj) {
AndroidHelper::instance().moveToThread(qApp->thread());
}
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnPause(JNIEnv *env, jobject obj) {
AndroidHelper::instance().notifyEnterBackground();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnResume(JNIEnv *env, jobject obj) {
AndroidHelper::instance().notifyEnterForeground();
}
}
static const char* FRAME_FILE = "assets:/frames/20190121_1220.json";
static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) {
@ -84,11 +105,10 @@ void RenderThread::move(const glm::vec3& v) {
_correction = glm::inverse(glm::translate(mat4(), v)) * _correction;
}
void RenderThread::initialize(QWindow* window) {
void RenderThread::initialize() {
std::unique_lock<std::mutex> lock(_frameLock);
setObjectName("RenderThread");
Parent::initialize();
_window = window;
_thread->setObjectName("RenderThread");
}
@ -96,14 +116,7 @@ void RenderThread::setup() {
// Wait until the context has been moved to this thread
{ std::unique_lock<std::mutex> lock(_frameLock); }
ovr::VrHandler::initVr();
__android_log_write(ANDROID_LOG_WARN, "QQQ", "Launching oculus activity");
_vm->AttachCurrentThread(&_env, nullptr);
jclass cls = _env->GetObjectClass(_activity);
jmethodID mid = _env->GetMethodID(cls, "launchOculusActivity", "()V");
_env->CallVoidMethod(_activity, mid);
__android_log_write(ANDROID_LOG_WARN, "QQQ", "Launching oculus activity done");
ovr::VrHandler::setHandler(this);
makeCurrent();
@ -169,7 +182,6 @@ void RenderThread::handleInput() {
const auto &remote = controller.state;
if (remote.Joystick.x != 0.0f || remote.Joystick.y != 0.0f) {
glm::vec3 translation;
float rotation = 0.0f;
if (caps.ControllerCapabilities & ovrControllerCaps_LeftHand) {
translation = glm::vec3{0.0f, -remote.Joystick.y, 0.0f};
} else {

View file

@ -20,11 +20,9 @@
class RenderThread : public GenericThread, ovr::VrHandler {
using Parent = GenericThread;
public:
QWindow* _window{ nullptr };
std::mutex _mutex;
gpu::ContextPointer _gpuContext; // initialized during window creation
std::shared_ptr<gpu::Backend> _backend;
std::atomic<size_t> _presentCount{ 0 };
std::mutex _frameLock;
std::queue<gpu::FramePointer> _pendingFrames;
gpu::FramePointer _activeFrame;
@ -39,6 +37,6 @@ public:
void handleInput();
void submitFrame(const gpu::FramePointer& frame);
void initialize(QWindow* window);
void initialize();
void renderFrame();
};

View file

@ -11,30 +11,33 @@
#include <QtGui/QGuiApplication>
#include <QtCore/QTimer>
#include <QtCore/QFileInfo>
#include <QtAndroidExtras/QAndroidJniObject>
#include <Trace.h>
#include "PlayerWindow.h"
#include "AndroidHelper.h"
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
const char * local=message.toStdString().c_str();
const char* local = message.toStdString().c_str();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
__android_log_write(ANDROID_LOG_DEBUG, "Interface", local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO,"Interface",local);
__android_log_write(ANDROID_LOG_INFO, "Interface", local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN,"Interface",local);
__android_log_write(ANDROID_LOG_WARN, "Interface", local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR,"Interface",local);
__android_log_write(ANDROID_LOG_ERROR, "Interface", local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
__android_log_write(ANDROID_LOG_FATAL, "Interface", local);
abort();
}
}
@ -46,11 +49,13 @@ int main(int argc, char** argv) {
auto oldMessageHandler = qInstallMessageHandler(messageHandler);
DependencyManager::set<tracing::Tracer>();
PlayerWindow window;
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec");
QTimer::singleShot(10, []{
__android_log_write(ANDROID_LOG_WARN, "QQQ", "notifyLoadComplete");
AndroidHelper::instance().notifyLoadComplete();
});
__android_log_write(ANDROID_LOG_WARN, "QQQ", "Exec");
app.exec();
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec done");
__android_log_write(ANDROID_LOG_WARN, "QQQ", "Exec done");
qInstallMessageHandler(oldMessageHandler);
return 0;
}

View file

@ -1,53 +0,0 @@
//
// Created by Bradley Austin Davis on 2018/11/20
// Copyright 2013-2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
package io.highfidelity.frameplayer;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtActivity;
import io.highfidelity.oculus.OculusMobileActivity;
public class QuestQtActivity extends QtActivity {
private native void nativeOnCreate();
private boolean launchedQuestMode = false;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.w("QQQ_Qt", "QuestQtActivity::onCreate");
super.onCreate(savedInstanceState);
nativeOnCreate();
}
@Override
public void onDestroy() {
Log.w("QQQ_Qt", "QuestQtActivity::onDestroy");
super.onDestroy();
}
public void launchOculusActivity() {
Log.w("QQQ_Qt", "QuestQtActivity::launchOculusActivity");
runOnUiThread(()->{
keepInterfaceRunning = true;
launchedQuestMode = true;
moveTaskToBack(true);
startActivity(new Intent(this, QuestRenderActivity.class));
});
}
@Override
public void onResume() {
super.onResume();
if (launchedQuestMode) {
moveTaskToBack(true);
}
}
}

View file

@ -1,14 +1,6 @@
package io.highfidelity.frameplayer;
import android.content.Intent;
import android.os.Bundle;
import io.highfidelity.oculus.OculusMobileActivity;
public class QuestRenderActivity extends OculusMobileActivity {
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
startActivity(new Intent(this, QuestQtActivity.class));
}
}

View file

@ -0,0 +1,16 @@
set(TARGET_NAME questInterface)
setup_hifi_library()
link_hifi_libraries(
shared task networking qml
image fbx hfm render-utils physics entities octree
oculusMobile oculusMobilePlugin
gl gpu ${PLATFORM_GL_BACKEND}
)
target_opengl()
target_bullet()
target_oculus_mobile()
add_subdirectory("${CMAKE_SOURCE_DIR}/interface" "libraries/interface")
include_directories("${CMAKE_SOURCE_DIR}/interface/src")
add_subdirectory("${CMAKE_SOURCE_DIR}/plugins/hifiCodec" "libraries/hifiCodecPlugin")
target_link_libraries(questInterface android log m interface)

View file

@ -0,0 +1,149 @@
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
task renameHifiACTaskDebug() {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/debug/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
task renameHifiACTaskRelease(type: Copy) {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/release/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
android {
compileSdkVersion 28
defaultConfig {
applicationId "io.highfidelity.questInterface"
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName appVersionName
ndk { abiFilters 'arm64-v8a' }
externalNativeBuild {
cmake {
arguments '-DHIFI_ANDROID=1',
'-DHIFI_ANDROID_APP=questInterface',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_STL=c++_shared',
'-DCMAKE_VERBOSE_MAKEFILE=ON',
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
'-DRELEASE_TYPE=' + RELEASE_TYPE,
'-DSTABLE_BUILD=' + STABLE_BUILD,
'-DDISABLE_QML=OFF',
'-DDISABLE_KTX_CACHE=OFF',
'-DUSE_BREAKPAD=OFF'
targets = ['questInterface']
}
}
signingConfigs {
release {
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : ''
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : ''
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : ''
v2SigningEnabled false
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
debug {
buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\""
buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\""
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\""
buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\""
}
}
externalNativeBuild {
cmake {
path '../../../CMakeLists.txt'
}
}
applicationVariants.all { variant ->
// Our asset contents depend on items produced in the CMake build
// so our merge has to depend on the external native build
variant.externalNativeBuildTasks.each { task ->
variant.mergeResources.dependsOn(task)
if (Os.isFamily(Os.FAMILY_UNIX)) {
// FIXME
def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first()
def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first()
def renameHifiACTask = rootProject.getTasksByName("renameHifiACTask${variant.name.capitalize()}", false).first()
runDumpSymsTask.dependsOn(task)
variant.assemble.dependsOn(uploadDumpSymsTask)
variant.mergeResources.dependsOn(renameHifiACTask)
}
}
variant.mergeAssets.doLast {
def assetList = new LinkedList<String>()
def youngestLastModified = 0
// Copy the compiled resources generated by the external native build
copy {
from new File(projectDir, "../../../interface/compiledResources")
into outputDir
duplicatesStrategy DuplicatesStrategy.INCLUDE
eachFile { details ->
youngestLastModified = Math.max(youngestLastModified, details.lastModified)
assetList.add(details.path)
}
}
// Copy the scripts directory
copy {
from new File(projectDir, "../../../scripts")
into new File(outputDir, "scripts")
duplicatesStrategy DuplicatesStrategy.INCLUDE
eachFile { details->
youngestLastModified = Math.max(youngestLastModified, details.lastModified)
assetList.add("scripts/" + details.path)
}
}
// Write a list of files to be unpacked to the cache folder
new File(outputDir, 'cache_assets.txt').withWriter { out ->
out.println(Long.toString(youngestLastModified))
assetList.each { file -> out.println(file) }
}
}
variant.outputs.all {
if (RELEASE_NUMBER != '0') {
outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk"
}
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
implementation project(':oculus')
implementation project(':qt')
}

View file

@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Android\SDK/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.highfidelity.questInterface"
android:installLocation="auto">
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
<uses-feature android:name="android.software.vr.mode" android:required="true"/>
<uses-feature android:name="android.hardware.vr.high_performance" android:required="true"/>
<application
android:name="org.qtproject.qt5.android.bindings.QtApplication"
android:label="@string/app_name"
android:allowBackup="true">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity
android:name=".PermissionsChecker"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.INFO" />
</intent-filter>
</activity>
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name=".InterfaceActivity"
android:label="@string/app_name"
android:launchMode="singleTask">
<meta-data android:name="android.app.lib_name" android:value="questInterface"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
<meta-data android:name="android.app.background_running" android:value="false"/>
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,106 @@
#include <functional>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QThread>
#include <QtCore/QStringList>
#include <QtCore/QStandardPaths>
#include <QtCore/QTextStream>
#include <QtCore/QObject>
#include <QtAndroidExtras/QAndroidJniObject>
#include <QtAndroidExtras/QtAndroid>
#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <shared/Storage.h>
#include <AddressManager.h>
#include <AndroidHelper.h>
#include <udt/PacketHeaders.h>
#include <OVR_Platform.h>
#include <OVR_Functions_Voip.h>
void initOculusPlatform(JNIEnv* env, jobject obj) {
static std::once_flag once;
std::call_once(once, [&]{
// static const char* appID = "2343652845669354";
// if (ovr_PlatformInitializeAndroid(appID, obj, env) != ovrPlatformInitialize_Success) {
// __android_log_write(ANDROID_LOG_WARN, "QQQ", "Failed to init platform SDK");
// return;
// }
// ovr_Voip_SetSystemVoipSuppressed(true);
});
}
void getClassName(JNIEnv *env, jobject obj){
jclass cls = env->GetObjectClass(obj);
jmethodID mid = env->GetMethodID(cls,"getClass", "()Ljava/lang/Class;");
jobject clsObj = env->CallObjectMethod(obj, mid);
cls= env->GetObjectClass(clsObj);
mid= env->GetMethodID(cls, "getName", "()Ljava/lang/String;");
jstring strObj = (jstring) env->CallObjectMethod(clsObj, mid);
const char* str = env->GetStringUTFChars(strObj, NULL);
__android_log_print(ANDROID_LOG_ERROR,__FUNCTION__, "Native Class call: %s",str);
env->ReleaseStringUTFChars(strObj, str);
}
extern "C" {
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_nativeInitOculusPlatform(JNIEnv *env, jobject obj){
initOculusPlatform(env, obj);
}
QAndroidJniObject __interfaceActivity;
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnCreate(JNIEnv *env, jobject obj) {
__android_log_print(ANDROID_LOG_INFO, "QQQ", __FUNCTION__);
initOculusPlatform(env, obj);
getClassName(env, obj);
__interfaceActivity = QAndroidJniObject(obj);
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() {
__interfaceActivity.callMethod<void>("onAppLoadedComplete", "()V");
QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete,
nullptr,
nullptr);
});
}
JNIEXPORT void Java_io_highfidelity_oculus_OculusMobileActivity_questOnAppAfterLoad(JNIEnv* env, jobject obj) {
AndroidHelper::instance().moveToThread(qApp->thread());
}
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnPause(JNIEnv *env, jobject obj) {
AndroidHelper::instance().notifyEnterBackground();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnResume(JNIEnv *env, jobject obj) {
AndroidHelper::instance().notifyEnterForeground();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_receiver_HeadsetStateReceiver_notifyHeadsetOn(JNIEnv *env,
jobject instance,
jboolean pluggedIn) {
AndroidHelper::instance().notifyHeadsetOn(pluggedIn);
}
}

View file

@ -0,0 +1,13 @@
package io.highfidelity.questInterface;
import android.os.Bundle;
import io.highfidelity.oculus.OculusMobileActivity;
import io.highfidelity.utils.HifiUtils;
public class InterfaceActivity extends OculusMobileActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
HifiUtils.upackAssets(getAssets(), getCacheDir().getAbsolutePath());
super.onCreate(savedInstanceState);
}
}

View file

@ -0,0 +1,68 @@
package io.highfidelity.questInterface;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import io.highfidelity.oculus.OculusMobileActivity;
import io.highfidelity.utils.HifiUtils;
public class PermissionsChecker extends Activity {
private static final int REQUEST_PERMISSIONS = 20;
private static final String TAG = PermissionsChecker.class.getName();
private static final String[] REQUIRED_PERMISSIONS = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestAppPermissions(REQUIRED_PERMISSIONS,REQUEST_PERMISSIONS);
}
public void requestAppPermissions(final String[] requestedPermissions,
final int requestCode) {
int permissionCheck = PackageManager.PERMISSION_GRANTED;
boolean shouldShowRequestPermissionRationale = false;
for (String permission : requestedPermissions) {
permissionCheck = permissionCheck + checkSelfPermission(permission);
shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || shouldShowRequestPermissionRationale(permission);
}
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
System.out.println("Permission was not granted. Ask for permissions");
if (shouldShowRequestPermissionRationale) {
requestPermissions(requestedPermissions, requestCode);
} else {
requestPermissions(requestedPermissions, requestCode);
}
} else {
System.out.println("Launching the other activity..");
launchActivityWithPermissions();
}
}
private void launchActivityWithPermissions() {
startActivity(new Intent(this, InterfaceActivity.class));
finish();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
int permissionCheck = PackageManager.PERMISSION_GRANTED;
for (int permission : grantResults) {
permissionCheck = permissionCheck + permission;
}
if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) {
launchActivityWithPermissions();
} else if (grantResults.length > 0) {
System.out.println("User has deliberately denied Permissions. Launching anyways");
launchActivityWithPermissions();
}
}
}

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress AndroidUnknownAttribute -->
<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="192"
android:viewportHeight="192"
android:width="192dp"
android:height="192dp">
<path
android:pathData="M189.5 96.5A93.5 93.5 0 0 1 96 190 93.5 93.5 0 0 1 2.5 96.5 93.5 93.5 0 0 1 96 3 93.5 93.5 0 0 1 189.5 96.5Z"
android:fillColor="#333333" />
<path
android:pathData="M96.2 173.1c-10.3 0 -20.4 -2.1 -29.8 -6 -9.2 -3.8 -17.3 -9.4 -24.3 -16.4 -7 -7 -12.6 -15.2 -16.4 -24.3 -4.1 -9.6 -6.2 -19.6 -6.2 -30 0 -10.3 2.1 -20.4 6 -29.8 3.8 -9.2 9.4 -17.3 16.4 -24.3 7 -7 15.2 -12.6 24.3 -16.4 9.5 -4 19.5 -6 29.8 -6 10.3 0 20.4 2.1 29.8 6 9.2 3.8 17.3 9.4 24.3 16.4 7 7 12.6 15.2 16.4 24.3 4 9.5 6 19.5 6 29.8 0 10.3 -2.1 20.4 -6 29.8 -3.8 9.2 -9.4 17.3 -16.4 24.3 -7 7 -15.2 12.6 -24.3 16.4 -9.2 4.1 -19.3 6.2 -29.6 6.2zm0 -145.3c-37.8 0 -68.6 30.8 -68.6 68.6 0 37.8 30.8 68.6 68.6 68.6 37.8 0 68.6 -30.8 68.6 -68.6 0 -37.8 -30.8 -68.6 -68.6 -68.6z"
android:fillColor="#00b4f0" />
<path
android:pathData="M119.6 129l0 -53.8c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 25L79 83.8 79 64c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 54.1c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.7 -2.4 -6.9 -5.8 -8l0 -27.3 35 16.3 0 22.2c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.8 -2.4 -6.9 -5.8 -8z"
android:fillColor="#00b4f0" />
</vector>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name" translatable="false">Interface</string>
</resources>

View file

@ -42,6 +42,8 @@ ext {
RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
STABLE_BUILD = project.hasProperty('STABLE_BUILD') ? project.getProperty('STABLE_BUILD') : '0'
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
appVersionCode = Integer.valueOf(VERSION_CODE ?: 1)
appVersionName = RELEASE_NUMBER ?: "1.0"
}
def appDir = new File(projectDir, 'apps/interface')

View file

@ -1 +1,2 @@
org.gradle.jvmargs=-Xms2g -Xmx4g
android.debug.obsoleteApi=true

View file

@ -1,6 +1,6 @@
#Sat Dec 01 08:32:47 PST 2018
#Wed Dec 19 13:46:46 PST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

View file

@ -15,3 +15,7 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation project(path: ':qt')
}

View file

@ -7,62 +7,65 @@
//
package io.highfidelity.oculus;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import org.qtproject.qt5.android.bindings.QtActivity;
import io.highfidelity.utils.HifiUtils;
/**
* Contains a native surface and forwards the activity lifecycle and surface lifecycle
* events to the OculusMobileDisplayPlugin
*/
public class OculusMobileActivity extends Activity implements SurfaceHolder.Callback {
public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Callback {
private static final String TAG = OculusMobileActivity.class.getSimpleName();
static { System.loadLibrary("oculusMobile"); }
private native void nativeOnCreate();
private native static void nativeOnResume();
private native static void nativeOnPause();
private native static void nativeOnDestroy();
private native static void nativeOnSurfaceChanged(Surface s);
private native void questNativeOnCreate();
private native void questNativeOnPause();
private native void questNativeOnResume();
private native void questOnAppAfterLoad();
private SurfaceView mView;
private SurfaceHolder mSurfaceHolder;
public static void launch(Activity activity) {
if (activity != null) {
activity.runOnUiThread(()->{
activity.startActivity(new Intent(activity, OculusMobileActivity.class));
});
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.w(TAG, "QQQ onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Log.w(TAG, "QQQ onCreate");
// Create a native surface for VR rendering (Qt GL surfaces are not suitable
// because of the lack of fine control over the surface callbacks)
// Forward the create message to the JNI code
mView = new SurfaceView(this);
setContentView(mView);
mView.getHolder().addCallback(this);
// Forward the create message to the JNI code
nativeOnCreate();
questNativeOnCreate();
}
public void onAppLoadedComplete() {
Log.w(TAG, "QQQ Load Completed");
runOnUiThread(() -> {
setContentView(mView);
questOnAppAfterLoad();
});
}
@Override
protected void onDestroy() {
Log.w(TAG, "QQQ onDestroy");
if (mSurfaceHolder != null) {
nativeOnSurfaceChanged(null);
}
nativeOnDestroy();
nativeOnSurfaceChanged(null);
Log.w(TAG, "QQQ onDestroy -- SUPER onDestroy");
super.onDestroy();
}
@ -70,19 +73,38 @@ public class OculusMobileActivity extends Activity implements SurfaceHolder.Call
protected void onResume() {
Log.w(TAG, "QQQ onResume");
super.onResume();
//Reconnect the global reference back to handler
nativeOnCreate();
questNativeOnResume();
nativeOnResume();
}
@Override
protected void onPause() {
Log.w(TAG, "QQQ onPause");
nativeOnPause();
super.onPause();
questNativeOnPause();
nativeOnPause();
}
@Override
protected void onStop(){
super.onStop();
Log.w(TAG, "QQQ Onstop called");
}
@Override
protected void onRestart(){
super.onRestart();
Log.w(TAG, "QQQ onRestart called ****");
questOnAppAfterLoad();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceCreated");
Log.w(TAG, "QQQ surfaceCreated ************************************");
nativeOnSurfaceChanged(holder.getSurface());
mSurfaceHolder = holder;
}
@ -96,8 +118,9 @@ public class OculusMobileActivity extends Activity implements SurfaceHolder.Call
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceDestroyed");
Log.w(TAG, "QQQ surfaceDestroyed ***************************************************");
nativeOnSurfaceChanged(null);
mSurfaceHolder = null;
}
}

View file

@ -70,9 +70,6 @@ public class QtActivity extends Activity {
public final String QT_ANDROID_DEFAULT_THEME = QT_ANDROID_THEMES[0]; // sets the default theme.
private QtActivityLoader m_loader = new QtActivityLoader(this);
public boolean isLoading;
public boolean keepInterfaceRunning;
public QtActivity() {
}
@ -229,10 +226,13 @@ public class QtActivity extends Activity {
//---------------------------------------------------------------------------
protected void onCreateHook(Bundle savedInstanceState) {
m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS;
m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES;
m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES;
m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME;
m_loader.onCreate(savedInstanceState);
}
@ -364,7 +364,10 @@ public class QtActivity extends Activity {
@Override
protected void onDestroy() {
super.onDestroy();
QtApplication.invokeDelegate();
QtNative.terminateQt();
QtNative.setActivity(null,null);
System.exit(0);
}
//---------------------------------------------------------------------------
@ -506,9 +509,9 @@ public class QtActivity extends Activity {
super.onPause();
// GC: this trick allow us to show a splash activity until Qt app finishes
// loading
if (!isLoading && !keepInterfaceRunning) {
QtApplication.invokeDelegate();
}
//QtApplication.invokeDelegate();
//TODO(Amer): looking into why this messes up pause.
}
//---------------------------------------------------------------------------
@ -647,11 +650,7 @@ public class QtActivity extends Activity {
@Override
protected void onStop() {
super.onStop();
if (!keepInterfaceRunning) {
QtApplication.invokeDelegate();
}
QtNative.terminateQt();
QtNative.setActivity(null,null);
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------

View file

@ -12,15 +12,26 @@ project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
// Applications
//
include ':interface'
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
if (!getSettings().hasProperty("SUPPRESS_INTERFACE")) {
include ':interface'
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
}
if (!getSettings().hasProperty("SUPPRESS_QUEST_INTERFACE")) {
include ':questInterface'
project(':questInterface').projectDir = new File(settingsDir, 'apps/questInterface')
}
//
// Test projects
//
include ':framePlayer'
project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
if (!getSettings().hasProperty("SUPPRESS_FRAME_PLAYER")) {
include ':framePlayer'
project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
}
include ':questFramePlayer'
project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')
if (!getSettings().hasProperty("SUPPRESS_QUEST_FRAME_PLAYER")) {
include ':questFramePlayer'
project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')
}

View file

@ -270,6 +270,16 @@ macro(AUTOSCRIBE_SHADER_LIBS)
set(AUTOSCRIBE_SHADERGEN_COMMANDS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shadergen.txt)
file(WRITE ${AUTOSCRIBE_SHADERGEN_COMMANDS_FILE} "${AUTOSCRIBE_SHADERGEN_COMMANDS}")
if (HIFI_ANDROID)
if (
(${HIFI_ANDROID_APP} STREQUAL "questInterface") OR
(${HIFI_ANDROID_APP} STREQUAL "questFramePlayer") OR
(${HIFI_ANDROID_APP} STREQUAL "framePlayer")
)
set(EXTRA_SHADERGEN_ARGS --extensions EXT_clip_cull_distance)
endif()
endif()
# A custom python script which will generate all our shader artifacts
add_custom_command(
OUTPUT ${SCRIBED_SHADERS} ${SPIRV_SHADERS} ${REFLECTED_SHADERS}
@ -279,6 +289,7 @@ macro(AUTOSCRIBE_SHADER_LIBS)
--tools-dir ${VCPKG_TOOLS_DIR}
--build-dir ${CMAKE_CURRENT_BINARY_DIR}
--source-dir ${CMAKE_SOURCE_DIR}
${EXTRA_SHADERGEN_ARGS}
DEPENDS ${AUTOSCRIBE_SHADER_HEADERS} ${CMAKE_SOURCE_DIR}/tools/shadergen.py ${ALL_SCRIBE_SHADERS})
add_custom_target(shadergen DEPENDS ${SCRIBED_SHADERS} ${SPIRV_SHADERS} ${REFLECTED_SHADERS})

View file

@ -53,9 +53,9 @@ ANDROID_PACKAGES = {
'includeLibs': ['libvrapi.so']
},
'oculusPlatform': {
'file': 'OVRPlatformSDK_v1.32.0.zip',
'versionId': 'jG9DB16zOGxSrmtZy4jcQnwO0TJUuaeL',
'checksum': 'ab5b203b3a39a56ab148d68fff769e05',
'file': 'OVRPlatformSDK_v1.34.0.zip',
'versionId': 'vbRUkkyzUAXfTGSEtuiUr_7.Fm5h5BZk',
'checksum': '16e4c5f39520f122bc49cb6d5bb88289',
'sharedLibFolder': 'Android/libs/arm64-v8a',
'includeLibs': ['libovrplatformloader.so']
},

View file

@ -425,11 +425,12 @@ FocusScope {
console.warn("Could not find top level window for " + item);
return;
}
/*
if (typeof Controller === "undefined") {
console.warn("Controller not yet available... can't center");
return;
}
*/
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect();
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
@ -455,15 +456,17 @@ FocusScope {
console.warn("Could not find top level window for " + item);
return;
}
/*
if (typeof Controller === "undefined") {
console.warn("Controller not yet available... can't reposition targetWindow:" + targetWindow);
return;
}
*/
var oldRecommendedRect = recommendedRect;
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
var newRecommendedRect = Controller.getRecommendedHUDRect();
var newRecommendedRect = { width: 1280, height: 720, x: 0, y: 0 };
if (typeof Controller !== "undefined") newRecommendedRect = Controller.getRecommendedHUDRect();
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
repositionWindow(targetWindow, false, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
}
@ -480,7 +483,8 @@ FocusScope {
return;
}
var recommended = Controller.getRecommendedHUDRect();
var recommended = { width: 1280, height: 720, x: 0, y: 0 };
if (typeof Controller !== "undefined") recommended = Controller.getRecommendedHUDRect();
var maxX = recommended.x + recommended.width;
var maxY = recommended.y + recommended.height;
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);

View file

@ -18,8 +18,8 @@ OriginalDesktop.Desktop {
hoverEnabled: true
propagateComposedEvents: true
scrollGestureEnabled: false // we don't need/want these
onEntered: ApplicationCompositor.reticleOverDesktop = true
onExited: ApplicationCompositor.reticleOverDesktop = false
onEntered: if (typeof ApplicationCompositor !== "undefined") ApplicationCompositor.reticleOverDesktop = true
onExited: if (typeof ApplicationCompositor !== "undefined") ApplicationCompositor.reticleOverDesktop = false
acceptedButtons: Qt.NoButton
}

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include <chrono>
@ -37,7 +38,6 @@
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlEngine>
#include <QtQuick/QQuickWindow>
@ -51,6 +51,7 @@
#include <QProcessEnvironment>
#include <QTemporaryDir>
#include <gl/QOpenGLContextWrapper.h>
#include <gl/GLWindow.h>
#include <gl/GLHelpers.h>
@ -191,6 +192,9 @@
#include "scripting/WalletScriptingInterface.h"
#include "scripting/TTSScriptingInterface.h"
#include "scripting/KeyboardScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
@ -239,6 +243,7 @@
#include "webbrowser/WebBrowserSuggestionsEngine.h"
#include <DesktopPreviewProvider.h>
#include "AboutUtil.h"
#if defined(Q_OS_WIN)
@ -1745,7 +1750,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
_applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float {
#if defined(Q_OS_ANDROID)
return 1;
return 1 ;
#else
return 0;
#endif
@ -1848,6 +1853,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
this->installEventFilter(this);
#ifdef HAVE_DDE
auto ddeTracker = DependencyManager::get<DdeFaceTracker>();
ddeTracker->init();
@ -3085,7 +3092,7 @@ void Application::initializeUi() {
}
if (TouchscreenVirtualPadDevice::NAME == inputPlugin->getName()) {
_touchscreenVirtualPadDevice = std::dynamic_pointer_cast<TouchscreenVirtualPadDevice>(inputPlugin);
#if defined(Q_OS_ANDROID)
#if defined(ANDROID_APP_INTERFACE)
auto& virtualPadManager = VirtualPad::Manager::instance();
connect(&virtualPadManager, &VirtualPad::Manager::hapticFeedbackRequested,
this, [](int duration) {
@ -3617,10 +3624,14 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
}
// Get controller availability
#ifdef ANDROID_APP_QUEST_INTERFACE
bool hasHandControllers = true;
#else
bool hasHandControllers = false;
if (PluginUtils::isViveControllerAvailable() || PluginUtils::isOculusTouchControllerAvailable()) {
hasHandControllers = true;
}
#endif
// Check HMD use (may be technically available without being in use)
bool hasHMD = PluginUtils::isHMDAvailable();
@ -8237,6 +8248,7 @@ void Application::loadDomainConnectionDialog() {
}
void Application::toggleLogDialog() {
#ifndef ANDROID_APP_QUEST_INTERFACE
if (getLoginDialogPoppedUp()) {
return;
}
@ -8260,6 +8272,7 @@ void Application::toggleLogDialog() {
} else {
_logDialog->show();
}
#endif
}
void Application::recreateLogWindow(int keepOnTop) {
@ -9125,17 +9138,23 @@ void Application::beforeEnterBackground() {
void Application::enterBackground() {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection);
// Quest only supports one plugin which can't be deactivated currently
#if !defined(ANDROID_APP_QUEST_INTERFACE)
if (getActiveDisplayPlugin()->isActive()) {
getActiveDisplayPlugin()->deactivate();
}
#endif
}
void Application::enterForeground() {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"start", Qt::BlockingQueuedConnection);
// Quest only supports one plugin which can't be deactivated currently
#if !defined(ANDROID_APP_QUEST_INTERFACE)
if (!getActiveDisplayPlugin() || getActiveDisplayPlugin()->isActive() || !getActiveDisplayPlugin()->activate()) {
qWarning() << "Could not re-activate display plugin";
}
#endif
auto nodeList = DependencyManager::get<NodeList>();
nodeList->setSendDomainServerCheckInEnabled(true);
}

View file

@ -388,10 +388,23 @@ ShaderPointer Deserializer::readShader(const json& node) {
return nullptr;
}
static std::map<std::string, uint32_t> shadersIdsByName;
if (shadersIdsByName.empty()) {
for (const auto id : shader::allShaders()) {
const auto& shaderSource = shader::Source::get(id);
shadersIdsByName[shaderSource.name] = id;
}
}
// FIXME support procedural shaders
Shader::Type type = node[keys::type];
std::string name = node[keys::name];
uint32_t id = node[keys::id];
// Using the serialized ID is bad, because it's generated at
// cmake time, and can change across platforms or when
// shaders are added or removed
// uint32_t id = node[keys::id];
uint32_t id = shadersIdsByName[name];
ShaderPointer result;
switch (type) {
//case Shader::Type::GEOMETRY:

View file

@ -167,7 +167,7 @@ TransformObject getTransformObject() {
vec4 eyeClipEdge[2]= vec4[2](vec4(-1,0,0,1), vec4(1,0,0,1));
vec2 eyeOffsetScale = vec2(-0.5, +0.5);
uint eyeIndex = uint(_stereoSide);
#ifndef GPU_GLES
#if !defined(GPU_GLES) || (defined(HAVE_EXT_clip_cull_distance) && !defined(VULKAN))
gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]);
#endif
float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w;

View file

@ -315,10 +315,6 @@ JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOn
SURFACE.onCreate(env, obj);
}
JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnDestroy(JNIEnv*, jclass) {
__android_log_write(ANDROID_LOG_WARN, "QQQ_JNI", __FUNCTION__);
}
JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnResume(JNIEnv*, jclass) {
__android_log_write(ANDROID_LOG_WARN, "QQQ_JNI", __FUNCTION__);
SURFACE.setResumed(true);

View file

@ -0,0 +1,7 @@
layout(location=0) in vec4 _color;
layout(location=0) out vec4 _fragColor0;
void main(void) {
_fragColor0 = _color;
}

View file

@ -1,6 +1,3 @@
#version 310 es
#define GPU_GLES
#define GPU_GLES_310
#define BITFIELD highp int
#define LAYOUT(X) layout(X)
#define LAYOUT_STD140(X) layout(std140, X)
@ -9,6 +6,9 @@
#define gl_VertexID gl_VertexIndex
#endif
#extension GL_EXT_texture_buffer : enable
#if defined(HAVE_EXT_clip_cull_distance) && !defined(VULKAN)
#extension GL_EXT_clip_cull_distance : enable
#endif
precision highp float;
precision highp samplerBuffer;
precision highp sampler2DShadow;

View file

@ -0,0 +1,3 @@
#version 310 es
#define GPU_GLES
#define GPU_GLES_310

View file

@ -1,5 +1,3 @@
#version 410 core
#define GPU_GL410
#define BITFIELD int
#if defined(VULKAN)
#extension GL_ARB_shading_language_420pack : require

View file

@ -0,0 +1,2 @@
#version 410 core
#define GPU_GL410

View file

@ -1,5 +1,3 @@
#version 450 core
#define GPU_GL450
#define GPU_SSBO_TRANSFORM_OBJECT
#define BITFIELD int
#define LAYOUT(X) layout(X)

View file

@ -0,0 +1,2 @@
#version 450 core
#define GPU_GL450

View file

@ -224,7 +224,7 @@ String Source::getSource(Dialect dialect, Variant variant) const {
}
}
#ifdef Q_OS_ANDROID
#if defined(Q_OS_ANDROID) || defined(USE_GLES)
// SPIRV cross injects "#extension GL_OES_texture_buffer : require" into the GLSL shaders,
// which breaks android rendering
return variantSource.scribe;

View file

@ -24,7 +24,7 @@ QmlFragmentClass::QmlFragmentClass(bool restricted, QString id) : QmlWindowClass
// Method called by Qt scripts to create a new bottom menu bar in Android
QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
#ifndef DISABLE_QML
std::lock_guard<std::mutex> guard(_mutex);
auto qml = context->argument(0).toVariant().toMap().value("qml");
if (qml.isValid()) {
@ -53,6 +53,9 @@ QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QSc
QScriptValue scriptObject = engine->newQObject(retVal);
_fragments[qml.toString()] = scriptObject;
return scriptObject;
#else
return QScriptValue();
#endif
}
void QmlFragmentClass::close() {
@ -61,6 +64,7 @@ void QmlFragmentClass::close() {
}
QObject* QmlFragmentClass::addButton(const QVariant& properties) {
#ifndef DISABLE_QML
QVariant resultVar;
Qt::ConnectionType connectionType = Qt::AutoConnection;
@ -79,8 +83,10 @@ QObject* QmlFragmentClass::addButton(const QVariant& properties) {
qWarning() << "QmlFragmentClass addButton result not a QObject";
return NULL;
}
return qmlButton;
#else
return nullptr;
#endif
}
void QmlFragmentClass::removeButton(QObject* button) {

View file

@ -98,6 +98,7 @@ QmlWindowClass::QmlWindowClass(bool restricted) : _restricted(restricted) {
* @property {boolean} visible
*/
void QmlWindowClass::initQml(QVariantMap properties) {
#ifndef DISABLE_QML
auto offscreenUi = DependencyManager::get<OffscreenUi>();
_source = properties[SOURCE_PROPERTY].toString();
@ -150,6 +151,7 @@ void QmlWindowClass::initQml(QVariantMap properties) {
Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
#endif
}
void QmlWindowClass::qmlToScript(const QVariant& message) {

View file

@ -58,6 +58,9 @@ logging.setLoggerClass(TrackableLogger)
logger = logging.getLogger('prebuild')
def headSha():
if shutil.which('git') is None:
logger.warn("Unable to find git executable, can't caclulate commit ID")
return '0xDEADBEEF'
repo_dir = os.path.dirname(os.path.abspath(__file__))
git = subprocess.Popen(
'git rev-parse --short HEAD',
@ -67,7 +70,7 @@ def headSha():
stdout, _ = git.communicate()
sha = stdout.split('\n')[0]
if not sha:
raise RuntimeError("couldn't find git sha")
raise RuntimeError("couldn't find git sha for repository {}".format(repo_dir))
return sha
@contextmanager

View file

@ -49,11 +49,36 @@ def getCommonScribeArgs(scribefile, includeLibs):
scribeArgs.append(scribefile)
return scribeArgs
def getDialectAndVariantHeaders(dialect, variant):
extensionsHeaderMutex = Lock()
def getExtensionsHeader(dialect, variant, extensions):
extensionHeader = '{}/extensions_{}_{}.glsl'.format(args.build_dir, dialect, variant)
global extensionsHeaderMutex
extensionsHeaderMutex.acquire()
if not os.path.exists(extensionHeader):
extensionsDefines = []
for extension in extensions:
extensionsDefines.append('#define HAVE_{}'.format(extension))
# make sure we end with a line feed
extensionsDefines.append("\r\n")
with open(extensionHeader, "w") as f:
f.write('\r\n'.join(extensionsDefines))
extensionsHeaderMutex.release()
return extensionHeader
def getDialectAndVariantHeaders(dialect, variant, extensions=None):
result = []
headerPath = args.source_dir + '/libraries/shaders/headers/'
variantHeader = headerPath + ('stereo.glsl' if (variant == 'stereo') else 'mono.glsl')
versionHeader = headerPath + dialect + '/version.glsl'
result.append(versionHeader)
if extensions is not None:
result.append(getExtensionsHeader(dialect, variant, extensions))
dialectHeader = headerPath + dialect + '/header.glsl'
return [dialectHeader, variantHeader]
result.append(dialectHeader)
variantHeader = headerPath + ('stereo.glsl' if (variant == 'stereo') else 'mono.glsl')
result.append(variantHeader)
return result
class ScribeDependenciesCache:
cache = {}
@ -170,7 +195,7 @@ def processCommand(line):
scribeDepCache.gen(scribeFile, libs, dialect, variant)
scribeArgs = getCommonScribeArgs(scribeFile, libs)
for header in getDialectAndVariantHeaders(dialect, variant):
for header in getDialectAndVariantHeaders(dialect, variant, args.extensions):
scribeArgs.extend(['-H', header])
scribeArgs.extend(['-o', unoptGlslFile])
executeSubprocess(scribeArgs)
@ -218,6 +243,7 @@ def main():
parser = ArgumentParser(description='Generate shader artifacts.')
parser.add_argument('--extensions', type=str, nargs='*', help='Available extensions for the shaders')
parser.add_argument('--commands', type=argparse.FileType('r'), help='list of commands to execute')
parser.add_argument('--tools-dir', type=str, help='location of the host compatible binaries')
parser.add_argument('--build-dir', type=str, help='The build directory base path')
@ -230,8 +256,8 @@ args = None
if len(sys.argv) == 1:
# for debugging
sourceDir = expanduser('~/git/hifi')
toolsDir = os.path.join(expanduser('~/git/vcpkg'), 'installed', 'x64-windows', 'tools')
buildPath = sourceDir + '/build'
toolsDir = 'd:/hifi/vcpkg/android/fd82f0a8/installed/x64-windows/tools'
buildPath = sourceDir + '/build_android'
commandsPath = buildPath + '/libraries/shaders/shadergen.txt'
shaderDir = buildPath + '/libraries/shaders'
testArgs = '--commands {} --tools-dir {} --build-dir {} --source-dir {}'.format(
@ -239,6 +265,7 @@ if len(sys.argv) == 1:
).split()
testArgs.append('--debug')
testArgs.append('--force')
testArgs.extend('--extensions EXT_clip_cull_distance'.split())
#testArgs.append('--dry-run')
args = parser.parse_args(testArgs)
else: