Merged with samcake/black

This commit is contained in:
Olivier Prat 2018-01-25 09:27:48 +01:00
commit f3aac2a7b5
371 changed files with 8439 additions and 4613 deletions
.gitignoreBUILD_ANDROID.mdBUILD_LINUX_CHEATSHEET.mdCMakeLists.txt
android
assignment-client/src
cmake/macros
domain-server/src
interface

7
.gitignore vendored
View file

@ -18,9 +18,12 @@ local.properties
android/gradle*
android/.gradle
android/app/src/main/jniLibs
android/app/libs
android/app/src/main/res/values/libs.xml
android/app/src/main/assets/bundled
# VSCode
# List taken from Github Global Ignores master@435c4d92
# List taken from Github Global Ignores master@435c4d92
# https://github.com/github/gitignore/commits/master/Global/VisualStudioCode.gitignore
.vscode/*
!.vscode/settings.json
@ -66,7 +69,7 @@ gvr-interface/libs/*
# ignore files for various dev environments
TAGS
*.sw[po]
*.qmlc
*.jsc
# ignore QML compilation output
*.qmlc

View file

@ -2,9 +2,9 @@ Please read the [general build guide](BUILD.md) for information on building othe
# Dependencies
*Currently Android building is only supported on 64 bit Linux host environments*
Building is currently supported on OSX, Windows and Linux platforms, but developers intending to do work on the library dependencies are strongly urged to use 64 bit Linux as a build platform
You will need the following tools to build our Android targets.
You will need the following tools to build Android targets.
* [Gradle](https://gradle.org/install/)
* [Android Studio](https://developer.android.com/studio/index.html)

27
BUILD_LINUX_CHEATSHEET.md Normal file
View file

@ -0,0 +1,27 @@
# this guide is specific to Ubuntu 16.04.
# deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
sudo su -
apt-get -y update
apt-get install -y software-properties-common
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 15FF1AAE
add-apt-repository "deb http://debian.highfidelity.com stable main"
apt-get -y update
apt-get install -y hifi-domain-server
apt-get install -y hifi-assignment-client
# When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
apt-get install -y hifi-dev-domain-server
apt-get install -y hifi-dev-assignment-client
# domain server and assignment clients should already be running. The processes are controlled via:
systemctl start hifi-domain-server
systemctl stop hifi-domain-server
# Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100).
# The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
# As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
To do this you can modify /etc/crontab by adding the following lines
0 */1 * * * root apt-get update
1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server
2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client

View file

@ -39,6 +39,19 @@ else()
option(BUILD_TESTS "Build tests" ON)
endif()
if (ANDROID)
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
set(PLATFORM_GL_BACKEND gpu-gles)
else ()
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
set(PLATFORM_GL_BACKEND gpu-gl)
endif ()
foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS})
list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}")
endforeach()
option(BUILD_INSTALLER "Build installer" ON)
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})

View file

@ -1,10 +1,26 @@
set(TARGET_NAME native-lib)
setup_hifi_library()
link_hifi_libraries(shared networking gl gpu gpu-gles image fbx render-utils physics)
setup_hifi_library(Gui Qml Quick)
# Minimal dependencies for testing UI compositing
#link_hifi_libraries(shared networking gl ui)
link_hifi_libraries(
shared networking octree
script-engine recording trackers
gl ktx image gpu gpu-gles render render-utils
physics
audio audio-client
ui midi controllers pointers
model model-networking fbx animation
entities entities-renderer
avatars avatars-renderer
ui-plugins input-plugins
# display-plugins
# auto-updater
)
target_link_libraries(native-lib android log m)
target_opengl()
target_googlevr()
target_bullet()

View file

@ -1,13 +1,10 @@
apply plugin: 'com.android.application'
ext.RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
ext.RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
ext.BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : ''
android {
compileSdkVersion 26
defaultConfig {
applicationId "org.saintandreas.testapp"
applicationId "com.highfidelity.iface"
minSdkVersion 24
targetSdkVersion 26
versionCode 1
@ -56,8 +53,8 @@ android {
}
dependencies {
compile fileTree(dir: "${project.rootDir}/libraries/jar", include: 'QtAndroid-bundled.jar')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.vr:sdk-audio:1.80.0'
compile 'com.google.vr:sdk-base:1.80.0'
implementation 'com.google.vr:sdk-audio:1.80.0'
implementation 'com.google.vr:sdk-base:1.80.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}

View file

@ -1,28 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.saintandreas.testapp">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.gvrinterface">
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
<uses-feature android:glEsVersion="0x00030001" android:required="true" />
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
<application
android:name="org.qtproject.qt5.android.bindings.QtApplication"
android:hardwareAccelerated="true"
android:allowBackup="true"
android:screenOrientation="unspecified"
android:theme="@style/NoSystemUI"
android:icon="@mipmap/ic_launcher"
android:launchMode="singleTop"
android:roundIcon="@mipmap/ic_launcher_round">
<activity
android:name=".MainActivity"
android:name=".InterfaceActivity"
android:label="@string/app_name"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
android:launchMode="singleTop"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden|screenSize"
android:resizeableActivity="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="native-lib"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
<meta-data android:name="android.app.background_running" android:value="false"/>
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,19 @@
import QtQuick 2.7
import QtWebView 1.1
Rectangle {
id: window
anchors.fill: parent
color: "red"
ColorAnimation on color { from: "blue"; to: "yellow"; duration: 1000; loops: Animation.Infinite }
Text {
text: "Hello"
anchors.top: parent.top
}
WebView {
anchors.fill: parent
anchors.margins: 10
url: "http://doc.qt.io/qt-5/qml-qtwebview-webview.html"
}
}

View file

@ -1,50 +0,0 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <vr/gvr/capi/include/gvr.h>
namespace googlevr {
// Convert a GVR matrix to GLM matrix
glm::mat4 toGlm(const gvr::Mat4f &matrix) {
glm::mat4 result;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
result[j][i] = matrix.m[i][j];
}
}
return result;
}
// Given a field of view in degrees, compute the corresponding projection
// matrix.
glm::mat4 perspectiveMatrixFromView(const gvr::Rectf& fov, float z_near, float z_far) {
const float x_left = -std::tan(fov.left * M_PI / 180.0f) * z_near;
const float x_right = std::tan(fov.right * M_PI / 180.0f) * z_near;
const float y_bottom = -std::tan(fov.bottom * M_PI / 180.0f) * z_near;
const float y_top = std::tan(fov.top * M_PI / 180.0f) * z_near;
const float Y = (2 * z_near) / (y_top - y_bottom);
const float A = (x_right + x_left) / (x_right - x_left);
const float B = (y_top + y_bottom) / (y_top - y_bottom);
const float C = (z_near + z_far) / (z_near - z_far);
const float D = (2 * z_near * z_far) / (z_near - z_far);
glm::mat4 result { 0 };
result[2][0] = A;
result[1][1] = Y;
result[2][1] = B;
result[2][2] = C;
result[3][2] = D;
result[2][3] = -1;
return result;
}
glm::quat toGlm(const gvr::ControllerQuat& q) {
glm::quat result;
result.w = q.qw;
result.x = q.qx;
result.y = q.qy;
result.z = q.qz;
return result;
}
}

View file

@ -0,0 +1,153 @@
#include <jni.h>
#include <android/log.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlFileSelector>
#include <QtQuick/QQuickView>
#include <QtGui/QOpenGLContext>
#include <QtCore/QLoggingCategory>
#include <QtWebView/QtWebView>
#include <gl/Config.h>
#include <gl/OffscreenGLCanvas.h>
#include <gl/GLWindow.h>
#include <ui/OffscreenQmlSurface.h>
Q_LOGGING_CATEGORY(gpugllogging, "hifi.gl")
bool checkGLError(const char* name) {
GLenum error = glGetError();
if (!error) {
return false;
} else {
switch (error) {
case GL_INVALID_ENUM:
qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_INVALID_VALUE:
qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
break;
case GL_INVALID_OPERATION:
qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_OUT_OF_MEMORY:
qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
break;
default:
qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error;
break;
}
return true;
}
}
bool checkGLErrorDebug(const char* name) {
return checkGLError(name);
}
int QtMsgTypeToAndroidPriority(QtMsgType type) {
int priority = ANDROID_LOG_UNKNOWN;
switch (type) {
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
case QtInfoMsg: priority = ANDROID_LOG_INFO; break;
default: break;
}
return priority;
}
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
__android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str());
}
void qt_gl_set_global_share_context(QOpenGLContext *context);
int main(int argc, char* argv[])
{
qInstallMessageHandler(messageHandler);
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
QGuiApplication app(argc,argv);
app.setOrganizationName("QtProject");
app.setOrganizationDomain("qt-project.org");
app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());
QtWebView::initialize();
qputenv("QSG_RENDERER_DEBUG", (QStringList() << "render" << "build" << "change" << "upload" << "roots" << "dump").join(';').toUtf8());
OffscreenGLCanvas sharedCanvas;
if (!sharedCanvas.create()) {
qFatal("Unable to create primary offscreen context");
}
qt_gl_set_global_share_context(sharedCanvas.getContext());
auto globalContext = QOpenGLContext::globalShareContext();
GLWindow window;
window.create();
window.setGeometry(qApp->primaryScreen()->availableGeometry());
window.createContext(globalContext);
if (!window.makeCurrent()) {
qFatal("Unable to make primary window GL context current");
}
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
static const ivec2 offscreenSize { 640, 480 };
OffscreenQmlSurface::setSharedContext(sharedCanvas.getContext());
OffscreenQmlSurface* qmlSurface = new OffscreenQmlSurface();
qmlSurface->create();
qmlSurface->resize(fromGlm(offscreenSize));
qmlSurface->load("qrc:///simple.qml");
qmlSurface->resume();
auto discardLambda = qmlSurface->getDiscardLambda();
window.showFullScreen();
QTimer timer;
timer.setInterval(10);
timer.setSingleShot(false);
OffscreenQmlSurface::TextureAndFence currentTextureAndFence;
timer.connect(&timer, &QTimer::timeout, &app, [&]{
window.makeCurrent();
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
if (qmlSurface->fetchTexture(newTextureAndFence)) {
if (currentTextureAndFence.first) {
discardLambda(currentTextureAndFence.first, currentTextureAndFence.second);
}
currentTextureAndFence = newTextureAndFence;
}
checkGLErrorDebug(__FUNCTION__);
if (currentTextureAndFence.second) {
glWaitSync((GLsync)currentTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
glDeleteSync((GLsync)currentTextureAndFence.second);
currentTextureAndFence.second = nullptr;
}
if (currentTextureAndFence.first) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, currentTextureAndFence.first, 0);
glBlitFramebuffer(0, 0, offscreenSize.x, offscreenSize.y, 100, 100, offscreenSize.x + 100, offscreenSize.y + 100, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
window.swapBuffers();
window.doneCurrent();
});
timer.start();
return app.exec();
}

View file

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>simple.qml</file>
<file>+android/simple.qml</file>
</qresource>
</RCC>

View file

@ -1,73 +0,0 @@
#include <jni.h>
#include <android/log.h>
#include <QtCore/QDebug>
#include "renderer.h"
int QtMsgTypeToAndroidPriority(QtMsgType type) {
int priority = ANDROID_LOG_UNKNOWN;
switch (type) {
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
case QtInfoMsg: priority = ANDROID_LOG_INFO; break;
default: break;
}
return priority;
}
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
__android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str());
}
static jlong toJni(NativeRenderer *renderer) {
return reinterpret_cast<intptr_t>(renderer);
}
static NativeRenderer *fromJni(jlong renderer) {
return reinterpret_cast<NativeRenderer*>(renderer);
}
#define JNI_METHOD(r, name) JNIEXPORT r JNICALL Java_org_saintandreas_testapp_MainActivity_##name
extern "C" {
JNI_METHOD(jlong, nativeCreateRenderer)
(JNIEnv *env, jclass clazz, jobject class_loader, jobject android_context, jlong native_gvr_api) {
qInstallMessageHandler(messageHandler);
return toJni(new NativeRenderer());
}
JNI_METHOD(void, nativeDestroyRenderer)
(JNIEnv *env, jclass clazz, jlong renderer) {
delete fromJni(renderer);
}
JNI_METHOD(void, nativeInitializeGl)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->InitializeGl();
}
JNI_METHOD(void, nativeDrawFrame)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->DrawFrame();
}
JNI_METHOD(void, nativeOnTriggerEvent)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->OnTriggerEvent();
}
JNI_METHOD(void, nativeOnPause)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->OnPause();
}
JNI_METHOD(void, nativeOnResume)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->OnResume();
}
} // extern "C"

View file

