Merge remote-tracking branch 'upstream/master' into NOverlaysFinal

This commit is contained in:
SamGondelman 2019-02-07 09:49:27 -08:00
commit e74ff18bef
168 changed files with 5991 additions and 1776 deletions

View file

@ -80,6 +80,7 @@ endif()
if (ANDROID)
set(GLES_OPTION ON)
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
else ()
set(PLATFORM_QT_COMPONENTS WebEngine)
endif ()

View file

@ -3,10 +3,10 @@ apply plugin: 'com.android.application'
android {
signingConfigs {
release {
keyAlias 'key0'
keyPassword 'password'
storeFile file('C:/android/keystore.jks')
storePassword 'password'
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 : ''
}
}

View file

@ -1,42 +0,0 @@
//
// InterfaceActivity.java
// gvr-interface/java
//
// Created by Stephen Birarda on 1/26/15.
// Copyright 2015 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.gvrinterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtActivity;
public class InterfaceActivity extends QtActivity {
public static native void handleHifiURL(String hifiURLString);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Get the intent that started this activity in case we have a hifi:// URL to parse
Intent intent = getIntent();
if (intent.getAction() == Intent.ACTION_VIEW) {
Uri data = intent.getData();
if (data.getScheme().equals("hifi")) {
handleHifiURL(data.toString());
}
}
}
}

View file

@ -31,6 +31,7 @@ import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.SlidingDrawer;
import org.qtproject.qt5.android.QtNative;
import org.qtproject.qt5.android.QtLayout;
import org.qtproject.qt5.android.QtSurface;
import org.qtproject.qt5.android.bindings.QtActivity;
@ -166,8 +167,27 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
@Override
protected void onDestroy() {
super.onDestroy();
nativeOnDestroy();
/*
cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly
After Qt upgrade to 5.11 we had a black screen crash after closing the application with
the hardware button "Back" and trying to start the app again. It could only be fixed after
totally closing the app swiping it in the list of running apps.
This problem did not happen with the previous Qt version.
After analysing changes we came up with this case and change:
https://codereview.qt-project.org/#/c/218882/
In summary they've moved libs loading to the same thread as main() and as a matter of correctness
in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();`
That exit call is the main reason of this problem.
In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the
entire onDestroy method including that thread exit line for other three lines that purposely
terminate qt (borrowed from QtActivityDelegate::onDestroy as well).
*/
QtNative.terminateQt();
QtNative.setActivity(null, null);
System.exit(0);
super.onDestroy();
}
@Override

View file

@ -0,0 +1,9 @@
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

@ -0,0 +1,51 @@
apply plugin: 'com.android.application'
android {
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 : ''
}
}
compileSdkVersion 28
defaultConfig {
applicationId "io.highfidelity.frameplayer"
minSdkVersion 25
targetSdkVersion 28
ndk { abiFilters 'arm64-v8a' }
externalNativeBuild {
cmake {
arguments '-DHIFI_ANDROID=1',
'-DHIFI_ANDROID_APP=questFramePlayer',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_STL=c++_shared',
'-DCMAKE_VERBOSE_MAKEFILE=ON'
targets = ['questFramePlayer']
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
}
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,55 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.highfidelity.frameplayer"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto">
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<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:label="Frame Viewer"
android:allowBackup="false"
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"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
android:name=".QuestRenderActivity"
android:label="Frame Player"
android:launchMode="singleInstance"
android:screenOrientation="landscape"
android:excludeFromRecents="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,25 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 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 "PlayerWindow.h"
#include <QtWidgets/QFileDialog>
PlayerWindow::PlayerWindow() {
installEventFilter(this);
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
setSurfaceType(QSurface::OpenGLSurface);
create();
showFullScreen();
// Ensure the window is visible and the GL context is valid
QCoreApplication::processEvents();
_renderThread.initialize(this);
}
PlayerWindow::~PlayerWindow() {
}

View file

@ -0,0 +1,29 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 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
//
#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;
private:
QSettings _settings;
RenderThread _renderThread;
};

View file

@ -0,0 +1,240 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 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 "RenderThread.h"
#include <mutex>
#include <jni.h>
#include <android/log.h>
#include <QtCore/QFileInfo>
#include <QtGui/QWindow>
#include <QtGui/QImageReader>
#include <gl/QOpenGLContextWrapper.h>
#include <gpu/FrameIO.h>
#include <gpu/Texture.h>
#include <VrApi_Types.h>
#include <VrApi_Helpers.h>
#include <ovr/VrHandler.h>
#include <ovr/Helpers.h>
#include <VrApi.h>
#include <VrApi_Input.h>
static JNIEnv* _env { nullptr };
static JavaVM* _vm { nullptr };
static jobject _activity { nullptr };
struct HandController{
ovrInputTrackedRemoteCapabilities caps {};
ovrInputStateTrackedRemote state {};
ovrResult stateResult{ ovrSuccess };
ovrTracking tracking {};
ovrResult trackingResult{ ovrSuccess };
void update(ovrMobile* session, double time = 0.0) {
const auto& deviceId = caps.Header.DeviceID;
stateResult = vrapi_GetCurrentInputState(session, deviceId, &state.Header);
trackingResult = vrapi_GetInputTrackingState(session, deviceId, 0.0, &tracking);
}
};
std::vector<HandController> devices;
extern "C" {
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) {
__android_log_write(ANDROID_LOG_WARN, "QQQ", __FUNCTION__);
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL Java_io_highfidelity_frameplayer_QuestQtActivity_nativeOnCreate(JNIEnv* env, jobject obj) {
env->GetJavaVM(&_vm);
_activity = env->NewGlobalRef(obj);
}
}
static const char* FRAME_FILE = "assets:/frames/20190121_1220.json";
static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) {
QImage image;
QImageReader(filename.c_str()).read(&image);
if (layer > 0) {
return;
}
texture->assignStoredMip(0, image.byteCount(), image.constBits());
}
void RenderThread::submitFrame(const gpu::FramePointer& frame) {
std::unique_lock<std::mutex> lock(_frameLock);
_pendingFrames.push(frame);
}
void RenderThread::move(const glm::vec3& v) {
std::unique_lock<std::mutex> lock(_frameLock);
_correction = glm::inverse(glm::translate(mat4(), v)) * _correction;
}
void RenderThread::initialize(QWindow* window) {
std::unique_lock<std::mutex> lock(_frameLock);
setObjectName("RenderThread");
Parent::initialize();
_window = window;
_thread->setObjectName("RenderThread");
}
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();
// GPU library init
gpu::Context::init<gpu::gl::GLBackend>();
_gpuContext = std::make_shared<gpu::Context>();
_backend = _gpuContext->getBackend();
_gpuContext->beginFrame();
_gpuContext->endFrame();
makeCurrent();
glGenTextures(1, &_externalTexture);
glBindTexture(GL_TEXTURE_2D, _externalTexture);
static const glm::u8vec4 color{ 0,1,0,0 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);
if (QFileInfo(FRAME_FILE).exists()) {
auto frame = gpu::readFrame(FRAME_FILE, _externalTexture, &textureLoader);
submitFrame(frame);
}
}
void RenderThread::shutdown() {
_activeFrame.reset();
while (!_pendingFrames.empty()) {
_gpuContext->consumeFrameUpdates(_pendingFrames.front());
_pendingFrames.pop();
}
_gpuContext->shutdown();
_gpuContext.reset();
}
void RenderThread::handleInput() {
static std::once_flag once;
std::call_once(once, [&]{
withOvrMobile([&](ovrMobile* session){
int deviceIndex = 0;
ovrInputCapabilityHeader capsHeader;
while (vrapi_EnumerateInputDevices(session, deviceIndex, &capsHeader) >= 0) {
if (capsHeader.Type == ovrControllerType_TrackedRemote) {
HandController controller = {};
controller.caps.Header = capsHeader;
controller.state.Header.ControllerType = ovrControllerType_TrackedRemote;
vrapi_GetInputDeviceCapabilities( session, &controller.caps.Header);
devices.push_back(controller);
}
++deviceIndex;
}
});
});
auto readResult = ovr::VrHandler::withOvrMobile([&](ovrMobile *session) {
for (auto &controller : devices) {
controller.update(session);
}
});
if (readResult) {
for (auto &controller : devices) {
const auto &caps = controller.caps;
if (controller.stateResult >= 0) {
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 {
translation = glm::vec3{remote.Joystick.x, 0.0f, -remote.Joystick.y};
}
float scale = 0.1f + (1.9f * remote.GripTrigger);
_correction = glm::translate(glm::mat4(), translation * scale) * _correction;
}
}
}
}
}
void RenderThread::renderFrame() {
GLuint finalTexture = 0;
glm::uvec2 finalTextureSize;
const auto& tracking = beginFrame();
if (_activeFrame) {
const auto& frame = _activeFrame;
auto& eyeProjections = frame->stereoState._eyeProjections;
auto& eyeOffsets = frame->stereoState._eyeViews;
// Quest
auto frameCorrection = _correction * ovr::toGlm(tracking.HeadPose.Pose);
_backend->setCameraCorrection(glm::inverse(frameCorrection), frame->view);
ovr::for_each_eye([&](ovrEye eye){
const auto& eyeInfo = tracking.Eye[eye];
eyeProjections[eye] = ovr::toGlm(eyeInfo.ProjectionMatrix);
eyeOffsets[eye] = ovr::toGlm(eyeInfo.ViewMatrix);
});
_backend->recycle();
_backend->syncCache();
_gpuContext->enableStereo(true);
if (frame && !frame->batches.empty()) {
_gpuContext->executeFrame(frame);
}
auto& glbackend = (gpu::gl::GLBackend&)(*_backend);
finalTextureSize = { frame->framebuffer->getWidth(), frame->framebuffer->getHeight() };
finalTexture = glbackend.getTextureID(frame->framebuffer->getRenderBuffer(0));
}
presentFrame(finalTexture, finalTextureSize, tracking);
}
bool RenderThread::process() {
pollTask();
if (!vrActive()) {
QThread::msleep(1);
return true;
}
std::queue<gpu::FramePointer> pendingFrames;
{
std::unique_lock<std::mutex> lock(_frameLock);
pendingFrames.swap(_pendingFrames);
}
makeCurrent();
while (!pendingFrames.empty()) {
_activeFrame = pendingFrames.front();
pendingFrames.pop();
_gpuContext->consumeFrameUpdates(_activeFrame);
_activeFrame->stereoState._enable = true;
}
handleInput();
renderFrame();
return true;
}

View file

@ -0,0 +1,44 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 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
//
#pragma once
#include <QtCore/QElapsedTimer>
#include <GenericThread.h>
#include <shared/RateCounter.h>
#include <gl/Config.h>
#include <gl/Context.h>
#include <gpu/gl/GLBackend.h>
#include <ovr/VrHandler.h>
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;
uint32_t _externalTexture{ 0 };
glm::mat4 _correction;
void move(const glm::vec3& v);
void setup() override;
bool process() override;
void shutdown() override;
void handleInput();
void submitFrame(const gpu::FramePointer& frame);
void initialize(QWindow* window);
void renderFrame();
};

View file

@ -0,0 +1,56 @@
//
// Created by Bradley Austin Davis on 2018/11/22
// Copyright 2014 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 <android/log.h>
#include <QtGui/QGuiApplication>
#include <QtCore/QTimer>
#include <QtCore/QFileInfo>
#include <Trace.h>
#include "PlayerWindow.h"
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
const char * local=message.toStdString().c_str();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO,"Interface",local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN,"Interface",local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR,"Interface",local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
abort();
}
}
}
int main(int argc, char** argv) {
setupHifiApplication("gpuFramePlayer");
QGuiApplication app(argc, argv);
auto oldMessageHandler = qInstallMessageHandler(messageHandler);
DependencyManager::set<tracing::Tracer>();
PlayerWindow window;
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec");
app.exec();
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec done");
qInstallMessageHandler(oldMessageHandler);
return 0;
}

View file

@ -0,0 +1,53 @@
//
// 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

@ -0,0 +1,14 @@
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,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">GPU Frame Player</string>
</resources>

View file

@ -73,13 +73,10 @@ RUN mkdir "$HIFI_BASE" && \
RUN git clone https://github.com/jherico/hifi.git && \
cd ~/hifi && \
git checkout feature/quest_move_interface
git checkout feature/quest_frame_player
WORKDIR /home/jenkins/hifi
RUN touch .test6 && \
git fetch && git reset origin/feature/quest_move_interface --hard
RUN mkdir build
# Pre-cache the vcpkg managed dependencies

View file

@ -0,0 +1,17 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.shared.oculus"/>

View file

@ -0,0 +1,103 @@
//
// 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.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;
/**
* Contains a native surface and forwards the activity lifecycle and surface lifecycle
* events to the OculusMobileDisplayPlugin
*/
public class OculusMobileActivity extends Activity 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 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);
// Create a native surface for VR rendering (Qt GL surfaces are not suitable
// because of the lack of fine control over the surface callbacks)
mView = new SurfaceView(this);
setContentView(mView);
mView.getHolder().addCallback(this);
// Forward the create message to the JNI code
nativeOnCreate();
}
@Override
protected void onDestroy() {
Log.w(TAG, "QQQ onDestroy");
if (mSurfaceHolder != null) {
nativeOnSurfaceChanged(null);
}
nativeOnDestroy();
super.onDestroy();
}
@Override
protected void onResume() {
Log.w(TAG, "QQQ onResume");
super.onResume();
nativeOnResume();
}
@Override
protected void onPause() {
Log.w(TAG, "QQQ onPause");
nativeOnPause();
super.onPause();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceCreated");
nativeOnSurfaceChanged(holder.getSurface());
mSurfaceHolder = holder;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.w(TAG, "QQQ surfaceChanged");
nativeOnSurfaceChanged(holder.getSurface());
mSurfaceHolder = holder;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceDestroyed");
nativeOnSurfaceChanged(null);
mSurfaceHolder = null;
}
}

View file

@ -364,25 +364,7 @@ public class QtActivity extends Activity {
@Override
protected void onDestroy() {
super.onDestroy();
/*
cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly
After Qt upgrade to 5.11 we had a black screen crash after closing the application with
the hardware button "Back" and trying to start the app again. It could only be fixed after
totally closing the app swiping it in the list of running apps.
This problem did not happen with the previous Qt version.
After analysing changes we came up with this case and change:
https://codereview.qt-project.org/#/c/218882/
In summary they've moved libs loading to the same thread as main() and as a matter of correctness
in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();`
That exit call is the main reason of this problem.
In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the
entire onDestroy method including that thread exit line for other three lines that purposely
terminate qt (borrowed from QtActivityDelegate::onDestroy as well).
*/
QtNative.terminateQt();
QtNative.setActivity(null, null);
System.exit(0);
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------

View file

@ -1,8 +1,26 @@
//
// Libraries
//
include ':oculus'
project(':oculus').projectDir = new File(settingsDir, 'libraries/oculus')
include ':qt'
project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
//
// Applications
//
include ':interface'
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
//include ':framePlayer'
//project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
//
// Test projects
//
include ':framePlayer'
project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
include ':questFramePlayer'
project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')

View file

@ -10,5 +10,5 @@
#
macro(include_hifi_library_headers LIBRARY)
include_directories("${HIFI_LIBRARY_DIR}/${LIBRARY}/src")
target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${LIBRARY}/src")
endmacro(include_hifi_library_headers _library _root_dir)

View file

@ -19,8 +19,8 @@ function(LINK_HIFI_LIBRARIES)
endforeach()
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
# link the actual library - it is static so don't bubble it up
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
endforeach()

View file

@ -0,0 +1,4 @@
macro(target_egl)
find_library(EGL EGL)
target_link_libraries(${TARGET_NAME} ${EGL})
endmacro()

View file

@ -0,0 +1,20 @@
macro(target_oculus_mobile)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculus/VrApi)
# Mobile SDK
set(OVR_MOBILE_INCLUDE_DIRS ${INSTALL_DIR}/Include)
target_include_directories(${TARGET_NAME} PRIVATE ${OVR_MOBILE_INCLUDE_DIRS})
set(OVR_MOBILE_LIBRARY_DIR ${INSTALL_DIR}/Libs/Android/arm64-v8a)
set(OVR_MOBILE_LIBRARY_RELEASE ${OVR_MOBILE_LIBRARY_DIR}/Release/libvrapi.so)
set(OVR_MOBILE_LIBRARY_DEBUG ${OVR_MOBILE_LIBRARY_DIR}/Debug/libvrapi.so)
select_library_configurations(OVR_MOBILE)
target_link_libraries(${TARGET_NAME} ${OVR_MOBILE_LIBRARIES})
# Platform SDK
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculusPlatform)
set(OVR_PLATFORM_INCLUDE_DIRS ${INSTALL_DIR}/Include)
target_include_directories(${TARGET_NAME} PRIVATE ${OVR_PLATFORM_INCLUDE_DIRS})
set(OVR_PLATFORM_LIBRARIES ${INSTALL_DIR}/Android/libs/arm64-v8a/libovrplatformloader.so)
target_link_libraries(${TARGET_NAME} ${OVR_PLATFORM_LIBRARIES})
endmacro()

View file

@ -1,85 +0,0 @@
set(TARGET_NAME gvr-interface)
if (ANDROID)
set(ANDROID_APK_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk-build")
set(ANDROID_APK_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk")
set(ANDROID_SDK_ROOT $ENV{ANDROID_HOME})
set(ANDROID_APP_DISPLAY_NAME Interface)
set(ANDROID_API_LEVEL 19)
set(ANDROID_APK_PACKAGE io.highfidelity.gvrinterface)
set(ANDROID_ACTIVITY_NAME io.highfidelity.gvrinterface.InterfaceActivity)
set(ANDROID_APK_VERSION_NAME "0.1")
set(ANDROID_APK_VERSION_CODE 1)
set(ANDROID_APK_FULLSCREEN TRUE)
set(ANDROID_DEPLOY_QT_INSTALL "--install")
set(BUILD_SHARED_LIBS ON)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}")
setup_hifi_library(Gui AndroidExtras)
else ()
setup_hifi_project(Gui)
endif ()
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
link_hifi_libraries(shared networking audio-client avatars)
if (ANDROID)
find_package(LibOVR)
if (LIBOVR_FOUND)
add_definitions(-DHAVE_LIBOVR)
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES} ${LIBOVR_ANDROID_LIBRARIES} ${TURBOJPEG_LIBRARY})
include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS})
# we need VRLib, so add a project.properties to our apk build folder that says that
file(RELATIVE_PATH RELATIVE_VRLIB_PATH ${ANDROID_APK_OUTPUT_DIR} "${LIBOVR_VRLIB_DIR}")
file(WRITE "${ANDROID_APK_BUILD_DIR}/project.properties" "android.library.reference.1=${RELATIVE_VRLIB_PATH}")
list(APPEND IGNORE_COPY_LIBS ${LIBOVR_ANDROID_LIBRARIES})
endif ()
endif ()
# the presence of a HOCKEY_APP_ID means we are making a beta build
if (ANDROID AND HOCKEY_APP_ID)
set(HOCKEY_APP_ENABLED true)
set(HOCKEY_APP_ACTIVITY "<activity android:name='net.hockeyapp.android.UpdateActivity' />\n")
set(ANDROID_ACTIVITY_NAME io.highfidelity.gvrinterface.InterfaceBetaActivity)
set(ANDROID_DEPLOY_QT_INSTALL "")
set(ANDROID_APK_CUSTOM_NAME "Interface-beta.apk")
# set the ANDROID_APK_VERSION_CODE to the number of git commits
execute_process(
COMMAND git rev-list --first-parent --count HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_COUNT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(ANDROID_APK_VERSION_CODE ${GIT_COMMIT_COUNT})
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/templates/InterfaceBetaActivity.java.in" "${ANDROID_APK_BUILD_DIR}/src/io/highfidelity/gvrinterface/InterfaceBetaActivity.java")
elseif (ANDROID)
set(HOCKEY_APP_ENABLED false)
endif ()
if (ANDROID)
set(HIFI_URL_INTENT "<intent-filter>\
\n <action android:name='android.intent.action.VIEW' />\
\n <category android:name='android.intent.category.DEFAULT' />\
\n <category android:name='android.intent.category.BROWSABLE' />\
\n <data android:scheme='hifi' />\
\n </intent-filter>"
)
set(ANDROID_EXTRA_APPLICATION_XML "${HOCKEY_APP_ACTIVITY}")
set(ANDROID_EXTRA_ACTIVITY_XML "${HIFI_URL_INTENT}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/templates/hockeyapp.xml.in" "${ANDROID_APK_BUILD_DIR}/res/values/hockeyapp.xml")
qt_create_apk()
endif (ANDROID)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -1,73 +0,0 @@
//
// Client.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/15.
// Copyright 2013 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 "Client.h"
#include <AccountManager.h>
#include <AddressManager.h>
#include <HifiSockAddr.h>
#include <NodeList.h>
#include <PacketHeaders.h>
Client::Client(QObject* parent) :
QObject(parent)
{
// we need to make sure that required dependencies are created
DependencyManager::set<AddressManager>();
setupNetworking();
}
void Client::setupNetworking() {
// once Application order of instantiation is fixed this should be done from AccountManager
AccountManager::getInstance().setAuthURL(DEFAULT_NODE_AUTH_URL);
// setup the NodeList for this client
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
auto nodeList = DependencyManager::set<NodeList>(NodeType::Agent, 0);
// while datagram processing remains simple for targets using Client, we'll handle datagrams
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &Client::processDatagrams);
// every second, ask the NodeList to check in with the domain server
QTimer* domainCheckInTimer = new QTimer(this);
domainCheckInTimer->setInterval(DOMAIN_SERVER_CHECK_IN_MSECS);
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
// TODO: once the Client knows its Address on start-up we should be able to immediately send a check in here
domainCheckInTimer->start();
// handle the case where the domain stops talking to us
// TODO: can we just have the nodelist do this when it sets up? Is there a user of the NodeList that wouldn't want this?
connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
}
void Client::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) {
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, incomingPacket);
}
void Client::processDatagrams() {
HifiSockAddr senderSockAddr;
static QByteArray incomingPacket;
auto nodeList = DependencyManager::get<NodeList>();
while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) {
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
processVerifiedPacket(senderSockAddr, incomingPacket);
}
}
}