@ -1,120 +0,0 @@
#include "renderer.h"
#include <mutex>
#include <glm/gtc/matrix_transform.hpp>
#include <QtCore/QDebug>
#include <gl/Config.h>
#include <gl/GLShaders.h>
static const char *kSimepleVertexShader = R"glsl(#version 300 es
#extension GL_OVR_multiview2 : enable
layout(num_views=2) in;
layout(location = 0) in vec4 a_Position;
out vec4 v_Color;
void main() {
v_Color = vec4(a_Position.xyz, 1.0);
gl_Position = vec4(a_Position.xyz, 1.0);
}
)glsl";
static const char *kPassthroughFragmentShader = R"glsl(#version 300 es
precision mediump float;
in vec4 v_Color;
out vec4 FragColor;
void main() { FragColor = v_Color; }
)glsl";
int LoadGLShader(int type, const char *shadercode) {
GLuint result = 0;
std::string shaderError;
static const std::string SHADER_DEFINES;
if (!gl::compileShader(type, shadercode, SHADER_DEFINES, result, shaderError)) {
qWarning() << "QQQ" << __FUNCTION__ << "Shader compile failure" << shaderError.c_str();
}
return result;
}
static void CheckGLError(const char* label) {
int gl_error = glGetError();
if (gl_error != GL_NO_ERROR) {
qWarning("GL error @ %s: %d", label, gl_error);
// Crash immediately to make OpenGL errors obvious.
abort();
}
}
// Contains vertex, normal and other data.
namespace triangle {
static std::array<float, 9> TRIANGLE_VERTS {{
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
}};
}
void NativeRenderer::InitializeGl() {
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kSimepleVertexShader);
const uint32_t fragShader = LoadGLShader(GL_FRAGMENT_SHADER, kPassthroughFragmentShader);
std::string error;
_program = gl::compileProgram({ vertShader, fragShader }, error);
CheckGLError("build program");
glGenBuffers(1, &_geometryBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _geometryBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, triangle::TRIANGLE_VERTS.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CheckGLError("upload vertices");
glGenVertexArrays(1, &_vao);
glBindBuffer(GL_ARRAY_BUFFER, _geometryBuffer);
glBindVertexArray(_vao);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CheckGLError("build vao ");
}
void NativeRenderer::DrawFrame() {
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - start);
glm::vec3 v;
v.r = (float) (now.count() % 1000) / 1000.0f;
v.g = 1.0f - v.r;
v.b = 1.0f;
glClearColor(v.r, v.g, v.b, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(_program);
glBindVertexArray(_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
void NativeRenderer::OnTriggerEvent() {
qDebug() << "QQQ" << __FUNCTION__;
}
void NativeRenderer::OnPause() {
qDebug() << "QQQ" << __FUNCTION__;
}
void NativeRenderer::OnResume() {
qDebug() << "QQQ" << __FUNCTION__;
}

View file

@ -1,21 +0,0 @@
#pragma once
#include <chrono>
#include <array>
#include <glm/glm.hpp>
class NativeRenderer {
public:
void InitializeGl();
void DrawFrame();
void OnTriggerEvent();
void OnPause();
void OnResume();
private:
std::chrono::time_point<std::chrono::system_clock> start { std::chrono::system_clock::now() };
uint32_t _geometryBuffer { 0 };
uint32_t _vao { 0 };
uint32_t _program { 0 };
};

View file

@ -0,0 +1,10 @@
import QtQuick 2.0
Rectangle {
id: window
width: 320
height: 480
focus: true
color: "red"
ColorAnimation on color { from: "red"; to: "yellow"; duration: 1000; loops: Animation.Infinite }
}

View file

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

@ -0,0 +1,951 @@
/*
Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
Contact: http://www.qt.io/licensing/
Commercial License Usage
Licensees holding valid commercial Qt licenses may use this file in
accordance with the commercial license agreement provided with the
Software or, alternatively, in accordance with the terms contained in
a written agreement between you and The Qt Company. For licensing terms
and conditions see http://www.qt.io/terms-conditions. For further
information use the contact form at http://www.qt.io/contact-us.
BSD License Usage
Alternatively, this file may be used under the BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.qtproject.qt5.android.bindings;
import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
@SuppressWarnings("unused")
public class QtActivity extends Activity {
public String APPLICATION_PARAMETERS = null;
public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
public final String[] QT_ANDROID_THEMES = new String[]{"Theme_Holo_Light"};
public final String QT_ANDROID_DEFAULT_THEME = QT_ANDROID_THEMES[0]; // sets the default theme.
private QtActivityLoader m_loader = new QtActivityLoader(this);
public QtActivity() {
}
/////////////////////////// forward all notifications ////////////////////////////
/////////////////////////// Super class calls ////////////////////////////////////
/////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE //////////////////////////
//////////////////////////////////////////////////////////////////////////////////
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyEvent, event);
} else {
return super.dispatchKeyEvent(event);
}
}
public boolean super_dispatchKeyEvent(KeyEvent event) {
return super.dispatchKeyEvent(event);
}
//---------------------------------------------------------------------------
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.dispatchPopulateAccessibilityEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchPopulateAccessibilityEvent, event);
} else {
return super.dispatchPopulateAccessibilityEvent(event);
}
}
public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
return super_dispatchPopulateAccessibilityEvent(event);
}
//---------------------------------------------------------------------------
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (QtApplication.m_delegateObject != null && QtApplication.dispatchTouchEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTouchEvent, ev);
} else {
return super.dispatchTouchEvent(ev);
}
}
public boolean super_dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
//---------------------------------------------------------------------------
@Override
public boolean dispatchTrackballEvent(MotionEvent ev) {
if (QtApplication.m_delegateObject != null && QtApplication.dispatchTrackballEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTrackballEvent, ev);
} else {
return super.dispatchTrackballEvent(ev);
}
}
public boolean super_dispatchTrackballEvent(MotionEvent event) {
return super.dispatchTrackballEvent(event);
}
//---------------------------------------------------------------------------
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (QtApplication.m_delegateObject != null && QtApplication.onActivityResult != null) {
QtApplication.invokeDelegateMethod(QtApplication.onActivityResult, requestCode, resultCode, data);
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
public void super_onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
//---------------------------------------------------------------------------
@Override
protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
if (!QtApplication.invokeDelegate(theme, resid, first).invoked) {
super.onApplyThemeResource(theme, resid, first);
}
}
public void super_onApplyThemeResource(Theme theme, int resid, boolean first) {
super.onApplyThemeResource(theme, resid, first);
}
//---------------------------------------------------------------------------
@Override
protected void onChildTitleChanged(Activity childActivity, CharSequence title) {
if (!QtApplication.invokeDelegate(childActivity, title).invoked) {
super.onChildTitleChanged(childActivity, title);
}
}
public void super_onChildTitleChanged(Activity childActivity, CharSequence title) {
super.onChildTitleChanged(childActivity, title);
}
//---------------------------------------------------------------------------
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (!QtApplication.invokeDelegate(newConfig).invoked) {
super.onConfigurationChanged(newConfig);
}
}
public void super_onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
//---------------------------------------------------------------------------
@Override
public void onContentChanged() {
if (!QtApplication.invokeDelegate().invoked) {
super.onContentChanged();
}
}
public void super_onContentChanged() {
super.onContentChanged();
}
//---------------------------------------------------------------------------
@Override
public boolean onContextItemSelected(MenuItem item) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(item);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onContextItemSelected(item);
}
}
public boolean super_onContextItemSelected(MenuItem item) {
return super.onContextItemSelected(item);
}
//---------------------------------------------------------------------------
@Override
public void onContextMenuClosed(Menu menu) {
if (!QtApplication.invokeDelegate(menu).invoked) {
super.onContextMenuClosed(menu);
}
}
public void super_onContextMenuClosed(Menu menu) {
super.onContextMenuClosed(menu);
}
//---------------------------------------------------------------------------
protected void onCreateHook(Bundle savedInstanceState) {
m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS;
m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES;
m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES;
m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME;
m_loader.onCreate(savedInstanceState);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onCreateHook(savedInstanceState);
}
//---------------------------------------------------------------------------
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (!QtApplication.invokeDelegate(menu, v, menuInfo).invoked) {
super.onCreateContextMenu(menu, v, menuInfo);
}
}
public void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
}
//---------------------------------------------------------------------------
@Override
public CharSequence onCreateDescription() {
QtApplication.InvokeResult res = QtApplication.invokeDelegate();
if (res.invoked) {
return (CharSequence) res.methodReturns;
} else {
return super.onCreateDescription();
}
}
public CharSequence super_onCreateDescription() {
return super.onCreateDescription();
}
//---------------------------------------------------------------------------
@Override
protected Dialog onCreateDialog(int id) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(id);
if (res.invoked) {
return (Dialog) res.methodReturns;
} else {
return super.onCreateDialog(id);
}
}
public Dialog super_onCreateDialog(int id) {
return super.onCreateDialog(id);
}
//---------------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onCreateOptionsMenu(menu);
}
}
public boolean super_onCreateOptionsMenu(Menu menu) {
return super.onCreateOptionsMenu(menu);
}
//---------------------------------------------------------------------------
@Override
public boolean onCreatePanelMenu(int featureId, Menu menu) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onCreatePanelMenu(featureId, menu);
}
}
public boolean super_onCreatePanelMenu(int featureId, Menu menu) {
return super.onCreatePanelMenu(featureId, menu);
}
//---------------------------------------------------------------------------
@Override
public View onCreatePanelView(int featureId) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId);
if (res.invoked) {
return (View) res.methodReturns;
} else {
return super.onCreatePanelView(featureId);
}
}
public View super_onCreatePanelView(int featureId) {
return super.onCreatePanelView(featureId);
}
//---------------------------------------------------------------------------
@Override
public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(outBitmap, canvas);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onCreateThumbnail(outBitmap, canvas);
}
}
public boolean super_onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
return super.onCreateThumbnail(outBitmap, canvas);
}
//---------------------------------------------------------------------------
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(name, context, attrs);
if (res.invoked) {
return (View) res.methodReturns;
} else {
return super.onCreateView(name, context, attrs);
}
}
public View super_onCreateView(String name, Context context, AttributeSet attrs) {
return super.onCreateView(name, context, attrs);
}
//---------------------------------------------------------------------------
@Override
protected void onDestroy() {
super.onDestroy();
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, keyCode, event);
} else {
return super.onKeyDown(keyCode, event);
}
}
public boolean super_onKeyDown(int keyCode, KeyEvent event) {
return super.onKeyDown(keyCode, event);
}
//---------------------------------------------------------------------------
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onKeyMultiple != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyMultiple, keyCode, repeatCount, event);
} else {
return super.onKeyMultiple(keyCode, repeatCount, event);
}
}
public boolean super_onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
return super.onKeyMultiple(keyCode, repeatCount, event);
}
//---------------------------------------------------------------------------
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onKeyUp != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyUp, keyCode, event);
} else {
return super.onKeyUp(keyCode, event);
}
}
public boolean super_onKeyUp(int keyCode, KeyEvent event) {
return super.onKeyUp(keyCode, event);
}
//---------------------------------------------------------------------------
@Override
public void onLowMemory() {
if (!QtApplication.invokeDelegate().invoked) {
super.onLowMemory();
}
}
//---------------------------------------------------------------------------
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, item);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onMenuItemSelected(featureId, item);
}
}
public boolean super_onMenuItemSelected(int featureId, MenuItem item) {
return super.onMenuItemSelected(featureId, item);
}
//---------------------------------------------------------------------------
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onMenuOpened(featureId, menu);
}
}
public boolean super_onMenuOpened(int featureId, Menu menu) {
return super.onMenuOpened(featureId, menu);
}
//---------------------------------------------------------------------------
@Override
protected void onNewIntent(Intent intent) {
if (!QtApplication.invokeDelegate(intent).invoked) {
super.onNewIntent(intent);
}
}
public void super_onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
//---------------------------------------------------------------------------
@Override
public boolean onOptionsItemSelected(MenuItem item) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(item);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onOptionsItemSelected(item);
}
}
public boolean super_onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
//---------------------------------------------------------------------------
@Override
public void onOptionsMenuClosed(Menu menu) {
if (!QtApplication.invokeDelegate(menu).invoked) {
super.onOptionsMenuClosed(menu);
}
}
public void super_onOptionsMenuClosed(Menu menu) {
super.onOptionsMenuClosed(menu);
}
//---------------------------------------------------------------------------
@Override
public void onPanelClosed(int featureId, Menu menu) {
if (!QtApplication.invokeDelegate(featureId, menu).invoked) {
super.onPanelClosed(featureId, menu);
}
}
public void super_onPanelClosed(int featureId, Menu menu) {
super.onPanelClosed(featureId, menu);
}
//---------------------------------------------------------------------------
@Override
protected void onPause() {
super.onPause();
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
QtApplication.invokeDelegate(savedInstanceState);
}
//---------------------------------------------------------------------------
@Override
protected void onPostResume() {
super.onPostResume();
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
if (!QtApplication.invokeDelegate(id, dialog).invoked) {
super.onPrepareDialog(id, dialog);
}
}
public void super_onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
}
//---------------------------------------------------------------------------
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onPrepareOptionsMenu(menu);
}
}
public boolean super_onPrepareOptionsMenu(Menu menu) {
return super.onPrepareOptionsMenu(menu);
}
//---------------------------------------------------------------------------
@Override
public boolean onPreparePanel(int featureId, View view, Menu menu) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, view, menu);
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onPreparePanel(featureId, view, menu);
}
}
public boolean super_onPreparePanel(int featureId, View view, Menu menu) {
return super.onPreparePanel(featureId, view, menu);
}
//---------------------------------------------------------------------------
@Override
protected void onRestart() {
super.onRestart();
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (!QtApplication.invokeDelegate(savedInstanceState).invoked) {
super.onRestoreInstanceState(savedInstanceState);
}
}
public void super_onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
//---------------------------------------------------------------------------
@Override
protected void onResume() {
super.onResume();
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@Override
public Object onRetainNonConfigurationInstance() {
QtApplication.InvokeResult res = QtApplication.invokeDelegate();
if (res.invoked) {
return res.methodReturns;
} else {
return super.onRetainNonConfigurationInstance();
}
}
public Object super_onRetainNonConfigurationInstance() {
return super.onRetainNonConfigurationInstance();
}
//---------------------------------------------------------------------------
@Override
protected void onSaveInstanceState(Bundle outState) {
if (!QtApplication.invokeDelegate(outState).invoked) {
super.onSaveInstanceState(outState);
}
}
public void super_onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
//---------------------------------------------------------------------------
@Override
public boolean onSearchRequested() {
QtApplication.InvokeResult res = QtApplication.invokeDelegate();
if (res.invoked) {
return (Boolean) res.methodReturns;
} else {
return super.onSearchRequested();
}
}
public boolean super_onSearchRequested() {
return super.onSearchRequested();
}
//---------------------------------------------------------------------------
@Override
protected void onStart() {
super.onStart();
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@Override
protected void onStop() {
super.onStop();
QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@Override
protected void onTitleChanged(CharSequence title, int color) {
if (!QtApplication.invokeDelegate(title, color).invoked) {
super.onTitleChanged(title, color);
}
}
public void super_onTitleChanged(CharSequence title, int color) {
super.onTitleChanged(title, color);
}
//---------------------------------------------------------------------------
@Override
public boolean onTouchEvent(MotionEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onTouchEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTouchEvent, event);
} else {
return super.onTouchEvent(event);
}
}
public boolean super_onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
//---------------------------------------------------------------------------
@Override
public boolean onTrackballEvent(MotionEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onTrackballEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTrackballEvent, event);
} else {
return super.onTrackballEvent(event);
}
}
public boolean super_onTrackballEvent(MotionEvent event) {
return super.onTrackballEvent(event);
}
//---------------------------------------------------------------------------
@Override
public void onUserInteraction() {
if (!QtApplication.invokeDelegate().invoked) {
super.onUserInteraction();
}
}
public void super_onUserInteraction() {
super.onUserInteraction();
}
//---------------------------------------------------------------------------
@Override
protected void onUserLeaveHint() {
if (!QtApplication.invokeDelegate().invoked) {
super.onUserLeaveHint();
}
}
public void super_onUserLeaveHint() {
super.onUserLeaveHint();
}
//---------------------------------------------------------------------------
@Override
public void onWindowAttributesChanged(LayoutParams params) {
if (!QtApplication.invokeDelegate(params).invoked) {
super.onWindowAttributesChanged(params);
}
}
public void super_onWindowAttributesChanged(LayoutParams params) {
super.onWindowAttributesChanged(params);
}
//---------------------------------------------------------------------------
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (!QtApplication.invokeDelegate(hasFocus).invoked) {
super.onWindowFocusChanged(hasFocus);
}
}
public void super_onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
}
//---------------------------------------------------------------------------
//////////////// Activity API 5 /////////////
//@ANDROID-5
@Override
public void onAttachedToWindow() {
if (!QtApplication.invokeDelegate().invoked) {
super.onAttachedToWindow();
}
}
public void super_onAttachedToWindow() {
super.onAttachedToWindow();
}
//---------------------------------------------------------------------------
@Override
public void onBackPressed() {
if (!QtApplication.invokeDelegate().invoked) {
super.onBackPressed();
}
}
public void super_onBackPressed() {
super.onBackPressed();
}
//---------------------------------------------------------------------------
@Override
public void onDetachedFromWindow() {
if (!QtApplication.invokeDelegate().invoked) {
super.onDetachedFromWindow();
}
}
public void super_onDetachedFromWindow() {
super.onDetachedFromWindow();
}
//---------------------------------------------------------------------------
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onKeyLongPress != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyLongPress, keyCode, event);
} else {
return super.onKeyLongPress(keyCode, event);
}
}
public boolean super_onKeyLongPress(int keyCode, KeyEvent event) {
return super.onKeyLongPress(keyCode, event);
}
//---------------------------------------------------------------------------
//@ANDROID-5
//////////////// Activity API 8 /////////////
//@ANDROID-8
@Override
protected Dialog onCreateDialog(int id, Bundle args) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(id, args);
if (res.invoked) {
return (Dialog) res.methodReturns;
} else {
return super.onCreateDialog(id, args);
}
}
public Dialog super_onCreateDialog(int id, Bundle args) {
return super.onCreateDialog(id, args);
}
//---------------------------------------------------------------------------
@Override
protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
if (!QtApplication.invokeDelegate(id, dialog, args).invoked) {
super.onPrepareDialog(id, dialog, args);
}
}
public void super_onPrepareDialog(int id, Dialog dialog, Bundle args) {
super.onPrepareDialog(id, dialog, args);
}
//---------------------------------------------------------------------------
//@ANDROID-8
//////////////// Activity API 11 /////////////
//@ANDROID-11
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyShortcutEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyShortcutEvent, event);
} else {
return super.dispatchKeyShortcutEvent(event);
}
}
public boolean super_dispatchKeyShortcutEvent(KeyEvent event) {
return super.dispatchKeyShortcutEvent(event);
}
//---------------------------------------------------------------------------
@Override
public void onActionModeFinished(ActionMode mode) {
if (!QtApplication.invokeDelegate(mode).invoked) {
super.onActionModeFinished(mode);
}
}
public void super_onActionModeFinished(ActionMode mode) {
super.onActionModeFinished(mode);
}
//---------------------------------------------------------------------------
@Override
public void onActionModeStarted(ActionMode mode) {
if (!QtApplication.invokeDelegate(mode).invoked) {
super.onActionModeStarted(mode);
}
}
public void super_onActionModeStarted(ActionMode mode) {
super.onActionModeStarted(mode);
}
//---------------------------------------------------------------------------
@Override
public void onAttachFragment(Fragment fragment) {
if (!QtApplication.invokeDelegate(fragment).invoked) {
super.onAttachFragment(fragment);
}
}
public void super_onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
}
//---------------------------------------------------------------------------
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(parent, name, context, attrs);
if (res.invoked) {
return (View) res.methodReturns;
} else {
return super.onCreateView(parent, name, context, attrs);
}
}
public View super_onCreateView(View parent, String name, Context context,
AttributeSet attrs) {
return super.onCreateView(parent, name, context, attrs);
}
//---------------------------------------------------------------------------
@Override
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onKeyShortcut != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyShortcut, keyCode, event);
} else {
return super.onKeyShortcut(keyCode, event);
}
}
public boolean super_onKeyShortcut(int keyCode, KeyEvent event) {
return super.onKeyShortcut(keyCode, event);
}
//---------------------------------------------------------------------------
@Override
public ActionMode onWindowStartingActionMode(Callback callback) {
QtApplication.InvokeResult res = QtApplication.invokeDelegate(callback);
if (res.invoked) {
return (ActionMode) res.methodReturns;
} else {
return super.onWindowStartingActionMode(callback);
}
}
public ActionMode super_onWindowStartingActionMode(Callback callback) {
return super.onWindowStartingActionMode(callback);
}
//---------------------------------------------------------------------------
//@ANDROID-11
//////////////// Activity API 12 /////////////
//@ANDROID-12
@Override
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
if (QtApplication.m_delegateObject != null && QtApplication.dispatchGenericMotionEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchGenericMotionEvent, ev);
} else {
return super.dispatchGenericMotionEvent(ev);
}
}
public boolean super_dispatchGenericMotionEvent(MotionEvent event) {
return super.dispatchGenericMotionEvent(event);
}
//---------------------------------------------------------------------------
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (QtApplication.m_delegateObject != null && QtApplication.onGenericMotionEvent != null) {
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onGenericMotionEvent, event);
} else {
return super.onGenericMotionEvent(event);
}
}
public boolean super_onGenericMotionEvent(MotionEvent event) {
return super.onGenericMotionEvent(event);
}
//---------------------------------------------------------------------------
//@ANDROID-12
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (QtApplication.m_delegateObject != null && QtApplication.onRequestPermissionsResult != null) {
QtApplication.invokeDelegateMethod(QtApplication.onRequestPermissionsResult, requestCode, permissions, grantResults);
return;
}
}
}

View file

@ -0,0 +1,502 @@
/*
Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
Contact: http://www.qt-project.org/legal
Commercial License Usage
Licensees holding valid commercial Qt licenses may use this file in
accordance with the commercial license agreement provided with the
Software or, alternatively, in accordance with the terms contained in
a written agreement between you and Digia. For licensing terms and
conditions see http://qt.digia.com/licensing. For further information
use the contact form at http://qt.digia.com/contact-us.
BSD License Usage
Alternatively, this file may be used under the BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.qtproject.qt5.android.bindings;
import android.app.AlertDialog;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import dalvik.system.DexClassLoader;
public class QtActivityLoader {
private static final String DEX_PATH_KEY = "dex.path";
private static final String LIB_PATH_KEY = "lib.path";
private static final String NATIVE_LIBRARIES_KEY = "native.libraries";
private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
private static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
private static final String BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id";
private static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id";
private static final String MAIN_LIBRARY_KEY = "main.library";
private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
private static final String EXTRACT_STYLE_KEY = "extract.android.style";
private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option";
private static final int BUFFER_SIZE = 1024;
String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application,
String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
String[] QT_ANDROID_THEMES = null;
String QT_ANDROID_DEFAULT_THEME = null;
private String[] m_qtLibs = null; // required qt libs
private int m_displayDensity = -1;
private ContextWrapper m_context;
private ComponentInfo m_contextInfo;
private Class<?> m_delegateClass;
// this function is used to load and start the loader
private void loadApplication(Bundle loaderParams) {
try {
// add all bundled Qt libs to loader params
ArrayList<String> libs = new ArrayList<>();
String libName = m_contextInfo.metaData.getString("android.app.lib_name");
loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function
loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs);
// load and start QtLoader class
DexClassLoader classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files
m_context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), // directory where optimized DEX files should be written.
loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists)
m_context.getClassLoader()); // parent loader
Class<?> loaderClass = classLoader.loadClass(loaderClassName()); // load QtLoader class
Object qtLoader = loaderClass.newInstance(); // create an instance
Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication",
contextClassName(),
ClassLoader.class,
Bundle.class);
if (!(Boolean) prepareAppMethod.invoke(qtLoader, m_context, classLoader, loaderParams)) {
throw new Exception("");
}
QtApplication.setQtContextDelegate(m_delegateClass, qtLoader);
// now load the application library so it's accessible from this class loader
if (libName != null) {
System.loadLibrary(libName);
}
Method startAppMethod = qtLoader.getClass().getMethod("startApplication");
if (!(Boolean) startAppMethod.invoke(qtLoader)) {
throw new Exception("");
}
} catch (Exception e) {
e.printStackTrace();
AlertDialog errorDialog = new AlertDialog.Builder(m_context).create();
errorDialog.setMessage("Fatal error, your application can't be started.");
errorDialog.setButton(DialogInterface.BUTTON_NEUTRAL, m_context.getResources().getString(android.R.string.ok), (dialog, which) -> {
finish();
});
errorDialog.show();
}
}
static private void copyFile(InputStream inputStream, OutputStream outputStream)
throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int count;
while ((count = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count);
}
}
private void copyAsset(String source, String destination)
throws IOException {
// Already exists, we don't have to do anything
File destinationFile = new File(destination);
if (destinationFile.exists()) {
return;
}
File parentDirectory = destinationFile.getParentFile();
if (!parentDirectory.exists()) {
parentDirectory.mkdirs();
}
destinationFile.createNewFile();
AssetManager assetsManager = m_context.getAssets();
InputStream inputStream = assetsManager.open(source);
OutputStream outputStream = new FileOutputStream(destinationFile);
copyFile(inputStream, outputStream);
inputStream.close();
outputStream.close();
}
private static void createBundledBinary(String source, String destination)
throws IOException {
// Already exists, we don't have to do anything
File destinationFile = new File(destination);
if (destinationFile.exists()) {
return;
}
File parentDirectory = destinationFile.getParentFile();
if (!parentDirectory.exists()) {
parentDirectory.mkdirs();
}
destinationFile.createNewFile();
InputStream inputStream = new FileInputStream(source);
OutputStream outputStream = new FileOutputStream(destinationFile);
copyFile(inputStream, outputStream);
inputStream.close();
outputStream.close();
}
private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) {
File versionFile = new File(pluginsPrefix + "cache.version");
long cacheVersion = 0;
if (versionFile.exists() && versionFile.canRead()) {
try {
DataInputStream inputStream = new DataInputStream(new FileInputStream(versionFile));
cacheVersion = inputStream.readLong();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (cacheVersion != packageVersion) {
deleteRecursively(new File(pluginsPrefix));
return true;
} else {
return false;
}
}
private void extractBundledPluginsAndImports(String pluginsPrefix) throws IOException {
String libsDir = m_context.getApplicationInfo().nativeLibraryDir + "/";
long packageVersion = -1;
try {
PackageInfo packageInfo = m_context.getPackageManager().getPackageInfo(m_context.getPackageName(), 0);
packageVersion = packageInfo.lastUpdateTime;
} catch (Exception e) {
e.printStackTrace();
}
if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion)) {
return;
}
{
File versionFile = new File(pluginsPrefix + "cache.version");
File parentDirectory = versionFile.getParentFile();
if (!parentDirectory.exists()) {
parentDirectory.mkdirs();
}
versionFile.createNewFile();
DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(versionFile));
outputStream.writeLong(packageVersion);
outputStream.close();
}
{
String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY;
if (m_contextInfo.metaData.containsKey(key)) {
String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
for (String bundledImportBinary : list) {
String[] split = bundledImportBinary.split(":");
String sourceFileName = libsDir + split[0];
String destinationFileName = pluginsPrefix + split[1];
createBundledBinary(sourceFileName, destinationFileName);
}
}
}
{
String key = BUNDLED_IN_ASSETS_RESOURCE_ID_KEY;
if (m_contextInfo.metaData.containsKey(key)) {
String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
for (String fileName : list) {
String[] split = fileName.split(":");
String sourceFileName = split[0];
String destinationFileName = pluginsPrefix + split[1];
copyAsset(sourceFileName, destinationFileName);
}
}
}
}
private void deleteRecursively(File directory) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteRecursively(file);
} else {
file.delete();
}
}
directory.delete();
}
}
private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix) {
File newCache = new File(localPrefix);
if (!newCache.exists()) {
{
File oldPluginsCache = new File(oldLocalPrefix + "plugins/");
if (oldPluginsCache.exists() && oldPluginsCache.isDirectory()) {
deleteRecursively(oldPluginsCache);
}
}
{
File oldImportsCache = new File(oldLocalPrefix + "imports/");
if (oldImportsCache.exists() && oldImportsCache.isDirectory()) {
deleteRecursively(oldImportsCache);
}
}
{
File oldQmlCache = new File(oldLocalPrefix + "qml/");
if (oldQmlCache.exists() && oldQmlCache.isDirectory()) {
deleteRecursively(oldQmlCache);
}
}
}
}
public void startApp() {
try {
if (m_contextInfo.metaData.containsKey("android.app.qt_libs_resource_id")) {
int resourceId = m_contextInfo.metaData.getInt("android.app.qt_libs_resource_id");
m_qtLibs = m_context.getResources().getStringArray(resourceId);
}
ArrayList<String> libraryList = new ArrayList<>();
String localPrefix = m_context.getApplicationInfo().dataDir + "/";
String pluginsPrefix = localPrefix + "qt-reserved-files/";
cleanOldCacheIfNecessary(localPrefix, pluginsPrefix);
extractBundledPluginsAndImports(pluginsPrefix);
for (String lib : m_qtLibs) {
libraryList.add(localPrefix + "lib/lib" + lib + ".so");
}
if (m_contextInfo.metaData.containsKey("android.app.load_local_libs")) {
String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":");
for (String lib : extraLibs) {
if (lib.length() > 0) {
if (lib.startsWith("lib/")) {
libraryList.add(localPrefix + lib);
} else {
libraryList.add(pluginsPrefix + lib);
}
}
}
}
Bundle loaderParams = new Bundle();
loaderParams.putString(DEX_PATH_KEY, new String());
if (m_contextInfo.metaData.containsKey("android.app.static_init_classes")) {
loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY,
m_contextInfo.metaData.getString("android.app.static_init_classes").split(":"));
}
loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
String themePath = m_context.getApplicationInfo().dataDir + "/qt-reserved-files/android-style/";
String stylePath = themePath + m_displayDensity + "/";
String extractOption = "full";
if (m_contextInfo.metaData.containsKey("android.app.extract_android_style")) {
extractOption = m_contextInfo.metaData.getString("android.app.extract_android_style");
if (!extractOption.equals("full") && !extractOption.equals("minimal") && !extractOption.equals("none")) {
Log.e(QtApplication.QtTAG, "Invalid extract_android_style option \"" + extractOption + "\", defaulting to full");
extractOption = "full";
}
}
if (!(new File(stylePath)).exists() && !extractOption.equals("none")) {
loaderParams.putString(EXTRACT_STYLE_KEY, stylePath);
loaderParams.putBoolean(EXTRACT_STYLE_MINIMAL_KEY, extractOption.equals("minimal"));
}
if (extractOption.equals("full")) {
ENVIRONMENT_VARIABLES += "\tQT_USE_ANDROID_NATIVE_STYLE=1";
}
ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEMES_ROOT_PATH=" + themePath;
loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES
+ "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml"
+ "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports"
+ "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins");
String appParams = null;
if (APPLICATION_PARAMETERS != null) {
appParams = APPLICATION_PARAMETERS;
}
Intent intent = getIntent();
if (intent != null) {
String parameters = intent.getStringExtra("applicationArguments");
if (parameters != null) {
if (appParams == null) {
appParams = parameters;
} else {
appParams += '\t' + parameters;
}
}
}
if (m_contextInfo.metaData.containsKey("android.app.arguments")) {
String parameters = m_contextInfo.metaData.getString("android.app.arguments");
if (appParams == null) {
appParams = parameters;
} else {
appParams += '\t' + parameters;
}
}
if (appParams != null) {
loaderParams.putString(APPLICATION_PARAMETERS_KEY, appParams.replace(' ', '\t').trim());
}
loadApplication(loaderParams);
} catch (Exception e) {
Log.e(QtApplication.QtTAG, "Can't create main activity", e);
}
}
QtActivity m_activity;
QtActivityLoader(QtActivity activity) {
m_context = activity;
m_delegateClass = QtActivity.class;
m_activity = activity;
}
protected String loaderClassName() {
return "org.qtproject.qt5.android.QtActivityDelegate";
}
protected Class<?> contextClassName() {
return android.app.Activity.class;
}
protected void finish() {
m_activity.finish();
}
protected String getTitle() {
return (String) m_activity.getTitle();
}
protected void runOnUiThread(Runnable run) {
m_activity.runOnUiThread(run);
}
Intent getIntent() {
return m_activity.getIntent();
}
public void onCreate(Bundle savedInstanceState) {
try {
m_contextInfo = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA);
int theme = ((ActivityInfo) m_contextInfo).getThemeResource();
for (Field f : Class.forName("android.R$style").getDeclaredFields()) {
if (f.getInt(null) == theme) {
QT_ANDROID_THEMES = new String[]{f.getName()};
QT_ANDROID_DEFAULT_THEME = f.getName();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
finish();
return;
}
try {
m_activity.setTheme(Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME).getInt(null));
} catch (Exception e) {
e.printStackTrace();
}
m_activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) {
QtApplication.invokeDelegateMethod(QtApplication.onCreate, savedInstanceState);
return;
}
m_displayDensity = m_activity.getResources().getDisplayMetrics().densityDpi;
ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEME=" + QT_ANDROID_DEFAULT_THEME
+ "/\tQT_ANDROID_THEME_DISPLAY_DPI=" + m_displayDensity + "\t";
if (null == m_activity.getLastNonConfigurationInstance()) {
if (m_contextInfo.metaData.containsKey("android.app.background_running")
&& m_contextInfo.metaData.getBoolean("android.app.background_running")) {
ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=0\t";
} else {
ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=1\t";
}
if (m_contextInfo.metaData.containsKey("android.app.auto_screen_scale_factor")
&& m_contextInfo.metaData.getBoolean("android.app.auto_screen_scale_factor")) {
ENVIRONMENT_VARIABLES += "QT_AUTO_SCREEN_SCALE_FACTOR=1\t";
}
startApp();
}
}
}

View file

@ -0,0 +1,168 @@
/*
Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
Contact: http://www.qt.io/licensing/
Commercial License Usage
Licensees holding valid commercial Qt licenses may use this file in
accordance with the commercial license agreement provided with the
Software or, alternatively, in accordance with the terms contained in
a written agreement between you and The Qt Company. For licensing terms
and conditions see http://www.qt.io/terms-conditions. For further
information use the contact form at http://www.qt.io/contact-us.
BSD License Usage
Alternatively, this file may be used under the BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.qtproject.qt5.android.bindings;
import android.app.Application;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class QtApplication extends Application {
public final static String QtTAG = "Qt";
public static Object m_delegateObject = null;
public static Map<String, ArrayList<Method>> m_delegateMethods = new HashMap<>();
public static Method dispatchKeyEvent = null;
public static Method dispatchPopulateAccessibilityEvent = null;
public static Method dispatchTouchEvent = null;
public static Method dispatchTrackballEvent = null;
public static Method onKeyDown = null;
public static Method onKeyMultiple = null;
public static Method onKeyUp = null;
public static Method onTouchEvent = null;
public static Method onTrackballEvent = null;
public static Method onActivityResult = null;
public static Method onCreate = null;
public static Method onKeyLongPress = null;
public static Method dispatchKeyShortcutEvent = null;
public static Method onKeyShortcut = null;
public static Method dispatchGenericMotionEvent = null;
public static Method onGenericMotionEvent = null;
public static Method onRequestPermissionsResult = null;
private static String activityClassName;
public static void setQtContextDelegate(Class<?> clazz, Object listener) {
m_delegateObject = listener;
activityClassName = clazz.getCanonicalName();
ArrayList<Method> delegateMethods = new ArrayList<>();
for (Method m : listener.getClass().getMethods()) {
if (m.getDeclaringClass().getName().startsWith("org.qtproject.qt5.android")) {
delegateMethods.add(m);
}
}
ArrayList<Field> applicationFields = new ArrayList<>();
for (Field f : QtApplication.class.getFields()) {
if (f.getDeclaringClass().getName().equals(QtApplication.class.getName())) {
applicationFields.add(f);
}
}
for (Method delegateMethod : delegateMethods) {
try {
clazz.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes());
if (m_delegateMethods.containsKey(delegateMethod.getName())) {
m_delegateMethods.get(delegateMethod.getName()).add(delegateMethod);
} else {
ArrayList<Method> delegateSet = new ArrayList<>();
delegateSet.add(delegateMethod);
m_delegateMethods.put(delegateMethod.getName(), delegateSet);
}
for (Field applicationField : applicationFields) {
if (applicationField.getName().equals(delegateMethod.getName())) {
try {
applicationField.set(null, delegateMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
}
}
}
@Override
public void onTerminate() {
if (m_delegateObject != null && m_delegateMethods.containsKey("onTerminate")) {
invokeDelegateMethod(m_delegateMethods.get("onTerminate").get(0));
}
super.onTerminate();
}
static class InvokeResult {
boolean invoked = false;
Object methodReturns = null;
}
private static int stackDeep = -1;
public static InvokeResult invokeDelegate(Object... args) {
InvokeResult result = new InvokeResult();
if (m_delegateObject == null) {
return result;
}
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
if (-1 == stackDeep) {
for (int it = 0; it < elements.length; it++) {
if (elements[it].getClassName().equals(activityClassName)) {
stackDeep = it;
break;
}
}
}
if (-1 == stackDeep) {
return result;
}
final String methodName = elements[stackDeep].getMethodName();
if (!m_delegateMethods.containsKey(methodName)) {
return result;
}
for (Method m : m_delegateMethods.get(methodName)) {
if (m.getParameterTypes().length == args.length) {
result.methodReturns = invokeDelegateMethod(m, args);
result.invoked = true;
return result;
}
}
return result;
}
public static Object invokeDelegateMethod(Method m, Object... args) {
try {
return m.invoke(m_delegateObject, args);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View file

@ -1,93 +0,0 @@
package org.saintandreas.testapp;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.View;
import com.google.vr.ndk.base.AndroidCompat;
import com.google.vr.ndk.base.GvrLayout;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MainActivity extends Activity {
private final static int IMMERSIVE_STICKY_VIEW_FLAGS = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
static {
System.loadLibrary("gvr");
System.loadLibrary("native-lib");
}
private long nativeRenderer;
private GLSurfaceView surfaceView;
private native long nativeCreateRenderer(ClassLoader appClassLoader, Context context);
private native void nativeDestroyRenderer(long renderer);
private native void nativeInitializeGl(long renderer);
private native void nativeDrawFrame(long renderer);
private native void nativeOnTriggerEvent(long renderer);
private native void nativeOnPause(long renderer);
private native void nativeOnResume(long renderer);
class NativeRenderer implements GLSurfaceView.Renderer {
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeInitializeGl(nativeRenderer); }
@Override public void onSurfaceChanged(GL10 gl, int width, int height) { }
@Override public void onDrawFrame(GL10 gl) {
nativeDrawFrame(nativeRenderer);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setImmersiveSticky();
getWindow()
.getDecorView()
.setOnSystemUiVisibilityChangeListener((int visibility)->{
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { setImmersiveSticky(); }
});
nativeRenderer = nativeCreateRenderer(
getClass().getClassLoader(),
getApplicationContext());
surfaceView = new GLSurfaceView(this);
surfaceView.setEGLContextClientVersion(3);
surfaceView.setEGLConfigChooser(8, 8, 8, 0, 0, 0);
surfaceView.setPreserveEGLContextOnPause(true);
surfaceView.setRenderer(new NativeRenderer());
setContentView(surfaceView);
}
@Override
protected void onDestroy() {
super.onDestroy();
nativeDestroyRenderer(nativeRenderer);
nativeRenderer = 0;
}
@Override
protected void onPause() {
surfaceView.queueEvent(()->nativeOnPause(nativeRenderer));
surfaceView.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
surfaceView.onResume();
surfaceView.queueEvent(()->nativeOnResume(nativeRenderer));
}
private void setImmersiveSticky() {
getWindow().getDecorView().setSystemUiVisibility(IMMERSIVE_STICKY_VIEW_FLAGS);
}
}

Binary file not shown.

After

(image error) Size: 9.7 KiB

View file

@ -1,3 +1,13 @@
import de.undercouch.gradle.tasks.download.Download
import de.undercouch.gradle.tasks.download.Verify
import groovy.io.FileType
import groovy.json.JsonSlurper
import groovy.xml.XmlUtil
import org.apache.tools.ant.taskdefs.condition.Os
import java.util.regex.Matcher
import java.util.regex.Pattern
buildscript {
repositories {
jcenter()
@ -10,6 +20,7 @@ buildscript {
plugins {
id 'de.undercouch.download' version '3.3.0'
id "cz.malohlava" version "1.0.3"
}
allprojects {
@ -19,65 +30,290 @@ allprojects {
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : ''
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
QT5_DEPS = [
'Qt5Core',
'Qt5Gui',
'Qt5Multimedia',
'Qt5Network',
'Qt5OpenGL',
'Qt5Qml',
'Qt5Quick',
'Qt5Script',
'Qt5ScriptTools',
'Qt5WebChannel',
'Qt5WebSockets',
'Qt5Widgets',
'Qt5XmlPatterns',
// Android specific
'Qt5AndroidExtras',
'Qt5WebView',
]
}
def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def jniFolder = new File('app/src/main/jniLibs/arm64-v8a')
import org.apache.tools.ant.taskdefs.condition.Os
def appDir = new File(projectDir, 'app')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = 'https://hifi-public.s3.amazonaws.com/austin/android/'
def qtFile='qt-5.9.3_linux_armv8-libcpp.tgz'
def qtChecksum='547da3547d5690144e23d6504c6d6e91'
def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt'
if (Os.isFamily(Os.FAMILY_MAC)) {
qtFile = 'qt-5.9.3_osx_armv8-libcpp.tgz'
qtChecksum='6fa3e068cfdee863fc909b294a3a0cc6'
qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
qtFile = 'qt-5.9.3_win_armv8-libcpp.tgz'
qtChecksum='3a757378a7e9dbbfc662177e0eb46408'
qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtChecksum='a93d22c0c59aa112fda18c4c6d157d17'
qtVersionId='0Bl9NSUWb5CBKLT_NXaxTt75SNBBZ9sB'
}
def packages = [
qt: [
file: qtFile,
versionId: qtVersionId,
checksum: qtChecksum,
sharedLibFolder: '',
includeLibs: ['lib/*.so', 'plugins/*/*.so']
],
bullet: [
file: 'bullet-2.83_armv8-libcpp.tgz',
checksum: '2c558d604fce337f5eba3eb7ec1252fd'
versionId: 'ljb7v.1IjVRqyopUKVDbVnLA4z88J8Eo',
checksum: '2c558d604fce337f5eba3eb7ec1252fd',
],
draco: [
file: 'draco_armv8-libcpp.tgz',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd'
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
],
glm: [
file: 'glm-0.9.8.tgz',
versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2',
checksum: 'd2b42cee31d2bc17bab6ce69e6b3f30a',
],
gvr: [
file: 'gvrsdk_v1.101.0.tgz',
checksum: '57fd02baa069176ba18597a29b6b4fc7'
versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY',
checksum: '57fd02baa069176ba18597a29b6b4fc7',
],
openssl: [
file: 'openssl-1.1.0g_armv8.tgz',
checksum: 'cabb681fbccd79594f65fcc266e02f32'
versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9',
checksum: 'cabb681fbccd79594f65fcc266e02f32',
],
polyvox: [
file: 'polyvox_armv8-libcpp.tgz',
checksum: '5c918288741ee754c16aeb12bb46b9e1',
versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3',
checksum: '349ad5b72aaf2749ca95d847e60c5314',
sharedLibFolder: 'lib',
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so']
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
],
tbb: [
file: 'tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF',
checksum: '20768f298f53b195e71b414b0ae240c4',
sharedLibFolder: 'lib/release',
includeLibs: ['libtbb.so', 'libtbbmalloc.so']
includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
]
]
def scribeLocalFile='scribe' + EXEC_SUFFIX
def scribeFile='scribe_linux_x86_64'
def scribeChecksum='c98678d9726bd8bbf1bab792acf3ff6c'
if (Os.isFamily(Os.FAMILY_MAC)) {
scribeFile = 'scribe_osx_x86_64'
scribeChecksum='a137ad62c1bf7cca739da219544a9a16'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
scribeFile = 'scribe_win32_x86_64.exe'
scribeChecksum='75c2ce9ed45d17de375e3988bfaba816'
}
def options = [
files: new TreeSet<File>(),
features: new HashSet<String>(),
permissions: new HashSet<String>()
]
def qmlRoot = new File(HIFI_ANDROID_PRECOMPILED, 'qt')
def captureOutput = { String command ->
def proc = command.execute()
def sout = new StringBuilder(), serr = new StringBuilder()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(10000)
def errorOutput = serr.toString()
if (!errorOutput.isEmpty()) {
throw new GradleException("Command '${command}' failed with error ${errorOutput}")
}
return sout.toString()
}
def relativize = { File root, File absolute ->
def relativeURI = root.toURI().relativize(absolute.toURI())
return new File(relativeURI.toString())
}
def scanQmlImports = { File qmlRootPath ->
def qmlImportCommandFile = new File(qmlRoot, 'bin/qmlimportscanner' + EXEC_SUFFIX)
if (!qmlImportCommandFile.exists()) {
throw new GradleException('Unable to find required qmlimportscanner executable at ' + qmlImportCommandFile.parent.toString())
}
def command = qmlImportCommandFile.absolutePath +
" -rootPath ${qmlRootPath.absolutePath}" +
" -importPath ${qmlRoot.absolutePath}/qml"
def commandResult = captureOutput(command)
new JsonSlurper().parseText(commandResult).each {
if (!it.containsKey('path')) {
println "Warning: QML import could not be resolved in any of the import paths: ${it.name}"
return
}
def file = new File(it.path)
// Ignore non-existent files
if (!file.exists()) {
return
}
// Ignore files in the import path
if (file.canonicalPath.startsWith(qmlRootPath.canonicalPath)) {
return
}
if (file.isFile()) {
options.files.add(file)
} else {
file.eachFileRecurse(FileType.FILES, {
options.files.add(it)
})
}
}
}
def parseQtDependencies = { List qtLibs ->
qtLibs.each({
def libFile = new File(qmlRoot, "lib/lib${it}.so")
options.files.add(libFile)
def androidDeps = new File(qmlRoot, "lib/${it}-android-dependencies.xml")
if (!libFile.exists()) return
if (!androidDeps.exists()) return
new XmlSlurper().parse(androidDeps).dependencies.lib.depends.'*'.each{ node ->
switch (node.name()) {
case 'lib':
case 'bundled':
def relativeFilename = node.@file.toString()
// Special case, since this is handled by qmlimportscanner instead
if (relativeFilename.startsWith('qml'))
return
def file = new File(qmlRoot, relativeFilename)
if (!file.exists())
return
if (file.isFile()) {
options.files.add(file)
} else {
file.eachFileRecurse(FileType.FILES, { options.files.add(it) })
}
break
case 'jar':
if (node.@bundling == "1") {
def jar = new File(qmlRoot, node.@file.toString())
if (!jar.exists()) {
throw new GradleException('Unable to find required JAR ' + jar.path)
}
options.files.add(jar)
}
break
case 'permission':
options.permissions.add(node.@name)
break
case 'feature':
options.features.add(node.@name)
break
default:
throw new GradleException('Unhandled Android Dependency node ' + node.name())
}
}
})
}
def generateLibsXml = {
def libDestinationDirectory = jniFolder
def jarDestinationDirectory = new File(appDir, 'libs')
def assetDestinationDirectory = new File(appDir, 'src/main/assets/bundled');
def libsXmlFile = new File(appDir, 'src/main/res/values/libs.xml')
def libPrefix = 'lib' + File.separator
def jarPrefix = 'jar' + File.separator
def xmlParser = new XmlParser()
def libsXmlRoot = xmlParser.parseText('<?xml version="1.0" encoding="UTF-8"?><resources/>')
def qtLibsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'qt_libs'])
def bundledLibsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'bundled_in_lib'])
def bundledAssetsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'bundled_in_assets'])
options.files.each {
def sourceFile = it
if (!sourceFile.exists()) {
throw new GradleException("Unable to find dependency file " + sourceFile.toString())
}
def relativePath = relativize( qmlRoot, sourceFile ).toString()
def destinationFile
if (relativePath.endsWith('.so')) {
def garbledFileName
if (relativePath.startsWith(libPrefix)) {
garbledFileName = relativePath.substring(libPrefix.size())
Pattern p = ~/lib(Qt5.*).so/
Matcher m = p.matcher(garbledFileName)
assert m.matches()
def libName = m.group(1)
xmlParser.createNode(qtLibsNode, 'item', [:]).setValue(libName)
} else {
garbledFileName = 'lib' + relativePath.replace(File.separator, '_'[0])
xmlParser.createNode(bundledLibsNode, 'item', [:]).setValue("${garbledFileName}:${relativePath}".replace(File.separator, '/'))
}
destinationFile = new File(libDestinationDirectory, garbledFileName)
} else if (relativePath.startsWith('jar')) {
destinationFile = new File(jarDestinationDirectory, relativePath.substring(jarPrefix.size()))
} else {
xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("bundled/${relativePath}:${relativePath}".replace(File.separator, '/'))
destinationFile = new File(assetDestinationDirectory, relativePath)
}
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
assert destinationFile.exists() && destinationFile.isFile()
}
def xml = XmlUtil.serialize(libsXmlRoot)
new FileWriter(libsXmlFile).withPrintWriter { writer ->
writer.write(xml)
}
}
task downloadDependencies {
doLast {
packages.each { entry ->
def filename = entry.value['file'];
def url = baseUrl + filename;
if (entry.value.containsKey('versionId')) {
url = url + '?versionId=' + entry.value['versionId']
}
download {
src url
dest new File(baseFolder, filename)
@ -87,8 +323,6 @@ task downloadDependencies {
}
}
import de.undercouch.gradle.tasks.download.Verify
task verifyQt(type: Verify) { def p = packages['qt']; src new File(baseFolder, p['file']); checksum p['checksum']; }
task verifyBullet(type: Verify) { def p = packages['bullet']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyDraco(type: Verify) { def p = packages['draco']; src new File(baseFolder, p['file']); checksum p['checksum'] }
@ -109,19 +343,26 @@ verifyDependencyDownloads.dependsOn verifyTBB
task extractDependencies(dependsOn: verifyDependencyDownloads) {
doLast {
packages.each { entry ->
def folder = entry.key;
def filename = entry.value['file'];
def folder = entry.key
def filename = entry.value['file']
def localFile = new File(HIFI_ANDROID_PRECOMPILED, filename)
def localFolder = new File(HIFI_ANDROID_PRECOMPILED, folder)
def fileTree;
if (filename.endsWith('zip')) {
fileTree = zipTree(localFile)
} else {
fileTree = tarTree(resources.gzip(localFile))
}
copy {
from tarTree(resources.gzip(localFile))
from fileTree
into localFolder
}
}
}
}
task copyDependencies(dependsOn: extractDependencies) {
// Copies the non Qt dependencies. Qt dependencies (primary libraries and plugins) are handled by the qtBundle task
task copyDependencies(dependsOn: [ extractDependencies ]) {
doLast {
packages.each { entry ->
def packageName = entry.key
@ -132,31 +373,17 @@ task copyDependencies(dependsOn: extractDependencies) {
if (currentPackage.containsKey('includeLibs')) {
currentPackage['includeLibs'].each { includeSpec -> tree.include includeSpec }
}
tree.visit { element ->
tree.visit { element ->
if (!element.file.isDirectory()) {
println "Copying " + element.file + " to " + jniFolder
copy { from element.file; into jniFolder }
}
}
}
}
}
}
}
}
def scribeFile='scribe_linux_x86_64'
def scribeLocalFile='scribe'
def scribeChecksum='c98678d9726bd8bbf1bab792acf3ff6c'
if (Os.isFamily(Os.FAMILY_MAC)) {
scribeFile = 'scribe_osx_x86_64'
scribeChecksum='a137ad62c1bf7cca739da219544a9a16'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
scribeFile = 'scribe_win32_x86_64.exe'
scribeLocalFile = 'scribe.exe'
scribeChecksum='75c2ce9ed45d17de375e3988bfaba816'
}
import de.undercouch.gradle.tasks.download.Download
task downloadScribe(type: Download) {
src baseUrl + scribeFile
dest new File(baseFolder, scribeLocalFile)
@ -203,14 +430,101 @@ task extractGvrBinaries(dependsOn: extractDependencies) {
}
task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBinaries]) {
// Copy required Qt main libraries and required plugins based on the predefined list here
// FIXME eventually we would like to use the readelf functionality to automatically detect dependencies
// from our built applications and use that during the full build process. However doing so would mean
// hooking existing Android build tasks since the output from the qtBundle logic adds JNI libs, asset
// files and resources files and potentially modifies the AndroidManifest.xml
task qtBundle {
doLast {
parseQtDependencies(QT5_DEPS)
//def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/")
def qmlImportFolder = new File("${projectDir}/app/src/main/cpp")
scanQmlImports(qmlImportFolder)
generateLibsXml()
}
}
task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBinaries, qtBundle]) { }
task cleanDependencies(type: Delete) {
delete HIFI_ANDROID_PRECOMPILED
delete 'app/src/main/jniLibs/arm64-v8a'
delete 'app/src/main/assets/bundled'
delete 'app/src/main/res/values/libs.xml'
}
task clean(type: Delete) {
delete rootProject.buildDir
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// See the comment on the qtBundle task above
/*
// FIXME derive the path from the gradle environment
def toolchain = [
version: '4.9',
prefix: 'aarch64-linux-android',
// FIXME derive from the host OS
ndkHost: 'windows-x86_64',
]
def findDependentLibrary = { String name ->
def libFolders = [
new File(qmlRoot, 'lib'),
new File("${HIFI_ANDROID_PRECOMPILED}/tbb/lib/release"),
new File("${HIFI_ANDROID_PRECOMPILED}/polyvox/lib/Release"),
new File("${HIFI_ANDROID_PRECOMPILED}/polyvox/lib/"),
new File("${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/libraries"),
]
}
def readElfBinary = new File(android.ndkDirectory, "/toolchains/${toolchain.prefix}-${toolchain.version}/prebuilt/${toolchain.ndkHost}/bin/${toolchain.prefix}-readelf${EXEC_SUFFIX}")
def getDependencies = { File elfBinary ->
Set<File> result = []
Queue<File> pending = new LinkedList<>()
pending.add(elfBinary)
Set<File> scanned = []
Pattern p = ~/.*\(NEEDED\).*Shared library: \[(.*\.so)\]/
while (!pending.isEmpty()) {
File current = pending.remove()
if (scanned.contains(current)) {
continue
}
scanned.add(current)
def command = "${readElfBinary} -d -W ${current.absolutePath}"
captureOutput(command).split('[\r\n]').each { line ->
Matcher m = p.matcher(line)
if (!m.matches()) {
return
}
def libName = m.group(1)
def file = new File(qmlRoot, "lib/${libName}")
if (file.exists()) {
result.add(file)
pending.add(file)
}
}
}
return result
}
task testElf (dependsOn: 'externalNativeBuildDebug') {
doLast {
def appLibraries = new HashSet<File>()
def qtDependencies = new HashSet<File>()
externalNativeBuildDebug.nativeBuildConfigurationsJsons.each { File file ->
def json = new JsonSlurper().parse(file)
json.libraries.each { node ->
def outputFile = new File(node.value.output)
if (outputFile.canonicalPath.startsWith(projectDir.canonicalPath)) {
appLibraries.add(outputFile)
}
}
}
appLibraries.each { File file ->
println getDependencies(file)
}
}
}
*/

87
android/build_recipes.md Normal file
View file

@ -0,0 +1,87 @@
Different libraries require different mechanism for building. Some are easiest with the standalone toolchain. Some are easiest with the ndk-build tool. Some can rely on CMake to do the right thing.
## Setup
### Android build environment
You need the Android NDK and SDK. The easiest way to get these is to download Android Studio for your platform and use the SDK manager in Studio to install the items. In particular you will need Android API levels 24 and 26, as well as the NDK, CMake, and LLDB. You should be using NDK version 16 or higher.
The Studio installation can install the SDK wherver you like. This guide assumes you install it to `$HOME/Android/SDK`. It will install the NDK inside the SDK in a folder named `ndk-bundle` but for convenience we will assume a symlink from `$HOME/Android/NDK` to the actual NDK folder exists
Additionally, some of the tools require a standalone toolchain in order to build. From the NDK build/tools directory you can execute the following command
`./make-standalone-toolchain.sh --arch=arm64 --platform=android-24 --install-dir=$HOME/Android/arm64_toolchain`
This will create the toolchain and install it in `$HOME/Android/arm64_toolchain`
When doing a build that relies on the toolchain you can execute the following commands to enable it
```
target_host=aarch64-linux-android
export PATH=$PATH:$HOME/Android/arm64_toolchain/bin
export AR=$target_host-ar
export AS=$target_host-as
export CC=$target_host-gcc
export CXX=$target_host-g++
export LD=$target_host-ld
export STRIP=$target_host-strip
export CFLAGS="-fPIE -fPIC"
export LDFLAGS="-pie"
```
## Qt
### Windows host
* Install the Android SDK
* Install the Android NDK
* Install Git for Windows
* Install Strawberry Perl
* Install Java 8 (Do NOT use Java 9, it will fail)
* Install Python 3.6 for Windows
* Open a Git Bash command prompt
* Ensure the following commands are visible in the path with `which <command>`
* gcc
* javac
* python
* gmake
* If any of them fail, fix your path and restart the bash prompt
* Fetch the pre-built OpenSSL binaries for Android/iOS from here: https://github.com/leenjewel/openssl_for_ios_and_android/releases
* Grab the latest release of the 1.0.2 series
* Open the archive and extract the `/android/openssl-arm64-v8a` folder
* Download the Qt sources
* `git clone git://code.qt.io/qt/qt5.git`
* `cd qt5`
* `perl init-repository`
* `git checkout v5.9.3`
* `git submodule update --recursive`
* `cd ..`
* Create a build directory with the command `mkdir qt5build`
* Configure the Qt5 build with the command `../qt5/configure -xplatform android-clang -android-ndk-host windows-x86_64 -confirm-license -opensource --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-ndk C:/Android/NDK -android-toolchain-version 4.9 -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -platform win32-g++ -prefix C:/qt5build_debug -android-sdk C:/Android/SDK -c++std c++14 -openssl-linked -L<PATH_TO_SSL>/lib -I<PATH_TO_SSL>/include`
## TBB
Use the ndk-build tool
ndk-build tbb tbbmalloc target=android arch=ia32 tbb_os=windows ndk_version=16
## OpenSSL
Use a standalone toolchain
* Grab the latest 1.1.0x series source from https://github.com/openssl/openssl/releases
* Follow the NDK guidelines for building a standalone toolchain for aarch64
* Use the following script to configure and build OpenSSL
* Enable the standalone toolchain with the export commands described above
* Configure SSL with the command `./Configure android64-aarch64 no-asm no-ssl2 no-ssl3 no-comp no-hw no-engine --prefix=$HOME/Android/openssl_1.1.0g`
* Build and install SSL with the command `make depend && make && make install`

View file

@ -94,7 +94,6 @@ Agent::Agent(ReceivedMessage& message) :
packetReceiver.registerListenerForTypes(
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket");
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
@ -149,17 +148,6 @@ void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNo
}
}
void Agent::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
NodeType_t nodeType;
message->peekPrimitive(&nodeType);
// PacketType_JURISDICTION, first byte is the node type...
if (nodeType == NodeType::EntityServer) {
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
queueReceivedPacket(message, senderNode);
}
}
void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
_receivedAudioStream.parseData(*message);
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
@ -483,10 +471,7 @@ void Agent::executeScript() {
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
_scriptEngine->registerGlobalObject("Recording", recordingInterface.data());
// we need to make sure that init has been called for our EntityScriptingInterface
// so that it actually has a jurisdiction listener when we ask it for it next
entityScriptingInterface->init();
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
_entityViewer.init();

View file

@ -73,7 +73,6 @@ private slots:
void handleAudioPacket(QSharedPointer<ReceivedMessage> message);
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
void nodeActivated(SharedNodePointer activatedNode);

View file

@ -46,8 +46,14 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION,
"run single assignment client of given type", "type");
QString typeDescription = "run single assignment client of given type\n# | Type\n============================";
for (Assignment::Type type = Assignment::FirstType;
type != Assignment::AllTypes;
type = static_cast<Assignment::Type>(static_cast<int>(type) + 1)) {
typeDescription.append(QStringLiteral("\n%1 | %2").arg(QString::number(type), Assignment::typeToString(type)));
}
const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION, typeDescription, "type");
parser.addOption(clientTypeOption);
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");