View file

@ -1,33 +0,0 @@
//
// Client.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/15.
// Copyright 2013 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_Client_h
#define hifi_Client_h
#include <QtCore/QObject>
#include <HifiSockAddr.h>
class Client : public QObject {
Q_OBJECT
public:
Client(QObject* parent = 0);
virtual void cleanupBeforeQuit() = 0;
protected:
void setupNetworking();
virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
private slots:
void processDatagrams();
};
#endif // hifi_Client_h

View file

@ -1,191 +0,0 @@
//
// GVRInterface.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 11/18/14.
// Copyright 2013 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 "GVRInterface.h"
#ifdef ANDROID
#include <jni.h>
#include <qpa/qplatformnativeinterface.h>
#include <QtAndroidExtras/QAndroidJniEnvironment>
#include <QtAndroidExtras/QAndroidJniObject>
#ifdef HAVE_LIBOVR
#include <KeyState.h>
#include <VrApi/VrApi.h>
#endif
#endif
#include <QtCore/QTimer>
#include <QtGui/QKeyEvent>
#include <QtWidgets/QMenuBar>
#include "GVRMainWindow.h"
#include "RenderingClient.h"
static QString launchURLString = QString();
#ifdef ANDROID
extern "C" {
JNIEXPORT void Java_io_highfidelity_gvrinterface_InterfaceActivity_handleHifiURL(JNIEnv *jni, jclass clazz, jstring hifiURLString) {
launchURLString = QAndroidJniObject(hifiURLString).toString();
}
}
#endif
GVRInterface::GVRInterface(int argc, char* argv[]) :
QApplication(argc, argv),
_mainWindow(NULL),
_inVRMode(false)
{
setApplicationName("gvr-interface");
setOrganizationName("highfidelity");
setOrganizationDomain("io");
if (!launchURLString.isEmpty()) {
// did we get launched with a lookup URL? If so it is time to give that to the AddressManager
qDebug() << "We were opened via a hifi URL -" << launchURLString;
}
_client = new RenderingClient(this, launchURLString);
launchURLString = QString();
connect(this, &QGuiApplication::applicationStateChanged, this, &GVRInterface::handleApplicationStateChange);
#if defined(ANDROID) && defined(HAVE_LIBOVR)
QAndroidJniEnvironment jniEnv;
QPlatformNativeInterface* interface = QApplication::platformNativeInterface();
jobject activity = (jobject) interface->nativeResourceForIntegration("QtActivity");
ovr_RegisterHmtReceivers(&*jniEnv, activity);
// PLATFORMACTIVITY_REMOVAL: Temp workaround for PlatformActivity being
// stripped from UnityPlugin. Alternate is to use LOCAL_WHOLE_STATIC_LIBRARIES
// but that increases the size of the plugin by ~1MiB
OVR::linkerPlatformActivity++;
#endif
// call our idle function whenever we can
QTimer* idleTimer = new QTimer(this);
connect(idleTimer, &QTimer::timeout, this, &GVRInterface::idle);
idleTimer->start(0);
// call our quit handler before we go down
connect(this, &QCoreApplication::aboutToQuit, this, &GVRInterface::handleApplicationQuit);
}
void GVRInterface::handleApplicationQuit() {
_client->cleanupBeforeQuit();
}
void GVRInterface::idle() {
#if defined(ANDROID) && defined(HAVE_LIBOVR)
if (!_inVRMode && ovr_IsHeadsetDocked()) {
qDebug() << "The headset just got docked - enter VR mode.";
enterVRMode();
} else if (_inVRMode) {
if (ovr_IsHeadsetDocked()) {
static int counter = 0;
// Get the latest head tracking state, predicted ahead to the midpoint of the time
// it will be displayed. It will always be corrected to the real values by
// time warp, but the closer we get, the less black will be pulled in at the edges.
const double now = ovr_GetTimeInSeconds();
static double prev;
const double rawDelta = now - prev;
prev = now;
const double clampedPrediction = std::min( 0.1, rawDelta * 2);
ovrSensorState sensor = ovrHmd_GetSensorState(OvrHmd, now + clampedPrediction, true );
auto ovrOrientation = sensor.Predicted.Pose.Orientation;
glm::quat newOrientation(ovrOrientation.w, ovrOrientation.x, ovrOrientation.y, ovrOrientation.z);
_client->setOrientation(newOrientation);
if (counter++ % 100000 == 0) {
qDebug() << "GetSensorState in frame" << counter << "-"
<< ovrOrientation.x << ovrOrientation.y << ovrOrientation.z << ovrOrientation.w;
}
} else {
qDebug() << "The headset was undocked - leaving VR mode.";
leaveVRMode();
}
}
OVR::KeyState& backKeyState = _mainWindow->getBackKeyState();
auto backEvent = backKeyState.Update(ovr_GetTimeInSeconds());
if (backEvent == OVR::KeyState::KEY_EVENT_LONG_PRESS) {
qDebug() << "Attemping to start the Platform UI Activity.";
ovr_StartPackageActivity(_ovr, PUI_CLASS_NAME, PUI_GLOBAL_MENU);
} else if (backEvent == OVR::KeyState::KEY_EVENT_DOUBLE_TAP || backEvent == OVR::KeyState::KEY_EVENT_SHORT_PRESS) {
qDebug() << "Got an event we should cancel for!";
} else if (backEvent == OVR::KeyState::KEY_EVENT_DOUBLE_TAP) {
qDebug() << "The button is down!";
}
#endif
}
void GVRInterface::handleApplicationStateChange(Qt::ApplicationState state) {
switch(state) {
case Qt::ApplicationActive:
qDebug() << "The application is active.";
break;
case Qt::ApplicationSuspended:
qDebug() << "The application is being suspended.";
break;
default:
break;
}
}
void GVRInterface::enterVRMode() {
#if defined(ANDROID) && defined(HAVE_LIBOVR)
// Default vrModeParms
ovrModeParms vrModeParms;
vrModeParms.AsynchronousTimeWarp = true;
vrModeParms.AllowPowerSave = true;
vrModeParms.DistortionFileName = NULL;
vrModeParms.EnableImageServer = false;
vrModeParms.CpuLevel = 2;
vrModeParms.GpuLevel = 2;
vrModeParms.GameThreadTid = 0;
QAndroidJniEnvironment jniEnv;
QPlatformNativeInterface* interface = QApplication::platformNativeInterface();
jobject activity = (jobject) interface->nativeResourceForIntegration("QtActivity");
vrModeParms.ActivityObject = activity;
ovrHmdInfo hmdInfo;
_ovr = ovr_EnterVrMode(vrModeParms, &hmdInfo);
_inVRMode = true;
#endif
}
void GVRInterface::leaveVRMode() {
#if defined(ANDROID) && defined(HAVE_LIBOVR)
ovr_LeaveVrMode(_ovr);
_inVRMode = false;
#endif
}

View file

@ -1,72 +0,0 @@
//
// GVRInterface.h
// gvr-interface/src
//
// Created by Stephen Birarda on 11/18/14.
// Copyright 2013 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_GVRInterface_h
#define hifi_GVRInterface_h
#include <QtWidgets/QApplication>
#if defined(ANDROID) && defined(HAVE_LIBOVR)
class ovrMobile;
class ovrHmdInfo;
// This is set by JNI_OnLoad() when the .so is initially loaded.
// Must use to attach each thread that will use JNI:
namespace OVR {
// PLATFORMACTIVITY_REMOVAL: Temp workaround for PlatformActivity being
// stripped from UnityPlugin. Alternate is to use LOCAL_WHOLE_STATIC_LIBRARIES
// but that increases the size of the plugin by ~1MiB
extern int linkerPlatformActivity;
}
#endif
class GVRMainWindow;
class RenderingClient;
class QKeyEvent;
#if defined(qApp)
#undef qApp
#endif
#define qApp (static_cast<GVRInterface*>(QApplication::instance()))
class GVRInterface : public QApplication {
Q_OBJECT
public:
GVRInterface(int argc, char* argv[]);
RenderingClient* getClient() { return _client; }
void setMainWindow(GVRMainWindow* mainWindow) { _mainWindow = mainWindow; }
protected:
void keyPressEvent(QKeyEvent* event);
private slots:
void handleApplicationStateChange(Qt::ApplicationState state);
void idle();
private:
void handleApplicationQuit();
void enterVRMode();
void leaveVRMode();
#if defined(ANDROID) && defined(HAVE_LIBOVR)
ovrMobile* _ovr;
ovrHmdInfo* _hmdInfo;
#endif
GVRMainWindow* _mainWindow;
RenderingClient* _client;
bool _inVRMode;
};
#endif // hifi_GVRInterface_h

View file

@ -1,176 +0,0 @@
//
// GVRMainWindow.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/14.
// Copyright 2013 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 "GVRMainWindow.h"
#include <QtGui/QKeyEvent>
#include <QtWidgets/QApplication>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QVBoxLayout>
#ifndef ANDROID
#include <QtWidgets/QDesktopWidget>
#elif defined(HAVE_LIBOVR)
#include <OVR_CAPI.h>
const float LIBOVR_DOUBLE_TAP_DURATION = 0.25f;
const float LIBOVR_LONG_PRESS_DURATION = 0.75f;
#endif
#include <AddressManager.h>
#include "InterfaceView.h"
#include "LoginDialog.h"
#include "RenderingClient.h"
GVRMainWindow::GVRMainWindow(QWidget* parent) :
QMainWindow(parent),
#if defined(ANDROID) && defined(HAVE_LIBOVR)
_backKeyState(LIBOVR_DOUBLE_TAP_DURATION, LIBOVR_LONG_PRESS_DURATION),
_wasBackKeyDown(false),
#endif
_mainLayout(NULL),
_menuBar(NULL),
_loginAction(NULL)
{
#ifndef ANDROID
const int NOTE_4_WIDTH = 2560;
const int NOTE_4_HEIGHT = 1440;
setFixedSize(NOTE_4_WIDTH / 2, NOTE_4_HEIGHT / 2);
#endif
setupMenuBar();
QWidget* baseWidget = new QWidget(this);
// setup a layout so we can vertically align to top
_mainLayout = new QVBoxLayout(baseWidget);
_mainLayout->setAlignment(Qt::AlignTop);
// set the layout on the base widget
baseWidget->setLayout(_mainLayout);
setCentralWidget(baseWidget);
// add the interface view
new InterfaceView(baseWidget);
}
GVRMainWindow::~GVRMainWindow() {
delete _menuBar;
}
void GVRMainWindow::keyPressEvent(QKeyEvent* event) {
#ifdef ANDROID
if (event->key() == Qt::Key_Back) {
// got the Android back key, hand off to OVR KeyState
_backKeyState.HandleEvent(ovr_GetTimeInSeconds(), true, (_wasBackKeyDown ? 1 : 0));
_wasBackKeyDown = true;
return;
}
#endif
QWidget::keyPressEvent(event);
}
void GVRMainWindow::keyReleaseEvent(QKeyEvent* event) {
#ifdef ANDROID
if (event->key() == Qt::Key_Back) {
// release on the Android back key, hand off to OVR KeyState
_backKeyState.HandleEvent(ovr_GetTimeInSeconds(), false, 0);
_wasBackKeyDown = false;
}
#endif
QWidget::keyReleaseEvent(event);
}
void GVRMainWindow::setupMenuBar() {
QMenu* fileMenu = new QMenu("File");
QMenu* helpMenu = new QMenu("Help");
_menuBar = new QMenuBar(0);
_menuBar->addMenu(fileMenu);
_menuBar->addMenu(helpMenu);
QAction* goToAddress = new QAction("Go to Address", fileMenu);
connect(goToAddress, &QAction::triggered, this, &GVRMainWindow::showAddressBar);
fileMenu->addAction(goToAddress);
_loginAction = new QAction("Login", fileMenu);
fileMenu->addAction(_loginAction);
// change the login action depending on our logged in/out state
AccountManager& accountManager = AccountManager::getInstance();
connect(&accountManager, &AccountManager::loginComplete, this, &GVRMainWindow::refreshLoginAction);
connect(&accountManager, &AccountManager::logoutComplete, this, &GVRMainWindow::refreshLoginAction);
// refresh the state now
refreshLoginAction();
QAction* aboutQt = new QAction("About Qt", helpMenu);
connect(aboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);
helpMenu->addAction(aboutQt);
setMenuBar(_menuBar);
}
void GVRMainWindow::showAddressBar() {
// setup the address QInputDialog
QInputDialog* addressDialog = new QInputDialog(this);
addressDialog->setLabelText("Address");
// add the address dialog to the main layout
_mainLayout->addWidget(addressDialog);
connect(addressDialog, &QInputDialog::textValueSelected,
DependencyManager::get<AddressManager>().data(), &AddressManager::handleLookupString);
}
void GVRMainWindow::showLoginDialog() {
LoginDialog* loginDialog = new LoginDialog(this);
// have the acccount manager handle credentials from LoginDialog
AccountManager& accountManager = AccountManager::getInstance();
connect(loginDialog, &LoginDialog::credentialsEntered, &accountManager, &AccountManager::requestAccessToken);
connect(&accountManager, &AccountManager::loginFailed, this, &GVRMainWindow::showLoginFailure);
_mainLayout->addWidget(loginDialog);
}
void GVRMainWindow::showLoginFailure() {
QMessageBox::warning(this, "Login Failed",
"Could not log in with that username and password. Please try again!");
}
void GVRMainWindow::refreshLoginAction() {
AccountManager& accountManager = AccountManager::getInstance();
disconnect(_loginAction, &QAction::triggered, &accountManager, 0);
if (accountManager.isLoggedIn()) {
_loginAction->setText("Logout");
connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
} else {
_loginAction->setText("Login");
connect(_loginAction, &QAction::triggered, this, &GVRMainWindow::showLoginDialog);
}
}

View file

@ -1,58 +0,0 @@
//
// GVRMainWindow.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/14.
// Copyright 2013 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_GVRMainWindow_h
#define hifi_GVRMainWindow_h
#include <QtWidgets/QMainWindow>
#if defined(ANDROID) && defined(HAVE_LIBOVR)
#include <KeyState.h>
#endif
class QKeyEvent;
class QMenuBar;
class QVBoxLayout;
class GVRMainWindow : public QMainWindow {
Q_OBJECT
public:
GVRMainWindow(QWidget* parent = 0);
~GVRMainWindow();
public slots:
void showAddressBar();
void showLoginDialog();
void showLoginFailure();
#if defined(ANDROID) && defined(HAVE_LIBOVR)
OVR::KeyState& getBackKeyState() { return _backKeyState; }
#endif
protected:
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);
private slots:
void refreshLoginAction();
private:
void setupMenuBar();
#if defined(ANDROID) && defined(HAVE_LIBOVR)
OVR::KeyState _backKeyState;
bool _wasBackKeyDown;
#endif
QVBoxLayout* _mainLayout;
QMenuBar* _menuBar;
QAction* _loginAction;
};
#endif // hifi_GVRMainWindow_h

View file

@ -1,18 +0,0 @@
//
// InterfaceView.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 1/28/14.
// Copyright 2013 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 "InterfaceView.h"
InterfaceView::InterfaceView(QWidget* parent, Qt::WindowFlags flags) :
QOpenGLWidget(parent, flags)
{
}

View file

@ -1,23 +0,0 @@
//
// InterfaceView.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/28/14.
// Copyright 2013 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_InterfaceView_h
#define hifi_InterfaceView_h
#include <QtWidgets/QOpenGLWidget>
class InterfaceView : public QOpenGLWidget {
Q_OBJECT
public:
InterfaceView(QWidget* parent = 0, Qt::WindowFlags flags = 0);
};
#endif // hifi_InterfaceView_h

View file

@ -1,69 +0,0 @@
//
// LoginDialog.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 2015-02-03.
// Copyright 2015 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 "LoginDialog.h"
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
LoginDialog::LoginDialog(QWidget* parent) :
QDialog(parent)
{
setupGUI();
setWindowTitle("Login");
setModal(true);
}
void LoginDialog::setupGUI() {
// setup a grid layout
QGridLayout* formGridLayout = new QGridLayout(this);
_usernameLineEdit = new QLineEdit(this);
QLabel* usernameLabel = new QLabel(this);
usernameLabel->setText("Username");
usernameLabel->setBuddy(_usernameLineEdit);
formGridLayout->addWidget(usernameLabel, 0, 0);
formGridLayout->addWidget(_usernameLineEdit, 1, 0);
_passwordLineEdit = new QLineEdit(this);
_passwordLineEdit->setEchoMode(QLineEdit::Password);
QLabel* passwordLabel = new QLabel(this);
passwordLabel->setText("Password");
passwordLabel->setBuddy(_passwordLineEdit);
formGridLayout->addWidget(passwordLabel, 2, 0);
formGridLayout->addWidget(_passwordLineEdit, 3, 0);
QDialogButtonBox* buttons = new QDialogButtonBox(this);
QPushButton* okButton = buttons->addButton(QDialogButtonBox::Ok);
QPushButton* cancelButton = buttons->addButton(QDialogButtonBox::Cancel);
okButton->setText("Login");
connect(cancelButton, &QPushButton::clicked, this, &QDialog::close);
connect(okButton, &QPushButton::clicked, this, &LoginDialog::loginButtonClicked);
formGridLayout->addWidget(buttons, 4, 0, 1, 2);
setLayout(formGridLayout);
}
void LoginDialog::loginButtonClicked() {
emit credentialsEntered(_usernameLineEdit->text(), _passwordLineEdit->text());
close();
}

View file

@ -1,34 +0,0 @@
//
// LoginDialog.h
// gvr-interface/src
//
// Created by Stephen Birarda on 2015-02-03.
// Copyright 2015 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_LoginDialog_h
#define hifi_LoginDialog_h
#include <QtWidgets/QDialog>
class QLineEdit;
class LoginDialog : public QDialog {
Q_OBJECT
public:
LoginDialog(QWidget* parent = 0);
signals:
void credentialsEntered(const QString& username, const QString& password);
private slots:
void loginButtonClicked();
private:
void setupGUI();
QLineEdit* _usernameLineEdit;
QLineEdit* _passwordLineEdit;
};
#endif // hifi_LoginDialog_h

View file