View file

@ -214,7 +214,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
uint64_t getTimestamp() const override {
return _lastEncodeTime;
}
const AvatarSharedPointer& getAvatar() const { return _avatar; }
AvatarSharedPointer getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
@ -326,7 +326,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
int remainingAvatars = (int)sortedAvatars.size();
while (!sortedAvatars.empty()) {
const auto& avatarData = sortedAvatars.top().getAvatar();
const auto avatarData = sortedAvatars.top().getAvatar();
sortedAvatars.pop();
remainingAvatars--;

View file

@ -23,23 +23,6 @@ void OctreeHeadlessViewer::queryOctree() {
char serverType = getMyNodeType();
PacketType packetType = getMyQueryMessageType();
NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions();
bool wantExtraDebugging = false;
if (wantExtraDebugging) {
qCDebug(octree) << "OctreeHeadlessViewer::queryOctree() _jurisdictionListener=" << _jurisdictionListener;
qCDebug(octree) << "---------------";
qCDebug(octree) << "_jurisdictionListener=" << _jurisdictionListener;
qCDebug(octree) << "Jurisdictions...";
jurisdictions.withReadLock([&] {
for (NodeToJurisdictionMapIterator i = jurisdictions.begin(); i != jurisdictions.end(); ++i) {
qCDebug(octree) << i.key() << ": " << &i.value();
}
});
qCDebug(octree) << "---------------";
}
_octreeQuery.setCameraPosition(_viewFrustum.getPosition());
_octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
_octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
@ -51,159 +34,22 @@ void OctreeHeadlessViewer::queryOctree() {
_octreeQuery.setOctreeSizeScale(_voxelSizeScale);
_octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);
// Iterate all of the nodes, and get a count of how many voxel servers we have...
int totalServers = 0;
int inViewServers = 0;
int unknownJurisdictionServers = 0;
DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node){
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() && node->getType() == serverType) {
totalServers++;
// get the server bounds for this server
QUuid nodeUUID = node->getUUID();
// if we haven't heard from this voxel server, go ahead and send it a query, so we
// can get the jurisdiction...
VoxelPositionSize rootDetails;
bool foundRootDetails = false;
jurisdictions.withReadLock([&] {
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
unknownJurisdictionServers++;
return;
}
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
auto rootCode = map.getRootOctalCode();
if (!rootCode) {
return;
}
voxelDetailsForCode(rootCode.get(), rootDetails);
foundRootDetails = true;
});
if (foundRootDetails) {
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
if ((bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds))) {
inViewServers++;
}
}
}
});
if (wantExtraDebugging) {
qCDebug(octree, "Servers: total %d, in view %d, unknown jurisdiction %d",
totalServers, inViewServers, unknownJurisdictionServers);
}
int perServerPPS = 0;
const int SMALL_BUDGET = 10;
int perUnknownServer = SMALL_BUDGET;
int totalPPS = getMaxPacketsPerSecond();
// determine PPS based on number of servers
if (inViewServers >= 1) {
// set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS
// for each unknown jurisdiction server
perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
} else {
if (unknownJurisdictionServers > 0) {
perUnknownServer = (totalPPS / unknownJurisdictionServers);
}
}
if (wantExtraDebugging) {
qCDebug(octree, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer);
}
auto nodeList = DependencyManager::get<NodeList>();
nodeList->eachNode([&](const SharedNodePointer& node){
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() && node->getType() == serverType) {
// get the server bounds for this server
QUuid nodeUUID = node->getUUID();
auto node = nodeList->soloNodeOfType(serverType);
if (node && node->getActiveSocket()) {
_octreeQuery.setMaxQueryPacketsPerSecond(getMaxPacketsPerSecond());
bool inView = false;
bool unknownView = false;
auto queryPacket = NLPacket::create(packetType);
// if we haven't heard from this voxel server, go ahead and send it a query, so we
// can get the jurisdiction...
VoxelPositionSize rootDetails;
bool foundRootDetails = false;
jurisdictions.withReadLock([&] {
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
unknownView = true; // assume it's in view
if (wantExtraDebugging) {
qCDebug(octree) << "no known jurisdiction for node " << *node << ", assume it's visible.";
}
return;
}
// encode the query data
auto packetData = reinterpret_cast<unsigned char*>(queryPacket->getPayload());
int packetSize = _octreeQuery.getBroadcastData(packetData);
queryPacket->setPayloadSize(packetSize);
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
auto rootCode = map.getRootOctalCode();
if (!rootCode) {
if (wantExtraDebugging) {
qCDebug(octree) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
}
return;
}
voxelDetailsForCode(rootCode.get(), rootDetails);
foundRootDetails = true;
});
if (foundRootDetails) {
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
inView = (bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds));
}
if (inView) {
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
if (wantExtraDebugging) {
qCDebug(octree) << "inView for node " << *node << ", give it budget of " << perServerPPS;
}
} else if (unknownView) {
if (wantExtraDebugging) {
qCDebug(octree) << "no known jurisdiction for node " << *node << ", give it budget of "
<< perUnknownServer << " to send us jurisdiction.";
}
// set the query's position/orientation to be degenerate in a manner that will get the scene quickly
// If there's only one server, then don't do this, and just let the normal voxel query pass through
// as expected... this way, we will actually get a valid scene if there is one to be seen
if (totalServers > 1) {
_octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1));
const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0);
_octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE);
_octreeQuery.setCameraNearClip(0.1f);
_octreeQuery.setCameraFarClip(0.1f);
if (wantExtraDebugging) {
qCDebug(octree) << "Using 'minimal' camera position for node" << *node;
}
} else {
if (wantExtraDebugging) {
qCDebug(octree) << "Using regular camera position for node" << *node;
}
}
_octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
} else {
_octreeQuery.setMaxQueryPacketsPerSecond(0);
}
// setup the query packet
auto queryPacket = NLPacket::create(packetType);
// read the data to our packet and set the payload size to fit the query
int querySize = _octreeQuery.getBroadcastData(reinterpret_cast<unsigned char*>(queryPacket->getPayload()));
queryPacket->setPayloadSize(querySize);
// ask the NodeList to send it
nodeList->sendPacket(std::move(queryPacket), *node);
}
});
// make sure we still have an active socket
nodeList->sendUnreliablePacket(*queryPacket, *node);
}
}