@ -1,156 +0,0 @@
//
// RenderingClient.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/15.
// Copyright 2013 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 "RenderingClient.h"
#include <QtCore/QThread>
#include <QtWidgets/QInputDialog>
#include <AddressManager.h>
#include <AudioClient.h>
#include <AvatarHashMap.h>
#include <NodeList.h>
RenderingClient* RenderingClient::_instance = NULL;
RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString) :
Client(parent)
{
_instance = this;
// connect to AddressManager and pass it the launch URL, if we have one
auto addressManager = DependencyManager::get<AddressManager>();
connect(addressManager.data(), &AddressManager::locationChangeRequired, this, &RenderingClient::goToLocation);
addressManager->loadSettings(launchURLString);
// tell the NodeList which node types all rendering clients will want to know about
DependencyManager::get<NodeList>()->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer);
DependencyManager::set<AvatarHashMap>();
// get our audio client setup on its own thread
auto audioClient = DependencyManager::set<AudioClient>();
audioClient->setPositionGetter(getPositionForAudio);
audioClient->setOrientationGetter(getOrientationForAudio);
audioClient->startThread();
connect(&_avatarTimer, &QTimer::timeout, this, &RenderingClient::sendAvatarPacket);
_avatarTimer.setInterval(16); // 60 FPS
_avatarTimer.start();
_fakeAvatar.setDisplayName("GearVR");
_fakeAvatar.setFaceModelURL(QUrl(DEFAULT_HEAD_MODEL_URL));
_fakeAvatar.setSkeletonModelURL(QUrl(DEFAULT_BODY_MODEL_URL));
_fakeAvatar.toByteArray(); // Creates HeadData
}
void RenderingClient::sendAvatarPacket() {
_fakeAvatar.setPosition(_position);
_fakeAvatar.setHeadOrientation(_orientation);
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
packet.append(_fakeAvatar.toByteArray());
DependencyManager::get<NodeList>()->broadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
_fakeAvatar.sendIdentityPacket();
}
void RenderingClient::cleanupBeforeQuit() {
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
// destroy the AudioClient so it and its thread will safely go down
DependencyManager::destroy<AudioClient>();
}
void RenderingClient::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) {
auto nodeList = DependencyManager::get<NodeList>();
PacketType incomingType = packetTypeForPacket(incomingPacket);
switch (incomingType) {
case PacketTypeAudioEnvironment:
case PacketTypeAudioStreamStats:
case PacketTypeMixedAudio:
case PacketTypeSilentAudioFrame: {
if (incomingType == PacketTypeAudioStreamStats) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioStreamStatsPacket",
Qt::QueuedConnection,
Q_ARG(QByteArray, incomingPacket));
} else if (incomingType == PacketTypeAudioEnvironment) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioEnvironmentData",
Qt::QueuedConnection,
Q_ARG(QByteArray, incomingPacket));
} else {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "addReceivedAudioToStream",
Qt::QueuedConnection,
Q_ARG(QByteArray, incomingPacket));
}
// update having heard from the audio-mixer and record the bytes received
SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket);
if (audioMixer) {
audioMixer->setLastHeardMicrostamp(usecTimestampNow());
}
break;
}
case PacketTypeBulkAvatarData:
case PacketTypeKillAvatar:
case PacketTypeAvatarIdentity:
case PacketTypeAvatarBillboard: {
// update having heard from the avatar-mixer and record the bytes received
SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket);
if (avatarMixer) {
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
QMetaObject::invokeMethod(DependencyManager::get<AvatarHashMap>().data(),
"processAvatarMixerDatagram",
Q_ARG(const QByteArray&, incomingPacket),
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
}
break;
}
default:
Client::processVerifiedPacket(senderSockAddr, incomingPacket);
break;
}
}
void RenderingClient::goToLocation(const glm::vec3& newPosition,
bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation) {
qDebug().nospace() << "RenderingClient goToLocation - moving to " << newPosition.x << ", "
<< newPosition.y << ", " << newPosition.z;
glm::vec3 shiftedPosition = newPosition;
if (hasOrientationChange) {
qDebug().nospace() << "RenderingClient goToLocation - new orientation is "
<< newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w;
// orient the user to face the target
glm::quat quatOrientation = newOrientation;
if (shouldFaceLocation) {
quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
// move the user a couple units away
const float DISTANCE_TO_USER = 2.0f;
shiftedPosition = newPosition - quatOrientation * glm::vec3( 0.0f, 0.0f,-1.0f) * DISTANCE_TO_USER;
}
_orientation = quatOrientation;
}
_position = shiftedPosition;
}

View file

@ -1,57 +0,0 @@
//
// RenderingClient.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/15.
// Copyright 2013 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_RenderingClient_h
#define hifi_RenderingClient_h
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QTimer>
#include <AvatarData.h>
#include "Client.h"
class RenderingClient : public Client {
Q_OBJECT
public:
RenderingClient(QObject* parent = 0, const QString& launchURLString = QString());
const glm::vec3& getPosition() const { return _position; }
const glm::quat& getOrientation() const { return _orientation; }
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
static glm::vec3 getPositionForAudio() { return _instance->getPosition(); }
static glm::quat getOrientationForAudio() { return _instance->getOrientation(); }
virtual void cleanupBeforeQuit();
private slots:
void goToLocation(const glm::vec3& newPosition,
bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation);
void sendAvatarPacket();
private:
virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
static RenderingClient* _instance;
glm::vec3 _position;
glm::quat _orientation;
QTimer _avatarTimer;
AvatarData _fakeAvatar;
};
#endif // hifi_RenderingClient_h

View file

@ -1,41 +0,0 @@
//
// InterfaceActivity.java
// gvr-interface/java
//
// Created by Stephen Birarda on 1/26/15.
// Copyright 2015 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.gvrinterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.WindowManager;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtActivity;
public class InterfaceActivity extends QtActivity {
public static native void handleHifiURL(String hifiURLString);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Get the intent that started this activity in case we have a hifi:// URL to parse
Intent intent = getIntent();
if (intent.getAction() == Intent.ACTION_VIEW) {
Uri data = intent.getData();
if (data.getScheme().equals("hifi")) {
handleHifiURL(data.toString());
}
}
}
}

View file

@ -1,28 +0,0 @@
//
// main.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 11/17/14.
// Copyright 2014 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 "GVRMainWindow.h"
#include "GVRInterface.h"
int main(int argc, char* argv[]) {
GVRInterface app(argc, argv);
GVRMainWindow mainWindow;
#ifdef ANDROID
mainWindow.showFullScreen();
#else
mainWindow.showMaximized();
#endif
app.setMainWindow(&mainWindow);
return app.exec();
}

View file

@ -1,51 +0,0 @@
//
// InterfaceBetaActivity.java
// gvr-interface/java
//
// Created by Stephen Birarda on 1/27/15.
// Copyright 2015 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.gvrinterface;
import android.os.Bundle;
import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.UpdateManager;
public class InterfaceBetaActivity extends InterfaceActivity {
public String _hockeyAppID;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_hockeyAppID = getString(R.string.HockeyAppID);
checkForUpdates();
}
@Override
protected void onPause() {
super.onPause();
UpdateManager.unregister();
}
@Override
protected void onResume() {
super.onResume();
checkForCrashes();
}
private void checkForCrashes() {
CrashManager.register(this, _hockeyAppID);
}
private void checkForUpdates() {
// Remove this for store / production builds!
UpdateManager.register(this, _hockeyAppID);
}
}

View file

@ -1,5 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="HockeyAppID">${HOCKEY_APP_ID}</string>
<bool name="HockeyAppEnabled">${HOCKEY_APP_ENABLED}</bool>
</resources>

View file

@ -52,6 +52,13 @@ ANDROID_PACKAGES = {
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
'includeLibs': ['libvrapi.so']
},
'oculusPlatform': {
'file': 'OVRPlatformSDK_v1.32.0.zip',
'versionId': 'jG9DB16zOGxSrmtZy4jcQnwO0TJUuaeL',
'checksum': 'ab5b203b3a39a56ab148d68fff769e05',
'sharedLibFolder': 'Android/libs/arm64-v8a',
'includeLibs': ['libovrplatformloader.so']
},
'openssl': {
'file': 'openssl-1.1.0g_armv8.tgz',
'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',

View file

@ -265,7 +265,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
endforeach()
# include headers for interface and InterfaceConfig.
include_directories("${PROJECT_SOURCE_DIR}/src")
target_include_directories(${TARGET_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/src")
if (ANDROID)
find_library(ANDROID_LOG_LIB log)

View file

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 735 B

View file

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View file

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View file

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View file

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 655 B

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View file

@ -662,7 +662,7 @@ Rectangle {
anchors.right: parent.right;
text: "Cancel"
onClicked: {
sendToScript({method: 'checkout_cancelClicked', params: itemId});
sendToScript({method: 'checkout_cancelClicked', itemId: itemId});
}
}
}

View file

@ -24,11 +24,8 @@ Item {
HifiConstants { id: hifi; }
id: root;
property string referrerURL: (Account.metaverseServerURL + "/marketplace?");
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
property alias usernameDropdownVisible: usernameDropdown.visible;
height: mainContainer.height + additionalDropdownHeight;
height: mainContainer.height;
Connections {
target: Commerce;
@ -93,77 +90,7 @@ Item {
MouseArea {
anchors.fill: parent;
onClicked: {
sendToParent({method: "header_marketplaceImageClicked", referrerURL: root.referrerURL});
}
}
}
Item {
id: buttonAndUsernameContainer;
anchors.left: marketplaceHeaderImage.right;
anchors.leftMargin: 8;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
anchors.right: securityImage.left;
anchors.rightMargin: 6;
TextMetrics {
id: textMetrics;
font.family: "Raleway"
text: usernameText.text;
}
Rectangle {
id: myUsernameButton;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: usernameText.width + 25;
color: "white";
radius: 4;
border.width: 1;
border.color: hifi.colors.lightGray;
// Username Text
RalewayRegular {
id: usernameText;
text: Account.username;
// Text size
size: 18;
// Style
color: hifi.colors.baseGray;
elide: Text.ElideRight;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
width: Math.min(textMetrics.width + 25, 110);
// Anchors
anchors.centerIn: parent;
rightPadding: 10;
}
HiFiGlyphs {
id: dropdownIcon;
text: hifi.glyphs.caratDn;
// Size
size: 50;
// Anchors
anchors.right: parent.right;
anchors.rightMargin: -14;
anchors.verticalCenter: parent.verticalCenter;
horizontalAlignment: Text.AlignHCenter;
// Style
color: hifi.colors.baseGray;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
usernameDropdown.visible = !usernameDropdown.visible;
}
onEntered: usernameText.color = hifi.colors.baseGrayShadow;
onExited: usernameText.color = hifi.colors.baseGray;
sendToParent({method: "header_marketplaceImageClicked"});
}
}
}
@ -205,92 +132,6 @@ Item {
}
}
Item {
id: usernameDropdown;
z: 998;
visible: false;
anchors.top: buttonAndUsernameContainer.bottom;
anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
anchors.right: buttonAndUsernameContainer.right;
height: childrenRect.height;
width: 150;
Rectangle {
id: myItemsButton;
color: hifi.colors.white;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
RalewaySemiBold {
anchors.fill: parent;
text: "My Submissions"
color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
size: 18;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
myItemsButton.color = hifi.colors.blueHighlight;
}
onExited: {
myItemsButton.color = hifi.colors.white;
}
onClicked: {
sendToParent({method: "header_myItemsClicked"});
}
}
}
Rectangle {
id: logOutButton;
color: hifi.colors.white;
anchors.top: myItemsButton.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
RalewaySemiBold {
anchors.fill: parent;
text: "Log Out"
color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
size: 18;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
logOutButton.color = hifi.colors.blueHighlight;
}
onExited: {
logOutButton.color = hifi.colors.white;
}
onClicked: {
Account.logOut();
}
}
}
}
DropShadow {
z: 997;
visible: usernameDropdown.visible;
anchors.fill: usernameDropdown;
horizontalOffset: 3;
verticalOffset: 3;
radius: 8.0;
samples: 17;
color: "#80000000";
source: usernameDropdown;
}
}