View file

@ -13,7 +13,6 @@
#define hifi_OctreeHeadlessViewer_h
#include <OctreeProcessor.h>
#include <JurisdictionListener.h>
#include <OctreeQuery.h>
@ -23,8 +22,6 @@ class OctreeHeadlessViewer : public OctreeProcessor {
public:
OctreeHeadlessViewer();
virtual ~OctreeHeadlessViewer() {};
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
@ -57,7 +54,6 @@ public slots:
unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
private:
JurisdictionListener* _jurisdictionListener = nullptr;
OctreeQuery _octreeQuery;
ViewFrustum _viewFrustum;

View file

@ -391,8 +391,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
nodeData->sceneStart(usecTimestampNow() - CHANGE_FUDGE);
// start tracking our stats
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
_myServer->getOctree()->getRoot(), _myServer->getJurisdiction());
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot());
preStartNewScene(nodeData, isFullScene);
}
@ -507,7 +506,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
float octreeSizeScale = nodeData->getOctreeSizeScale();
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
isFullScene, _myServer->getJurisdiction(), nodeData);
isFullScene, nodeData);
// Our trackSend() function is implemented by the server subclass, and will be called back as new entities/data elements are sent
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, _nodeUuid);

View file

@ -237,8 +237,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) :
_debugSending(false),
_debugReceiving(false),
_verboseDebug(false),
_jurisdiction(NULL),
_jurisdictionSender(NULL),
_octreeInboundPacketProcessor(NULL),
_persistThread(NULL),
_started(time(0)),
@ -257,12 +255,6 @@ OctreeServer::~OctreeServer() {
delete[] _parsedArgV;
}
if (_jurisdictionSender) {
_jurisdictionSender->terminating();
_jurisdictionSender->terminate();
_jurisdictionSender->deleteLater();
}
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->terminating();
_octreeInboundPacketProcessor->terminate();
@ -275,9 +267,6 @@ OctreeServer::~OctreeServer() {
_persistThread->deleteLater();
}
delete _jurisdiction;
_jurisdiction = NULL;
// cleanup our tree here...
qDebug() << qPrintable(_safeServerName) << "server START cleaning up octree... [" << this << "]";
_tree.reset();
@ -933,10 +922,6 @@ void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> me
}
}
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
_jurisdictionSender->queueReceivedPacket(message, senderNode);
}
void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message) {
if (!_isFinished && !_isShuttingDown) {
// these messages are only allowed to come from the domain server, so make sure that is the case
@ -1111,23 +1096,6 @@ void OctreeServer::readConfiguration() {
qDebug() << "statusPort= DISABLED";
}
QString jurisdictionFile;
if (readOptionString(QString("jurisdictionFile"), settingsSectionObject, jurisdictionFile)) {
qDebug("jurisdictionFile=%s", qPrintable(jurisdictionFile));
qDebug("about to readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile));
_jurisdiction = new JurisdictionMap(qPrintable(jurisdictionFile));
qDebug("after readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile));
} else {
QString jurisdictionRoot;
bool hasRoot = readOptionString(QString("jurisdictionRoot"), settingsSectionObject, jurisdictionRoot);
QString jurisdictionEndNodes;
bool hasEndNodes = readOptionString(QString("jurisdictionEndNodes"), settingsSectionObject, jurisdictionEndNodes);
if (hasRoot || hasEndNodes) {
_jurisdiction = new JurisdictionMap(qPrintable(jurisdictionRoot), qPrintable(jurisdictionEndNodes));
}
}
readOptionBool(QString("verboseDebug"), settingsSectionObject, _verboseDebug);
qDebug("verboseDebug=%s", debug::valueOf(_verboseDebug));
@ -1241,7 +1209,6 @@ void OctreeServer::domainSettingsRequestComplete() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement");
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURL");
@ -1365,13 +1332,6 @@ void OctreeServer::domainSettingsRequestComplete() {
_persistThread->initialize(true);
}
// set up our jurisdiction broadcaster...
if (_jurisdiction) {
_jurisdiction->setNodeType(getMyNodeType());
}
_jurisdictionSender = new JurisdictionSender(_jurisdiction, getMyNodeType());
_jurisdictionSender->initialize(true);
// set up our OctreeServerPacketProcessor
_octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this);
_octreeInboundPacketProcessor->initialize(true);
@ -1441,10 +1401,6 @@ void OctreeServer::aboutToFinish() {
_octreeInboundPacketProcessor->terminating();
}
if (_jurisdictionSender) {
_jurisdictionSender->terminating();
}
// Shut down all the send threads
for (auto& it : _sendThreads) {
auto& sendThread = *it.second;

View file

@ -44,7 +44,6 @@ public:
bool wantsVerboseDebug() const { return _verboseDebug; }
OctreePointer getOctree() { return _tree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
int getPacketsPerClientPerInterval() const { return std::min(_packetsPerClientPerInterval,
std::max(1, getPacketsTotalPerInterval() / std::max(1, getCurrentClientCount()))); }
@ -138,7 +137,6 @@ private slots:
void domainSettingsRequestComplete();
void handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message);
void handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message);
void removeSendThread();
@ -190,8 +188,6 @@ protected:
bool _debugReceiving;
bool _debugTimestampNow;
bool _verboseDebug;
JurisdictionMap* _jurisdiction;
JurisdictionSender* _jurisdictionSender;
OctreeInboundPacketProcessor* _octreeInboundPacketProcessor;
OctreePersistThread* _persistThread;

View file

@ -14,7 +14,6 @@
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
const int MAX_FILENAME_LENGTH = 1024;

View file

@ -79,7 +79,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket");
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
@ -283,11 +282,8 @@ void EntityScriptServer::run() {
// Setup Script Engine
resetEntitiesScriptEngine();
// we need to make sure that init has been called for our EntityScriptingInterface
// so that it actually has a jurisdiction listener when we ask it for it next
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->init();
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
_entityViewer.init();
@ -566,17 +562,6 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer<ReceivedMessage> mess
}
}
void EntityScriptServer::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
NodeType_t nodeType;
message->peekPrimitive(&nodeType);
// PacketType_JURISDICTION, first byte is the node type...
if (nodeType == NodeType::EntityServer) {
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
queueReceivedPacket(message, senderNode);
}
}
void EntityScriptServer::aboutToFinish() {
shutdownScriptEngine();

View file

@ -41,7 +41,6 @@ public slots:
private slots:
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
void handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);

View file

@ -6,7 +6,11 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_GLM)
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
if (ANDROID)
set(GLM_INCLUDE_DIRS "${HIFI_ANDROID_PRECOMPILED}/glm/include")
else()
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
endif()
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
endmacro()

View file

@ -945,7 +945,7 @@ void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const Q
void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes) {
// enumerate over all assignment types and see if we've already excluded it
for (Assignment::Type defaultedType = Assignment::AudioMixerType;
for (Assignment::Type defaultedType = Assignment::FirstType;
defaultedType != Assignment::AllTypes;
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) {

View file

@ -13,7 +13,7 @@ endfunction()
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "LeapMotion")
@ -56,14 +56,6 @@ else ()
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
endif ()
if (ANDROID)
set(PLATFORM_QT_COMPONENTS AndroidExtras)
set(PLATFORM_QT_LIBRARIES Qt5::AndroidExtras)
else ()
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
set(PLATFORM_QT_LIBRARIES Qt5::WebEngine Qt5::WebEngineWidgets)
endif ()
find_package(
Qt5 COMPONENTS
Gui Multimedia Network OpenGL Qml Quick Script Svg
@ -209,14 +201,14 @@ endif()
# link required hifi libraries
link_hifi_libraries(
shared octree ktx gpu gl gpu-gl procedural model render
shared octree ktx gpu gl procedural model render
pointers
recording fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
${NON_ANDROID_LIBRARIES}
${PLATFORM_GL_BACKEND}
)
# include the binary directory of render-utils for shader includes

View file

@ -56,29 +56,28 @@
{
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
},
{
"from": "Vive.RightFoot", "to" : "Standard.RightFoot",
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
},
{
"from": "Vive.Hips", "to" : "Standard.Hips",
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
},
{
"from": "Vive.Spine2", "to" : "Standard.Spine2",
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
},
{ "from": "Vive.Head", "to" : "Standard.Head"},
{ "from": "Vive.RightArm", "to" : "Standard.RightArm" },
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" },
{ "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" },
{ "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" },
{ "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" },

View file

@ -110,7 +110,7 @@ Item {
}
function pullFreshValues() {
if (Audio.getRecording()) {
if (AudioScriptingInterface.getRecording()) {
updateRecordingLabel();
}
@ -129,14 +129,14 @@ Item {
_wavFilePath = _wavFilePath.replace(/[\-:]|\.\d*Z$/g, "").replace("T", "-") + ".wav";
// Using controller recording default directory
_wavFilePath = Recording.getDefaultRecordingSaveDirectory() + _wavFilePath;
if (!Audio.startRecording(_wavFilePath)) {
if (!AudioScriptingInterface.startRecording(_wavFilePath)) {
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Error creating: "+_wavFilePath}));
updateRecordingUI(false);
}
}
function stopRecording() {
Audio.stopRecording();
AudioScriptingInterface.stopRecording();
setRecordingLabelOpacity(0.0);
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Saved: "+_wavFilePath}));
}
@ -158,7 +158,7 @@ Item {
}
function toggleRecording() {
if (Audio.getRecording()) {
if (AudioScriptingInterface.getRecording()) {
updateRecordingUI(false);
stopRecording();
} else {

View file

@ -0,0 +1,18 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
Rectangle {
width: 100
height: 100
color: "white"
Rectangle {
width: 10
height: 10
color: "red"
}
Label {
text: OverlayWindowTestString
anchors.centerIn: parent
}
}

View file

@ -22,7 +22,6 @@ Windows.Window {
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
destroyOnCloseButton: false
property var source;
property var component;
property var dynamicContent;
// Keyboard control properties in case needed by QML content.
@ -35,28 +34,9 @@ Windows.Window {
dynamicContent.destroy();
dynamicContent = null;
}
component = Qt.createComponent(source);
console.log("Created component " + component + " from source " + source);
}
onComponentChanged: {
console.log("Component changed to " + component)
populate();
}
function populate() {
console.log("Populate called: dynamicContent " + dynamicContent + " component " + component);
if (!dynamicContent && component) {
if (component.status == Component.Error) {
console.log("Error loading component:", component.errorString());
} else if (component.status == Component.Ready) {
console.log("Building dynamic content");
dynamicContent = component.createObject(contentHolder);
} else {
console.log("Component not yet ready, connecting to status change");
component.statusChanged.connect(populate);
}
}
QmlSurface.load(source, contentHolder, function(newObject) {
dynamicContent = newObject;
});
}
// Handle message traffic from the script that launched us to the loaded QML

View file

@ -29,12 +29,12 @@ Original.Button {
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
}
style: ButtonStyle {

View file

@ -31,12 +31,12 @@ Original.CheckBox {
activeFocusOnPress: true
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
}
// TODO: doesnt works for QQC1. check with QQC2
// onHovered: {
// tabletInterface.playSound(TabletEnums.ButtonHover);
// Tablet.playSound(TabletEnums.ButtonHover);
// }
style: CheckBoxStyle {

View file

@ -36,12 +36,12 @@ CheckBox {
hoverEnabled: true
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
}
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
}
}

View file

@ -27,12 +27,12 @@ Original.Button {
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
}
style: ButtonStyle {

View file

@ -41,13 +41,13 @@ Item {
onContainsMouseChanged: {
if (containsMouse) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
mouse.accepted = true;
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
webEntity.synthesizeKeyPress(glyph);
webEntity.synthesizeKeyPress(glyph, mirrorText);

View file

@ -30,12 +30,12 @@ Original.RadioButton {
readonly property int checkRadius: 2
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
}
// TODO: doesnt works for QQC1. check with QQC2
// onHovered: {
// tabletInterface.playSound(TabletEnums.ButtonHover);
// Tablet.playSound(TabletEnums.ButtonHover);
// }
style: RadioButtonStyle {

View file

@ -49,7 +49,7 @@ Item {
}
if (WebEngineView.LoadFailedStatus === loadRequest.status) {
console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
}
if (WebEngineView.LoadSucceededStatus === loadRequest.status) {

View file

@ -25,13 +25,13 @@ Preference {
id: button
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
preference.trigger();
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
}
width: 180
anchors.bottom: parent.bottom

View file

@ -41,12 +41,12 @@ Preference {
id: checkBox
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
}
anchors {

View file

@ -246,12 +246,12 @@ Item {
anchors.fill: parent;
acceptedButtons: Qt.LeftButton;
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
goFunction("hifi://" + hifiUrl);
}
hoverEnabled: true;
onEntered: {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
hoverThunk();
}
onExited: unhoverThunk();
@ -269,7 +269,7 @@ Item {
}
}
function go() {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablet.playSound(TabletEnums.ButtonClick);
goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId));
}
MouseArea {

View file

@ -45,11 +45,13 @@ OriginalDesktop.Desktop {
Toolbar {
id: sysToolbar;
objectName: "com.highfidelity.interface.toolbar.system";
property var tablet: Tablet.getTablet("com.highfidelity.interface.tablet.system");
anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined;
// Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained.
x: sysToolbar.x
y: 50
shown: true
buttonModel: tablet.buttons;
shown: tablet.toolbarMode;
}
Settings {

View file

@ -597,18 +597,11 @@ Item {
// Function body by Howard Stearns 2017-01-08
function goToUserInDomain(avatarUuid) {
var avatar = AvatarList.getAvatar(avatarUuid);
if (!avatar) {
if (!avatar || !avatar.position || !avatar.orientation) {
console.log("This avatar is no longer present. goToUserInDomain() failed.");
return;
}
// FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up,
// the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now.
// FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script.
// Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target.
// Position avatar 2 metres from the target in the direction that target avatar was facing.
MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2}));
// Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch.
MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1}));
// This is the last step of what AddressManager.goToUser does, but we don't need to resolve the username.
MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true);
}
}

View file

@ -26,7 +26,7 @@ Rectangle {
HifiConstants { id: hifi; }
property var eventBridge;
property string title: "Audio Settings - " + Audio.context;
property string title: "Audio Settings - " + AudioScriptingInterface.context;
signal sendToScript(var message);
color: hifi.colors.baseGray;
@ -37,7 +37,7 @@ Rectangle {
}
property bool isVR: Audio.context === "VR"
property bool isVR: AudioScriptingInterface.context === "VR"
property real rightMostInputLevelPos: 0
//placeholder for control sizes and paddings
//recalculates dynamically in case of UI size is changed
@ -72,17 +72,17 @@ Rectangle {
property bool showPeaks: true;
function enablePeakValues() {
Audio.devices.input.peakValuesEnabled = true;
Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
AudioScriptingInterface.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
if (!enabled && root.showPeaks) {
Audio.devices.input.peakValuesEnabled = true;
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
}
});
}
function disablePeakValues() {
root.showPeaks = false;
Audio.devices.input.peakValuesEnabled = false;
AudioScriptingInterface.devices.input.peakValuesEnabled = false;
}
Component.onCompleted: enablePeakValues();
@ -117,10 +117,10 @@ Rectangle {
text: qsTr("Mute microphone");
spacing: margins.sizeCheckBox - boxSize
isRedCheck: true;
checked: Audio.muted;
checked: AudioScriptingInterface.muted;
onClicked: {
Audio.muted = checked;
checked = Qt.binding(function() { return Audio.muted; }); // restore binding
AudioScriptingInterface.muted = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
}
}
}
@ -130,10 +130,10 @@ Rectangle {
AudioControls.CheckBox {
spacing: muteMic.spacing
text: qsTr("Enable noise reduction");
checked: Audio.noiseReduction;
checked: AudioScriptingInterface.noiseReduction;
onClicked: {
Audio.noiseReduction = checked;
checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding
AudioScriptingInterface.noiseReduction = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding
}
}
AudioControls.CheckBox {
@ -184,7 +184,7 @@ Rectangle {
spacing: 4;
snapMode: ListView.SnapToItem;
clip: true;
model: Audio.devices.input;
model: AudioScriptingInterface.devices.input;
delegate: Item {
width: rightMostInputLevelPos
height: margins.sizeCheckBox > checkBoxInput.implicitHeight ?
@ -204,7 +204,7 @@ Rectangle {
text: devicename
onPressed: {
if (!checked) {
Audio.setInputDevice(info, bar.currentIndex === 1);
AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1);
}
}
}
@ -215,7 +215,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
visible: ((bar.currentIndex === 1 && isVR) ||
(bar.currentIndex === 0 && !isVR)) &&
Audio.devices.input.peakValuesAvailable;
AudioScriptingInterface.devices.input.peakValuesAvailable;
}
}
}
@ -256,7 +256,7 @@ Rectangle {
spacing: 4;
snapMode: ListView.SnapToItem;
clip: true;
model: Audio.devices.output;
model: AudioScriptingInterface.devices.output;
delegate: Item {
width: rightMostInputLevelPos
height: margins.sizeCheckBox > checkBoxOutput.implicitHeight ?
@ -273,7 +273,7 @@ Rectangle {
text: devicename
onPressed: {
if (!checked) {
Audio.setOutputDevice(info, bar.currentIndex === 1);
AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1);
}
}
}

View file

@ -40,7 +40,7 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
visible: Audio.muted;
visible: AudioScriptingInterface.muted;
color: colors.muted;
text: "MUTED";

View file

@ -17,7 +17,7 @@ import QtGraphicalEffects 1.0
import TabletScriptingInterface 1.0
Rectangle {
readonly property var level: Audio.inputLevel;
readonly property var level: AudioScriptingInterface.inputLevel;
property bool standalone: false;
property var dragTarget: null;
@ -60,13 +60,13 @@ Rectangle {
hoverEnabled: true;
scrollGestureEnabled: false;
onClicked: {
Audio.muted = !Audio.muted;
tabletInterface.playSound(TabletEnums.ButtonClick);
AudioScriptingInterface.muted = !AudioScriptingInterface.muted;
Tablet.playSound(TabletEnums.ButtonClick);
}
drag.target: dragTarget;
onContainsMouseChanged: {
if (containsMouse) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablet.playSound(TabletEnums.ButtonHover);
}
}
}
@ -82,7 +82,7 @@ Rectangle {
readonly property string red: colors.muted;
readonly property string fill: "#55000000";
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
readonly property string icon: Audio.muted ? muted : unmuted;
readonly property string icon: AudioScriptingInterface.muted ? muted : unmuted;
}
Item {
@ -103,7 +103,7 @@ Rectangle {
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
id: image;
source: Audio.muted ? mutedIcon : unmutedIcon;
source: AudioScriptingInterface.muted ? mutedIcon : unmutedIcon;
width: 30;
height: 30;
@ -126,9 +126,9 @@ Rectangle {
Item {
id: status;
readonly property string color: Audio.muted ? colors.muted : colors.unmuted;
readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted;
visible: Audio.muted;
visible: AudioScriptingInterface.muted;
anchors {
left: parent.left;
@ -147,7 +147,7 @@ Rectangle {
color: parent.color;
text: Audio.muted ? "MUTED" : "MUTE";
text: AudioScriptingInterface.muted ? "MUTED" : "MUTE";
font.pointSize: 12;
}

View file

@ -27,9 +27,9 @@ RowLayout {
}
function playSound() {
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
// FIXME: Audio.playSystemSound should not require position
// FIXME: AudioScriptingInterface.playSystemSound should not require position
if (sample === null && !isPlaying) {
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
sample = AudioScriptingInterface.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(reset);
}

View file

@ -33,6 +33,7 @@ Rectangle {
property string itemName;
property string itemId;
property string itemHref;
property string itemAuthor;
property double balanceAfterPurchase;
property bool alreadyOwned: false;
property int itemPrice: -1;
@ -41,10 +42,11 @@ Rectangle {
property bool debugCheckoutSuccess: false;
property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified();
property bool isWearable;
property string referrer;
// Style
color: hifi.colors.white;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onWalletStatusResult: {
if (walletStatus === 0) {
@ -59,6 +61,7 @@ Rectangle {
} else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
UserActivityLogger.commercePassphraseEntry("marketplace checkout");
}
} else if (walletStatus === 3) {
authSuccessStep();
@ -71,7 +74,7 @@ Rectangle {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else {
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
}
@ -79,12 +82,12 @@ Rectangle {
if (result.status !== 'success') {
failureErrorText.text = result.message;
root.activeView = "checkoutFailure";
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message);
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message);
} else {
root.itemHref = result.data.download_url;
root.isWearable = result.data.categories.indexOf("Wearables") > -1;
root.activeView = "checkoutSuccess";
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned);
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned);
}
}
@ -114,7 +117,7 @@ Rectangle {
}
onItemIdChanged: {
commerce.inventory();
Commerce.inventory();
itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg";
}
@ -123,14 +126,14 @@ Rectangle {
}
onItemPriceChanged: {
commerce.balance();
Commerce.balance();
}
Timer {
id: notSetUpTimer;
interval: 200;
onTriggered: {
sendToScript({method: 'checkout_walletNotSetUp', itemId: itemId});
sendToScript({method: 'checkout_walletNotSetUp', itemId: itemId, referrer: referrer});
}
}
@ -202,7 +205,7 @@ Rectangle {
Component.onCompleted: {
purchasesReceived = false;
balanceReceived = false;
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
}
@ -223,7 +226,7 @@ Rectangle {
Connections {
target: GlobalServices
onMyUsernameChanged: {
commerce.getLoginStatus();
Commerce.getLoginStatus();
}
}
@ -408,7 +411,8 @@ Rectangle {
Rectangle {
id: buyTextContainer;
visible: buyText.text !== "";
anchors.top: parent.top;
anchors.top: cancelPurchaseButton.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.right: parent.right;
height: buyText.height + 30;
@ -463,8 +467,8 @@ Rectangle {
enabled: (root.balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || !itemIsJson;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top;
anchors.topMargin: buyTextContainer.visible ? 12 : 16;
anchors.top: checkoutActionButtonsContainer.top;
anchors.topMargin: 16;
height: 40;
anchors.left: parent.left;
anchors.right: parent.right;
@ -473,9 +477,9 @@ Rectangle {
if (itemIsJson) {
buyButton.enabled = false;
if (!root.shouldBuyWithControlledFailure) {
commerce.buy(itemId, itemPrice);
Commerce.buy(itemId, itemPrice);
} else {
commerce.buy(itemId, itemPrice, true);
Commerce.buy(itemId, itemPrice, true);
}
} else {
if (urlHandler.canHandleUrl(itemHref)) {
@ -876,6 +880,8 @@ Rectangle {
itemName = message.params.itemName;
root.itemPrice = message.params.itemPrice;
itemHref = message.params.itemHref;
referrer = message.params.referrer;
itemAuthor = message.params.itemAuthor;
setBuyText();
break;
default:
@ -923,11 +929,11 @@ Rectangle {
buyText.text = "";
}
} else {
buyText.text = "This free item <b>will not</b> be added to your <b>Purchases</b>. Non-entities can't yet be purchased for HFC.";
buyTextContainer.color = "#FFD6AD";
buyTextContainer.border.color = "#FAC07D";
buyGlyph.text = hifi.glyphs.alert;
buyGlyph.size = 46;
buyText.text = '<i>This type of item cannot currently be certified, so it will not show up in "My Purchases". You can access it again for free from the Marketplace.</i>';
buyTextContainer.color = hifi.colors.white;
buyTextContainer.border.color = hifi.colors.white;
buyGlyph.text = "";
buyGlyph.size = 0;
}
}
@ -939,8 +945,8 @@ Rectangle {
}
root.balanceReceived = false;
root.purchasesReceived = false;
commerce.inventory();
commerce.balance();
Commerce.inventory();
Commerce.balance();
}
//

View file

@ -31,14 +31,14 @@ Item {
height: mainContainer.height + additionalDropdownHeight;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onWalletStatusResult: {
if (walletStatus === 0) {
sendToParent({method: "needsLogIn"});
} else if (walletStatus === 3) {
commerce.getSecurityImage();
Commerce.getSecurityImage();
} else if (walletStatus > 3) {
console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus);
}
@ -48,7 +48,7 @@ Item {
if (!isLoggedIn) {
sendToParent({method: "needsLogIn"});
} else {
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
}
@ -61,13 +61,13 @@ Item {
}
Component.onCompleted: {
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
Connections {
target: GlobalServices
onMyUsernameChanged: {
commerce.getLoginStatus();
Commerce.getLoginStatus();
}
}

View file

@ -36,8 +36,8 @@ Rectangle {
property bool isCertificateInvalid: false;
// Style
color: hifi.colors.faintGray;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onCertificateInfoResult: {
if (result.status !== 'success') {
@ -109,7 +109,7 @@ Rectangle {
onCertificateIdChanged: {
if (certificateId !== "") {
commerce.certificateInfo(certificateId);
Commerce.certificateInfo(certificateId);
}
}

View file

@ -38,8 +38,8 @@ Rectangle {
property bool isDebuggingFirstUseTutorial: false;
// Style
color: hifi.colors.white;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onWalletStatusResult: {
if (walletStatus === 0) {
@ -54,13 +54,14 @@ Rectangle {
} else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
UserActivityLogger.commercePassphraseEntry("marketplace purchases");
}
} else if (walletStatus === 3) {
if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!Settings.getValue("isFirstUseOfPurchases", true) && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
Commerce.inventory();
}
} else {
console.log("ERROR in Purchases.qml: Unknown wallet status: " + walletStatus);
@ -71,7 +72,7 @@ Rectangle {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else {
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
}
@ -197,7 +198,7 @@ Rectangle {
Component.onCompleted: {
securityImageResultReceived = false;
purchasesReceived = false;
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
}
@ -218,7 +219,7 @@ Rectangle {
Connections {
target: GlobalServices
onMyUsernameChanged: {
commerce.getLoginStatus();
Commerce.getLoginStatus();
}
}
@ -233,7 +234,7 @@ Rectangle {
onSendSignalToParent: {
if (msg.method === "authSuccess") {
root.activeView = "initialize";
commerce.getWalletStatus();
Commerce.getWalletStatus();
} else {
sendToScript(msg);
}
@ -254,7 +255,7 @@ Rectangle {
case 'tutorial_finished':
Settings.setValue("isFirstUseOfPurchases", false);
root.activeView = "purchasesMain";
commerce.inventory();
Commerce.inventory();
break;
}
}
@ -594,7 +595,7 @@ Rectangle {
if (root.activeView === "purchasesMain" && !root.pendingInventoryReply) {
console.log("Refreshing Purchases...");
root.pendingInventoryReply = true;
commerce.inventory();
Commerce.inventory();
}
}
}

View file

@ -27,8 +27,8 @@ Item {
property string keyFilePath;
property bool showDebugButtons: true;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onKeyFilePathIfExistsResult: {
root.keyFilePath = path;
@ -37,7 +37,7 @@ Item {
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
Commerce.getKeyFilePathIfExists();
}
}
@ -55,6 +55,37 @@ Item {
// Style
color: hifi.colors.blueHighlight;
}
HifiControlsUit.Button {
id: clearCachedPassphraseButton;
visible: root.showDebugButtons;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.left: helpTitleText.right;
anchors.leftMargin: 20;
height: 40;
width: 150;
text: "DBG: Clear Pass";
onClicked: {
Commerce.setPassphrase("");
sendSignalToWallet({method: 'passphraseReset'});
}
}
HifiControlsUit.Button {
id: resetButton;
visible: root.showDebugButtons;
color: hifi.buttons.red;
colorScheme: hifi.colorSchemes.dark;
anchors.top: clearCachedPassphraseButton.top;
anchors.left: clearCachedPassphraseButton.right;
height: 40;
width: 150;
text: "DBG: RST Wallet";
onClicked: {
Commerce.reset();
sendSignalToWallet({method: 'walletReset'});
}
}
ListModel {
id: helpModel;

View file

@ -30,8 +30,8 @@ Item {
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
}
//
@ -53,7 +53,7 @@ Item {
// Title Bar text
RalewaySemiBold {
text: "HIFI COMMERCE - LOGIN";
text: "Log in to continue";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors

View file

@ -25,10 +25,6 @@ Item {
id: root;
SecurityImageModel {
id: securityImageModel;
}
// Username Text
RalewayRegular {
id: usernameText;

View file

@ -36,8 +36,8 @@ Item {
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onSecurityImageResult: {
titleBarSecurityImage.source = "";
@ -50,8 +50,12 @@ Item {
submitPassphraseInputButton.enabled = true;
if (!isAuthenticated) {
errorText.text = "Authentication failed - please try again.";
passphraseField.error = true;
UserActivityLogger.commercePassphraseAuthenticationStatus("auth failure");
} else {
sendSignalToParent({method: 'authSuccess'});;
sendSignalToParent({method: 'authSuccess'});
passphraseField.error = false;
UserActivityLogger.commercePassphraseAuthenticationStatus("auth success");
}
}
}
@ -70,6 +74,7 @@ Item {
// TODO: Fix this unlikely bug
onVisibleChanged: {
if (visible) {
passphraseField.error = false;
passphraseField.focus = true;
sendSignalToParent({method: 'disableHmdPreview'});
} else {
@ -208,7 +213,7 @@ Item {
onAccepted: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
Commerce.setPassphrase(passphraseField.text);
}
}
@ -248,7 +253,7 @@ Item {
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
Commerce.getSecurityImage();
}
}
Item {
@ -316,7 +321,7 @@ Item {
text: "Submit"
onClicked: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
Commerce.setPassphrase(passphraseField.text);
}
}
@ -336,6 +341,7 @@ Item {
text: "Cancel"
onClicked: {
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
UserActivityLogger.commercePassphraseAuthenticationStatus("passphrase modal cancelled");
}
}
}

View file

@ -36,8 +36,8 @@ Item {
propagateComposedEvents: false;
}
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onSecurityImageResult: {
passphrasePageSecurityImage.source = "";
passphrasePageSecurityImage.source = "image://security/securityImage";
@ -54,6 +54,9 @@ Item {
// TODO: Fix this unlikely bug
onVisibleChanged: {
if (visible) {
passphraseField.error = false;
passphraseFieldAgain.error = false;
currentPassphraseField.error = false;
if (root.shouldImmediatelyFocus) {
focusFirstTextField();
}
@ -160,7 +163,7 @@ Item {
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
Commerce.getSecurityImage();
}
}
Item {
@ -283,7 +286,7 @@ Item {
passphraseFieldAgain.error = false;
currentPassphraseField.error = false;
setErrorText("");
commerce.changePassphrase(currentPassphraseField.text, passphraseField.text);
Commerce.changePassphrase(currentPassphraseField.text, passphraseField.text);
return true;
}
}

View file

@ -27,8 +27,8 @@ Item {
id: root;
property string keyFilePath;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onKeyFilePathIfExistsResult: {
root.keyFilePath = path;
@ -234,7 +234,7 @@ Item {
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
Commerce.getKeyFilePathIfExists();
}
}

View file

@ -26,12 +26,8 @@ Item {
id: root;
property bool justSubmitted: false;
SecurityImageModel {
id: securityImageModel;
}
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onSecurityImageResult: {
securityImageChangePageSecurityImage.source = "";
@ -70,7 +66,7 @@ Item {
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
Commerce.getSecurityImage();
}
}
Item {
@ -198,7 +194,7 @@ Item {
securityImageSubmitButton.text = "Submitting...";
securityImageSubmitButton.enabled = false;
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
Commerce.chooseSecurityImage(securityImagePath);
}
}
}
@ -213,4 +209,8 @@ Item {
securityImageSubmitButton.enabled = true;
securityImageSubmitButton.text = "Submit";
}
function initModel() {
securityImageSelection.initModel();
}
}

View file

@ -15,29 +15,28 @@ import QtQuick 2.5
ListModel {
id: root;
ListElement{
sourcePath: "images/01.jpg"
securityImageEnumValue: 1;
function initModel() {
var array = [];
while (array.length < 6) {
// We currently have 34 security images to choose from
var randomNumber = Math.floor(Math.random() * 34) + 1;
if (array.indexOf(randomNumber) > -1) {
continue;
}
array[array.length] = randomNumber;
}
var modelElement;
for (var i = 0; i < 6; i++) {
modelElement = { "sourcePath":"images/" + addLeadingZero(array[i]) + ".jpg", "securityImageEnumValue": (i + 1) }
root.insert(i, modelElement);
}
}
ListElement{
sourcePath: "images/02.jpg"
securityImageEnumValue: 2;
}
ListElement{
sourcePath: "images/03.jpg"
securityImageEnumValue: 3;
}
ListElement{
sourcePath: "images/04.jpg"
securityImageEnumValue: 4;
}
ListElement{
sourcePath: "images/05.jpg"
securityImageEnumValue: 5;
}
ListElement{
sourcePath: "images/06.jpg"
securityImageEnumValue: 6;
function addLeadingZero(n) {
return n < 10 ? '0' + n : '' + n;
}
function getImagePathFromImageID(imageID) {

View file

@ -95,6 +95,10 @@ Item {
function getSelectedImageIndex() {
return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue;
}
function initModel() {
gridModel.initModel();
}
//
// FUNCTION DEFINITIONS END
//

View file

@ -25,8 +25,8 @@ Item {
id: root;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
}
// "Unavailable"

View file

@ -31,13 +31,15 @@ Rectangle {
property bool keyboardRaised: false;
property bool isPassword: false;
anchors.fill: (typeof parent === undefined) ? undefined : parent;
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onWalletStatusResult: {
if (walletStatus === 0) {
@ -47,7 +49,7 @@ Rectangle {
} else if (walletStatus === 1) {
if (root.activeView !== "walletSetup") {
root.activeView = "walletSetup";
commerce.resetLocalWalletOnly();
Commerce.resetLocalWalletOnly();
var timestamp = new Date();
walletSetup.startingTimestamp = timestamp;
walletSetup.setupAttemptID = generateUUID();
@ -57,10 +59,13 @@ Rectangle {
} else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
UserActivityLogger.commercePassphraseEntry("wallet app");
}
} else if (walletStatus === 3) {
root.activeView = "walletHome";
commerce.getSecurityImage();
if (root.activeView !== "walletSetup") {
root.activeView = "walletHome";
Commerce.getSecurityImage();
}
} else {
console.log("ERROR in Wallet.qml: Unknown wallet status: " + walletStatus);
}
@ -70,7 +75,7 @@ Rectangle {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
}
@ -82,10 +87,6 @@ Rectangle {
}
}
SecurityImageModel {
id: securityImageModel;
}
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
@ -180,7 +181,7 @@ Rectangle {
if (msg.method === 'walletSetup_finished') {
if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
root.activeView = "initialize";
commerce.getWalletStatus();
Commerce.getWalletStatus();
} else if (msg.referrer === 'purchases') {
sendToScript({method: 'goToPurchases'});
} else {
@ -209,17 +210,19 @@ Rectangle {
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
root.isPassword = msg.isPasswordField;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else if (msg.method === 'walletSecurity_changePassphraseCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changePassphraseSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
if (passphraseChange.visible) {
if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
root.isPassword = msg.isPasswordField;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else if (msg.method === 'walletSecurity_changePassphraseCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changePassphraseSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
}
}
}
}
@ -260,7 +263,7 @@ Rectangle {
color: hifi.colors.baseGray;
Component.onCompleted: {
commerce.getWalletStatus();
Commerce.getWalletStatus();
}
}
@ -281,7 +284,7 @@ Rectangle {
Connections {
target: GlobalServices
onMyUsernameChanged: {
commerce.getLoginStatus();
Commerce.getLoginStatus();
}
}
@ -295,7 +298,7 @@ Rectangle {
Connections {
onSendSignalToParent: {
if (msg.method === "authSuccess") {
commerce.getWalletStatus();
Commerce.getWalletStatus();
} else {
sendToScript(msg);
}
@ -342,6 +345,7 @@ Rectangle {
passphraseChange.clearPassphraseFields();
passphraseChange.resetSubmitButton();
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
securityImageChange.initModel();
root.activeView = "securityImageChange";
}
}

View file

@ -28,8 +28,8 @@ Item {
property bool historyReceived: false;
property int pendingCount: 0;
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onBalanceResult : {
balanceText.text = result.data.balance;
@ -135,8 +135,8 @@ Item {
onVisibleChanged: {
if (visible) {
historyReceived = false;
commerce.balance();
commerce.history();
Commerce.balance();
Commerce.history();
} else {
refreshTimer.stop();
}
@ -165,8 +165,8 @@ Item {
interval: 4000;
onTriggered: {
console.log("Refreshing Wallet Home...");
commerce.balance();
commerce.history();
Commerce.balance();
Commerce.history();
}
}
@ -197,14 +197,36 @@ Item {
anchors.topMargin: 26;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 30;
width: paintedWidth;
height: 30;
// Text size
size: 22;
// Style
color: hifi.colors.baseGrayHighlight;
}
RalewaySemiBold {
id: myPurchasesLink;
text: '<font color="#0093C5"><a href="#myPurchases">My Purchases</a></font>';
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: paintedWidth;
height: 30;
y: 4;
// Text size
size: 18;
// Style
color: hifi.colors.baseGrayHighlight;
horizontalAlignment: Text.AlignRight;
onLinkActivated: {
sendSignalToWallet({method: 'goToPurchases_fromWalletHome'});
}
}
ListModel {
id: tempTransactionHistoryModel;
}

View file

@ -41,8 +41,8 @@ Item {
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
Connections {
target: Commerce;
onSecurityImageResult: {
if (!exists && root.lastPage === "step_2") {
@ -236,6 +236,7 @@ Item {
height: 50;
text: "Set Up Wallet";
onClicked: {
securityImageSelection.initModel();
root.activeView = "step_2";
}
}
@ -251,7 +252,7 @@ Item {
height: 50;
text: "Cancel";
onClicked: {
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
sendSignalToWallet({method: 'walletSetup_cancelClicked', referrer: root.referrer });
}
}
}
@ -365,7 +366,7 @@ Item {
onClicked: {
root.lastPage = "step_2";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
Commerce.chooseSecurityImage(securityImagePath);
root.activeView = "step_3";
passphraseSelection.clearPassphraseFields();
}
@ -448,7 +449,7 @@ Item {
onVisibleChanged: {
if (visible) {
commerce.getWalletAuthenticatedStatus();
Commerce.getWalletAuthenticatedStatus();
}
}
@ -534,7 +535,7 @@ Item {
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
root.lastPage = "step_3";
commerce.generateKeyPair();
Commerce.generateKeyPair();
root.activeView = "step_4";
}
}
@ -667,7 +668,7 @@ Item {
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
Commerce.getKeyFilePathIfExists();
}
}
}

Binary file not shown.

Before

(image error) Size: 36 KiB

After

(image error) Size: 15 KiB

Binary file not shown.

Before

(image error) Size: 20 KiB

After

(image error) Size: 36 KiB

Binary file not shown.

Before

(image error) Size: 14 KiB

After

(image error) Size: 42 KiB

Binary file not shown.

Before

(image error) Size: 28 KiB

After

(image error) Size: 59 KiB

Binary file not shown.

Before

(image error) Size: 42 KiB

After

(image error) Size: 35 KiB

Binary file not shown.

After

(image error) Size: 48 KiB

Binary file not shown.

After

(image error) Size: 40 KiB

Binary file not shown.

After

(image error) Size: 46 KiB

Binary file not shown.

After

(image error) Size: 36 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 48 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 53 KiB

Binary file not shown.

After

(image error) Size: 68 KiB

Binary file not shown.

After

(image error) Size: 23 KiB

Binary file not shown.

After

(image error) Size: 38 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 36 KiB

Binary file not shown.

After

(image error) Size: 79 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 51 KiB

Binary file not shown.

After

(image error) Size: 52 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 39 KiB

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