View file

@ -22,7 +22,6 @@ Rectangle {
HifiConstants { id: hifi; }
id: root;
property string marketplaceUrl: "";
property string entityId: "";
property string certificateId: "";
property string itemName: "--";
@ -30,6 +29,7 @@ Rectangle {
property string itemEdition: "--";
property string dateAcquired: "--";
property string itemCost: "--";
property string marketplace_item_id: "";
property string certTitleTextColor: hifi.colors.darkGray;
property string certTextColor: hifi.colors.white;
property string infoTextColor: hifi.colors.blueAccent;
@ -69,7 +69,7 @@ Rectangle {
errorText.text = "Information about this certificate is currently unavailable. Please try again later.";
}
} else {
root.marketplaceUrl = result.data.marketplace_item_url;
root.marketplace_item_id = result.data.marketplace_item_id;
root.isMyCert = result.isMyCert ? result.isMyCert : false;
if (root.certInfoReplaceMode > 3) {
@ -352,7 +352,7 @@ Rectangle {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
}
onEntered: itemName.color = hifi.colors.blueHighlight;
onExited: itemName.color = root.certTextColor;
@ -391,7 +391,7 @@ Rectangle {
// "Show In Marketplace" button
HifiControlsUit.Button {
id: showInMarketplaceButton;
enabled: root.marketplaceUrl;
enabled: root.marketplace_item_id && marketplace_item_id !== "";
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.bottom: parent.bottom;
@ -401,7 +401,7 @@ Rectangle {
height: 40;
text: "View In Market"
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
}
}
}
@ -620,7 +620,7 @@ Rectangle {
root.itemOwner = "--";
root.itemEdition = "--";
root.dateAcquired = "--";
root.marketplaceUrl = "";
root.marketplace_item_id = "";
root.itemCost = "--";
root.isMyCert = false;
errorText.text = "";

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,648 @@
//
// MarketplaceListItem.qml
// qml/hifi/commerce/marketplace
//
// MarketplaceListItem
//
// Created by Roxanne Skelly on 2019-01-22
// Copyright 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
//
import Hifi 1.0 as Hifi
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtGraphicalEffects 1.0
import QtWebEngine 1.5
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
import "../common/sendAsset"
import "../.." as HifiCommon
Rectangle {
id: root;
property string item_id: ""
property string image_url: ""
property string name: ""
property int likes: 0
property bool liked: false
property string creator: ""
property var categories: []
property int price: 0
property var attributions: []
property string description: ""
property string license: ""
property string posted: ""
property bool available: false
property string created_at: ""
property bool isLoggedIn: false;
property int edition: -1;
property bool supports3DHTML: false;
onCategoriesChanged: {
categoriesListModel.clear();
categories.forEach(function(category) {
categoriesListModel.append({"category":category});
});
}
onDescriptionChanged: {
if(root.supports3DHTML) {
descriptionTextModel.clear();
descriptionTextModel.append({text: description});
} else {
descriptionText.text = description;
}
}
onAttributionsChanged: {
attributionsModel.clear();
if(root.attributions) {
root.attributions.forEach(function(attribution) {
attributionsModel.append(attribution);
});
}
footer.evalHeight();
}
signal buy()
signal categoryClicked(string category)
signal showLicense(string url)
HifiConstants {
id: hifi
}
Connections {
target: MarketplaceScriptingInterface
onMarketplaceItemLikeResult: {
if (result.status !== 'success') {
console.log("Like/Unlike item", result.data.message);
} else {
root.liked = !root.liked;
root.likes = root.liked ? root.likes + 1 : root.likes - 1;
}
}
}
function getFormattedDate(timestamp) {
function addLeadingZero(n) {
return n < 10 ? '0' + n : '' + n;
}
var a = new Date(timestamp);
var year = a.getFullYear();
var month = addLeadingZero(a.getMonth() + 1);
var day = addLeadingZero(a.getDate());
var hour = a.getHours();
var drawnHour = hour;
if (hour === 0) {
drawnHour = 12;
} else if (hour > 12) {
drawnHour -= 12;
}
drawnHour = addLeadingZero(drawnHour);
var amOrPm = "AM";
if (hour >= 12) {
amOrPm = "PM";
}
var min = addLeadingZero(a.getMinutes());
var sec = addLeadingZero(a.getSeconds());
return a.toDateString() + " " + drawnHour + ':' + min + amOrPm;
}
function evalHeight() {
height = footer.y - header.y + footer.height;
}
signal resized()
onHeightChanged: {
resized();
}
anchors {
left: parent.left
right: parent.right
leftMargin: 15
rightMargin: 15
}
height: footer.y - header.y + footer.height
Rectangle {
id: header
anchors {
left: parent.left
right: parent.right
top: parent.top
}
height: 50
RalewaySemiBold {
id: nameText
anchors {
top: parent.top
left: parent.left
bottom: parent.bottom
}
width: paintedWidth
text: root.name
size: 24
color: hifi.colors.baseGray
verticalAlignment: Text.AlignVCenter
}
Item {
id: likes
anchors {
top: parent.top
right: parent.right
bottom: parent.bottom
rightMargin: 5
}
RalewaySemiBold {
id: heart
anchors {
top: parent.top
right: parent.right
rightMargin: 0
verticalCenter: parent.verticalCenter
}
text: "\u2665"
size: 20
horizontalAlignment: Text.AlignHCenter
color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText
}
RalewaySemiBold {
id: likesText
anchors {
top: parent.top
right: heart.left
rightMargin: 5
leftMargin: 15
bottom: parent.bottom
}
width: paintedWidth
text: root.likes
size: hifi.fontSizes.overlayTitle
color: hifi.colors.baseGray
verticalAlignment: Text.AlignVCenter
}
MouseArea {
anchors {
left: likesText.left
right: heart.right
top: likesText.top
bottom: likesText.bottom
}
onClicked: {
if (isLoggedIn) {
MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked);
}
}
}
}
}
Image {
id: itemImage
anchors {
top: header.bottom
left: parent.left
right: parent.right
}
height: width*0.5625
source: root.image_url
fillMode: Image.PreserveAspectCrop;
}
Item {
id: footer
anchors {
left: parent.left;
right: parent.right;
top: itemImage.bottom;
}
height: categoriesList.y - buyButton.y + categoriesList.height
function evalHeight() {
height = categoriesList.y - buyButton.y + categoriesList.height;
console.log("HEIGHT: " + height);
}
HifiControlsUit.Button {
id: buyButton
anchors {
right: parent.right
top: parent.top
left: parent.left
topMargin: 15
}
height: 50
text: root.edition >= 0 ? "UPGRADE FOR FREE" : (root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)")
enabled: root.edition >= 0 || root.available
buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : ""
color: hifi.buttons.blue
onClicked: root.buy();
}
Item {
id: creatorItem
anchors {
top: buyButton.bottom
leftMargin: 15
topMargin: 15
}
width: parent.width
height: childrenRect.height
RalewaySemiBold {
id: creatorLabel
anchors.top: parent.top
anchors.left: parent.left
width: paintedWidth
text: "CREATOR:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
id: creatorText
anchors {
top: creatorLabel.bottom
left: parent.left
topMargin: 10
}
width: paintedWidth
text: root.creator
size: 18
color: hifi.colors.blueHighlight
verticalAlignment: Text.AlignVCenter
}
}
Item {
id: posted
anchors {
top: creatorItem.bottom
leftMargin: 15
topMargin: 15
}
width: parent.width
height: childrenRect.height
RalewaySemiBold {
id: postedLabel
anchors.top: parent.top
anchors.left: parent.left
width: paintedWidth
text: "POSTED:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
id: created_at
anchors {
top: postedLabel.bottom
left: parent.left
right: parent.right
topMargin: 5
}
text: { getFormattedDate(root.created_at); }
size: 14
color: hifi.colors.lightGray
verticalAlignment: Text.AlignVCenter
}
}
Rectangle {
anchors {
top: posted.bottom
leftMargin: 15
topMargin: 15
}
width: parent.width
height: 1
color: hifi.colors.lightGrayText
}
Item {
id: attributions
anchors {
top: posted.bottom
topMargin: 30
left: parent.left
right: parent.right
}
width: parent.width
height: attributionsModel.count > 0 ? childrenRect.height : 0
visible: attributionsModel.count > 0
RalewaySemiBold {
id: attributionsLabel
anchors.top: parent.top
anchors.left: parent.left
width: paintedWidth
height: paintedHeight
text: "ATTRIBUTIONS:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
ListModel {
id: attributionsModel
}
ListView {
anchors {
left: parent.left
right: parent.right
top: attributionsLabel.bottom
bottomMargin: 15
}
height: 24*model.count+10
model: attributionsModel
delegate: Item {
height: 24
width: parent.width
RalewaySemiBold {
id: attributionName
anchors.leftMargin: 15
height: 24
width: parent.width
text: model.name
elide: Text.ElideRight
size: 14
color: model.link ? hifi.colors.blueHighlight : hifi.colors.baseGray
verticalAlignment: Text.AlignVCenter
MouseArea {
anchors.fill: parent
onClicked: {
if (model.link) {
sendToScript({method: 'marketplace_open_link', link: model.link});
}
}
}
}
}
}
Rectangle {
anchors {
bottom: attributions.bottom
leftMargin: 15
}
width: parent.width
height: 1
color: hifi.colors.lightGrayText
}
}
Item {
id: licenseItem;
anchors {
top: attributions.bottom
left: parent.left
topMargin: 15
}
width: parent.width
height: childrenRect.height
RalewaySemiBold {
id: licenseLabel
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
text: "SHARED UNDER:";
size: 14;
color: hifi.colors.lightGrayText;
verticalAlignment: Text.AlignVCenter;
}
RalewaySemiBold {
id: licenseText
anchors.top: licenseLabel.bottom
anchors.left: parent.left
anchors.topMargin: 5
width: paintedWidth
text: root.license
size: 14
color: hifi.colors.lightGray
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
id: licenseHelp
anchors.top: licenseText.bottom
anchors.left: parent.left
anchors.topMargin: 5
width: paintedWidth
text: "More about this license"
size: 14
color: hifi.colors.blueHighlight
verticalAlignment: Text.AlignVCenter
MouseArea {
anchors.fill: parent
onClicked: {
licenseInfo.visible = true;
var url;
if (root.license === "No Rights Reserved (CC0)") {
url = "https://creativecommons.org/publicdomain/zero/1.0/"
} else if (root.license === "Attribution (CC BY)") {
url = "https://creativecommons.org/licenses/by/4.0/legalcode.txt"
} else if (root.license === "Attribution-ShareAlike (CC BY-SA)") {
url = "https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt"
} else if (root.license === "Attribution-NoDerivs (CC BY-ND)") {
url = "https://creativecommons.org/licenses/by-nd/4.0/legalcode.txt"
} else if (root.license === "Attribution-NonCommercial (CC BY-NC)") {
url = "https://creativecommons.org/licenses/by-nc/4.0/legalcode.txt"
} else if (root.license === "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") {
url = "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt"
} else if (root.license === "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") {
url = "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode.txt"
} else if (root.license === "Proof of Provenance License (PoP License)") {
url = "https://digitalassetregistry.com/PoP-License/v1/"
}
if(url) {
showLicense(url)
}
}
}
}
}
Item {
id: descriptionItem
property string text: ""
anchors {
top: licenseItem.bottom
topMargin: 15
left: parent.left
right: parent.right
}
height: childrenRect.height
onHeightChanged: {
footer.evalHeight();
}
RalewaySemiBold {
id: descriptionLabel
anchors.top: parent.top
anchors.left: parent.left
width: paintedWidth
height: 20
text: "DESCRIPTION:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
id: descriptionText
anchors.top: descriptionLabel.bottom
anchors.left: parent.left
anchors.topMargin: 5
width: parent.width
text: root.description
size: 14
color: hifi.colors.lightGray
linkColor: hifi.colors.blueHighlight
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
wrapMode: Text.Wrap
onLinkActivated: {
sendToScript({method: 'marketplace_open_link', link: link});
}
onHeightChanged: { footer.evalHeight(); }
}
}
Item {
id: categoriesList
anchors {
top: descriptionItem.bottom
topMargin: 15
left: parent.left
right: parent.right
}
width: parent.width
height: categoriesListModel.count*24 + categoryLabel.height + (isLoggedIn ? 50 : 150)
onHeightChanged: { footer.evalHeight(); }
RalewaySemiBold {
id: categoryLabel
anchors.top: parent.top
anchors.left: parent.left
width: paintedWidth
text: "IN:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
ListModel {
id: categoriesListModel
}
ListView {
anchors {
left: parent.left
right: parent.right
top: categoryLabel.bottom
bottomMargin: 15
}
height: 24*model.count+10
model: categoriesListModel
delegate: RalewaySemiBold {
id: categoryText
anchors.leftMargin: 15
width: paintedWidth
text: model.category
size: 14
height: 24
color: hifi.colors.blueHighlight
verticalAlignment: Text.AlignVCenter
MouseArea {
anchors.fill: categoryText
onClicked: root.categoryClicked(model.category);
}
}
}
}
}
}

View file

@ -0,0 +1,321 @@
//
// MarketplaceListItem.qml
// qml/hifi/commerce/marketplace
//
// MarketplaceListItem
//
// Created by Roxanne Skelly on 2019-01-22
// Copyright 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
//
import Hifi 1.0 as Hifi
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtGraphicalEffects 1.0
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
import "../common/sendAsset"
import "../.." as HifiCommon
Rectangle {
id: root
property string item_id: ""
property string image_url: ""
property string name: ""
property int likes: 0
property bool liked: false
property string creator: ""
property string category: ""
property int price: 0
property bool available: false
property bool isLoggedIn: false;
signal buy()
signal showItem()
signal categoryClicked(string category)
signal requestReload()
HifiConstants { id: hifi }
width: parent.width
height: childrenRect.height+50
DropShadow {
anchors.fill: shadowBase
source: shadowBase
verticalOffset: 4
horizontalOffset: 4
radius: 6
samples: 9
color: hifi.colors.baseGrayShadow
}
Rectangle {
id: shadowBase
anchors.fill: itemRect
color: "white"
}
Rectangle {
id: itemRect
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: 20
bottomMargin: 10
leftMargin: 15
rightMargin: 15
}
height: childrenRect.height
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: root.showItem();
onEntered: hoverRect.visible = true;
onExited: hoverRect.visible = false;
}
Rectangle {
id: header
anchors {
left: parent.left
right: parent.right
top: parent.top
}
height: 50
RalewaySemiBold {
id: nameText
anchors {
top: parent.top
left: parent.left
right: parent.right
rightMargin: 50
leftMargin: 15
bottom: parent.bottom
}
width: paintedWidth
text: root.name
size: hifi.fontSizes.overlayTitle
elide: Text.ElideRight
color: hifi.colors.blueHighlight
verticalAlignment: Text.AlignVCenter
}
Item {
id: likes
anchors {
top: parent.top
right: parent.right
rightMargin: 15
bottom: parent.bottom
}
width: heart.width + likesText.width
Connections {
target: MarketplaceScriptingInterface
onMarketplaceItemLikeResult: {
if (result.status !== 'success') {
console.log("Failed to get Marketplace Categories", result.data.message);
root.requestReload();
}
}
}
RalewaySemiBold {
id: heart
anchors {
top: parent.top
right: parent.right
rightMargin: 0
verticalCenter: parent.verticalCenter
}
text: "\u2665"
size: 20
horizontalAlignment: Text.AlignHCenter;
color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText
}
RalewaySemiBold {
id: likesText
anchors {
top: parent.top
right: heart.left
rightMargin: 5
leftMargin: 15
bottom: parent.bottom
}
width: paintedWidth
text: root.likes
size: hifi.fontSizes.overlayTitle
color: hifi.colors.baseGray
verticalAlignment: Text.AlignVCenter
}
MouseArea {
anchors {
left: likesText.left
right: heart.right
top: parent.top
bottom: parent.bottom
}
onClicked: {
if (isLoggedIn) {
root.liked = !root.liked;
root.likes = root.liked ? root.likes + 1 : root.likes - 1;
MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
}
}
}
}
}
Image {
id: itemImage
anchors {
top: header.bottom
left: parent.left
right: parent.right
}
height: width * 0.5625
source: root.image_url
fillMode: Image.PreserveAspectCrop
}
Item {
id: footer
anchors {
left: parent.left
right: parent.right
top: itemImage.bottom
}
height: 60
RalewaySemiBold {
id: creatorLabel
anchors {
top: parent.top
left: parent.left
leftMargin: 15
}
width: paintedWidth
text: "CREATOR:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
id: creatorText
anchors {
top: creatorLabel.top;
left: creatorLabel.right;
leftMargin: 15;
}
width: paintedWidth;
text: root.creator;
size: 14;
color: hifi.colors.lightGray;
verticalAlignment: Text.AlignVCenter;
}
RalewaySemiBold {
id: categoryLabel
anchors {
top: creatorLabel.bottom
left: parent.left
leftMargin: 15
}
width: paintedWidth;
text: "IN:";
size: 14;
color: hifi.colors.lightGrayText;
verticalAlignment: Text.AlignVCenter;
}
RalewaySemiBold {
id: categoryText
anchors {
top: categoryLabel.top
left: categoryLabel.right
leftMargin: 15
}
width: paintedWidth
text: root.category
size: 14
color: hifi.colors.blueHighlight;
verticalAlignment: Text.AlignVCenter;
MouseArea {
anchors.fill: parent
onClicked: root.categoryClicked(root.category);
}
}
HifiControlsUit.Button {
anchors {
right: parent.right
top: parent.top
bottom: parent.bottom
rightMargin: 15
topMargin:10
bottomMargin: 10
}
text: root.price ? root.price : "FREE"
buttonGlyph: root.price ? hifi.glyphs.hfc : ""
color: hifi.buttons.blue;
onClicked: root.buy();
}
}
Rectangle {
id: hoverRect
anchors.fill: parent
border.color: hifi.colors.blueHighlight
border.width: 2
color: "#00000000"
visible: false
}
}
}

View file

@ -0,0 +1,87 @@
//
// SortButton.qml
// qml/hifi/commerce/marketplace
//
// SortButton
//
// Created by Roxanne Skelly on 2019-01-18
// Copyright 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
//
import Hifi 1.0 as Hifi
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtGraphicalEffects 1.0
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
import "../common/sendAsset"
import "../.." as HifiCommon
Item {
HifiConstants { id: hifi; }
id: root;
property string glyph: "";
property string text: "";
property bool checked: false;
signal clicked();
width: childrenRect.width;
height: parent.height;
Rectangle {
anchors.top: parent.top;
anchors.left: parent.left;
height: parent.height;
width: 2;
color: hifi.colors.faintGray;
visible: index > 0;
}
HiFiGlyphs {
id: buttonGlyph;
text: root.glyph;
// Size
size: 14;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 0;
anchors.top: parent.top;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height;
horizontalAlignment: Text.AlignHCenter;
// Style
color: hifi.colors.lightGray;
}
RalewayRegular {
id: buttonText;
text: root.text;
// Text size
size: 14;
// Style
color: hifi.colors.lightGray;
elide: Text.ElideRight;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.top: parent.top;
height: parent.height;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.clicked();
}
}
}

View file

@ -28,6 +28,7 @@ Item {
property string purchaseStatus;
property string itemName;
property string itemId;
property string updateItemId;
property string itemPreviewImageUrl;
property string itemHref;
property string certificateId;
@ -45,9 +46,9 @@ Item {
property bool cardBackVisible;
property bool isInstalled;
property string wornEntityID;
property string upgradeUrl;
property string updatedItemId;
property string upgradeTitle;
property bool updateAvailable: root.upgradeUrl !== "";
property bool updateAvailable: root.updateItemId && root.updateItemId !== "";
property bool valid;
property string originalStatusText;
@ -175,7 +176,7 @@ Item {
Item {
property alias buttonGlyphText: buttonGlyph.text;
property alias buttonText: buttonText.text;
property alias itemButtonText: buttonText.text;
property alias glyphSize: buttonGlyph.size;
property string buttonColor: hifi.colors.black;
property string buttonColor_hover: hifi.colors.blueHighlight;
@ -243,7 +244,7 @@ Item {
onLoaded: {
item.enabled = root.valid;
item.buttonGlyphText = hifi.glyphs.gift;
item.buttonText = "Gift";
item.itemButtonText = "Gift";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({
@ -270,7 +271,7 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.market;
item.buttonText = "View in Marketplace";
item.itemButtonText = "View in Marketplace";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
@ -288,7 +289,7 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.certificate;
item.buttonText = "View Certificate";
item.itemButtonText = "View Certificate";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId});
@ -307,7 +308,7 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.uninstall;
item.buttonText = "Uninstall";
item.itemButtonText = "Uninstall";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
Commerce.uninstallApp(root.itemHref);
@ -330,15 +331,14 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.update;
item.buttonText = "Update";
item.itemButtonText = "Update";
item.buttonColor = "#E2334D";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({
method: 'updateItemClicked',
itemId: root.itemId,
itemId: root.updateAvailable ? root.updateItemId : root.itemId,
itemEdition: root.itemEdition,
upgradeUrl: root.upgradeUrl,
itemHref: root.itemHref,
itemType: root.itemType,
isInstalled: root.isInstalled,
@ -378,10 +378,10 @@ Item {
function updateProperties() {
if (updateButton.visible && uninstallButton.visible) {
item.buttonText = "";
item.itemButtonText = "";
item.glyphSize = 20;
} else {
item.buttonText = "Send to Trash";
item.itemButtonText = "Send to Trash";
item.glyphSize = 30;
}
}

View file

@ -29,7 +29,6 @@ Rectangle {
id: root;
property string activeView: "initialize";
property string referrerURL: "";
property bool securityImageResultReceived: false;
property bool purchasesReceived: false;
property bool punctuationMode: false;
@ -154,55 +153,10 @@ Rectangle {
}
}
//
// TITLE BAR START
//
HifiCommerceCommon.EmulatedMarketplaceHeader {
id: titleBarContainer;
z: 997;
visible: false;
height: 100;
// Size
width: parent.width;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
Connections {
onSendToParent: {
if (msg.method === 'needsLogIn' && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (msg.method === 'showSecurityPicLightbox') {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = msg.securityImageSource;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
}
lightboxPopup.visible = true;
} else {
sendToScript(msg);
}
}
}
}
MouseArea {
enabled: titleBarContainer.usernameDropdownVisible;
anchors.fill: parent;
onClicked: {
titleBarContainer.usernameDropdownVisible = false;
}
}
//
// TITLE BAR END
//
Rectangle {
id: initialize;
visible: root.activeView === "initialize";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
@ -219,8 +173,7 @@ Rectangle {
id: installedAppsContainer;
z: 998;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width;
@ -422,8 +375,8 @@ Rectangle {
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 8 - titleBarContainer.additionalDropdownHeight;
anchors.top: parent.top;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
//
@ -585,6 +538,7 @@ Rectangle {
delegate: PurchasedItem {
itemName: title;
itemId: id;
updateItemId: model.upgrade_id ? model.upgrade_id : "";
itemPreviewImageUrl: preview;
itemHref: download_url;
certificateId: certificate_id;
@ -596,7 +550,6 @@ Rectangle {
cardBackVisible: model.cardBackVisible || false;
isInstalled: model.isInstalled || false;
wornEntityID: model.wornEntityID;
upgradeUrl: model.upgrade_url;
upgradeTitle: model.upgrade_title;
itemType: model.item_type;
valid: model.valid;
@ -1083,8 +1036,6 @@ Rectangle {
function fromScript(message) {
switch (message.method) {
case 'updatePurchases':
referrerURL = message.referrerURL || "";
titleBarContainer.referrerURL = message.referrerURL || "";
filterBar.text = message.filterText ? message.filterText : "";
break;
case 'purchases_showMyItems':

View file

@ -335,7 +335,7 @@ Item {
if (link.indexOf("users/") !== -1) {
sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
} else {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
sendSignalToWallet({method: 'transactionHistory_linkClicked', itemId: model.marketplace_item});
}
}
}

View file

@ -233,6 +233,7 @@
#include "commerce/Ledger.h"
#include "commerce/Wallet.h"
#include "commerce/QmlCommerce.h"
#include "commerce/QmlMarketplace.h"
#include "ResourceRequestObserver.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h"
@ -2985,7 +2986,23 @@ void Application::initializeUi() {
QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" },
QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
QUrl{ "hifi/tablet/TabletMenu.qml" },
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
}, commerceCallback);
QmlContextCallback marketplaceCallback = [](QQmlContext* context) {
context->setContextProperty("MarketplaceScriptingInterface", new QmlMarketplace());
};
OffscreenQmlSurface::addWhitelistContextHandler({
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
}, marketplaceCallback);
QmlContextCallback platformInfoCallback = [](QQmlContext* context) {
context->setContextProperty("PlatformInfo", new PlatformInfoScriptingInterface());
};
OffscreenQmlSurface::addWhitelistContextHandler({
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
}, platformInfoCallback);
QmlContextCallback ttsCallback = [](QQmlContext* context) {
context->setContextProperty("TextToSpeech", DependencyManager::get<TTSScriptingInterface>().data());
};
@ -6231,6 +6248,8 @@ void Application::update(float deltaTime) {
_physicsEngine->processTransaction(transaction);
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
myAvatar->prepareForPhysicsSimulation();
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
dynamic->prepareForPhysicsSimulation();
});

View file

@ -75,7 +75,6 @@ const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec
const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
const float MIN_AVATAR_SPEED = 0.05f;
const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this
float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
@ -847,6 +846,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
updateOrientation(deltaTime);
updatePosition(deltaTime);
updateViewBoom();
}
// update sensorToWorldMatrix for camera and hand controllers
@ -3323,21 +3323,22 @@ void MyAvatar::updateActionMotor(float deltaTime) {
direction = Vectors::ZERO;
}
float sensorToWorldScale = getSensorToWorldScale();
if (state == CharacterController::State::Hover) {
// we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed
float motorSpeed = glm::length(_actionMotorVelocity);
float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
float finalMaxMotorSpeed = sensorToWorldScale * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
float speedGrowthTimescale = 2.0f;
float speedIncreaseFactor = 1.8f * _walkSpeedScalar;
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor;
const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED;
const float maxBoostSpeed = sensorToWorldScale * MAX_BOOST_SPEED;
if (_isPushing) {
if (motorSpeed < maxBoostSpeed) {
// an active action motor should never be slower than this
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
motorSpeed += getSensorToWorldScale() * MIN_AVATAR_SPEED * boostCoefficient;
motorSpeed += sensorToWorldScale * MIN_AVATAR_SPEED * boostCoefficient;
} else if (motorSpeed > finalMaxMotorSpeed) {
motorSpeed = finalMaxMotorSpeed;
}
@ -3348,45 +3349,21 @@ void MyAvatar::updateActionMotor(float deltaTime) {
const glm::vec2 currentVel = { direction.x, direction.z };
float scaledSpeed = scaleSpeedByDirection(currentVel, _walkSpeed.get(), _walkBackwardSpeed.get());
// _walkSpeedScalar is a multiplier if we are in sprint mode, otherwise 1.0
_actionMotorVelocity = getSensorToWorldScale() * (scaledSpeed * _walkSpeedScalar) * direction;
}
float previousBoomLength = _boomLength;
float boomChange = getDriveKey(ZOOM);
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
// May need to change view if boom length has changed
if (previousBoomLength != _boomLength) {
qApp->changeViewAsNeeded(_boomLength);
_actionMotorVelocity = sensorToWorldScale * (scaledSpeed * _walkSpeedScalar) * direction;
}
}
void MyAvatar::updatePosition(float deltaTime) {
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
updateActionMotor(deltaTime);
}
vec3 velocity = getWorldVelocity();
float sensorToWorldScale = getSensorToWorldScale();
float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
if (!_characterController.isEnabledAndReady()) {
// _characterController is not in physics simulation but it can still compute its target velocity
updateMotors();
_characterController.computeNewVelocity(deltaTime, velocity);
float speed2 = glm::length(velocity);
if (speed2 > sensorToWorldScale2 * MIN_AVATAR_SPEED_SQUARED) {
// update position ourselves
applyPositionDelta(deltaTime * velocity);
if (_characterController.isEnabledAndReady()) {
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
updateActionMotor(deltaTime);
}
measureMotionDerivatives(deltaTime);
_moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
} else {
float sensorToWorldScale = getSensorToWorldScale();
float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
vec3 velocity = getWorldVelocity();
float speed2 = glm::length2(velocity);
const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
_moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
if (_moving) {
// scan for walkability
glm::vec3 position = getWorldPosition();
@ -3398,6 +3375,18 @@ void MyAvatar::updatePosition(float deltaTime) {
}
}
void MyAvatar::updateViewBoom() {
float previousBoomLength = _boomLength;
float boomChange = getDriveKey(ZOOM);
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
// May need to change view if boom length has changed
if (previousBoomLength != _boomLength) {
qApp->changeViewAsNeeded(_boomLength);
}
}
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
// COLLISION SOUND API in Audio has been removed
}

View file

@ -1732,6 +1732,7 @@ private:
void updateOrientation(float deltaTime);
void updateActionMotor(float deltaTime);
void updatePosition(float deltaTime);
void updateViewBoom();
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
void initHeadBones();
void initAnimGraph();

View file

@ -54,11 +54,10 @@ void QmlCommerce::openSystemApp(const QString& appName) {
{"GOTO", "hifi/tablet/TabletAddressDialog.qml"},
{"PEOPLE", "hifi/Pal.qml"},
{"WALLET", "hifi/commerce/wallet/Wallet.qml"},
{"MARKET", "/marketplace.html"}
{"MARKET", "hifi/commerce/marketplace/Marketplace.qml"}
};
static const QMap<QString, QString> systemInject{
{"MARKET", "/scripts/system/html/js/marketplacesInject.js"}
};

Some files were not shown because too many files have changed in this diff Show more