Santa Cruz

This commit is contained in:
Brad Davis 2018-11-20 10:51:55 -08:00
parent 1d93b6ada0
commit b1e4e17f47
226 changed files with 5403 additions and 6931 deletions

1
.gitignore vendored
View file

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

View file

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

View file

@ -1,42 +0,0 @@
//
// InterfaceActivity.java
// gvr-interface/java
//
// Created by Stephen Birarda on 1/26/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
package io.highfidelity.gvrinterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtActivity;
public class InterfaceActivity extends QtActivity {
public static native void handleHifiURL(String hifiURLString);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Get the intent that started this activity in case we have a hifi:// URL to parse
Intent intent = getIntent();
if (intent.getAction() == Intent.ACTION_VIEW) {
Uri data = intent.getData();
if (data.getScheme().equals("hifi")) {
handleHifiURL(data.toString());
}
}
}
}

View file

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

View file

@ -0,0 +1,73 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.Html;
import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import io.highfidelity.hifiinterface.R;
public class SignedInFragment extends Fragment {
private Button mGetStartedButton;
private OnSignedInInteractionListener mListener;
public SignedInFragment() {
// Required empty public constructor
}
public static SignedInFragment newInstance() {
SignedInFragment fragment = new SignedInFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_signedin, container, false);
mGetStartedButton = rootView.findViewById(R.id.getStarted);
mGetStartedButton.setOnClickListener(view -> {
getStarted();
});
return rootView;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SignedInFragment.OnSignedInInteractionListener) {
mListener = (SignedInFragment.OnSignedInInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnSignedInInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public void getStarted() {
if (mListener != null) {
mListener.onGettingStarted();
}
}
public interface OnSignedInInteractionListener {
void onGettingStarted();
}
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:padding="9dp">
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@android:color/black" />
<solid android:color="@color/backgroundEditText"/>
</shape>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLight">
<ImageView
android:id="@+id/header"
android:layout_width="@dimen/header_hifi_width"
android:layout_height="@dimen/header_hifi_height"
android:layout_marginTop="@dimen/header_hifi_margin_top"
android:contentDescription="HighFidelity"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/hifi_header" />
<TextView
android:id="@+id/welcome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:paddingLeft="86dp"
android:paddingRight="86dp"
android:fontFamily="@font/raleway"
android:textColor="@color/clearText"
android:textSize="24sp"
android:text="@string/signedin_welcome"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:gravity="center"
/>
<Button
android:id="@+id/getStarted"
android:layout_width="217dp"
android:layout_height="38dp"
android:layout_marginTop="30dp"
android:background="@drawable/rounded_button"
android:fontFamily="@font/raleway_semibold"
android:paddingBottom="0dp"
android:paddingLeft="25dp"
android:paddingRight="25dp"
android:paddingTop="0dp"
android:text="@string/get_started"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="18sp"
app:layout_constraintTop_toBottomOf="@id/welcome"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_goneMarginTop="4dp"/>
</android.support.constraint.ConstraintLayout>

View file

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

View file

@ -0,0 +1,51 @@
apply plugin: 'com.android.application'
android {
signingConfigs {
release {
keyAlias 'key0'
keyPassword 'password'
storeFile file('C:/android/keystore.jks')
storePassword 'password'
}
}
compileSdkVersion 28
defaultConfig {
applicationId "io.highfidelity.frameplayer"
minSdkVersion 25
targetSdkVersion 28
ndk { abiFilters 'arm64-v8a' }
externalNativeBuild {
cmake {
arguments '-DHIFI_ANDROID=1',
'-DHIFI_ANDROID_APP=framePlayer',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_STL=c++_shared',
'-DCMAKE_VERBOSE_MAKEFILE=ON'
targets = ['framePlayer']
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
implementation project(':oculus')
implementation project(':qt')
}

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.highfidelity.frameplayer"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto">
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
<uses-feature android:name="android.software.vr.mode" android:required="true"/>
<uses-feature android:name="android.hardware.vr.high_performance" android:required="true"/>
<application android:label="Frame Viewer"
android:allowBackup="false"
android:name="org.qtproject.qt5.android.bindings.QtApplication"
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity
android:name=".QuestQtActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:excludeFromRecents="false"
android:alwaysRetainTaskState="true"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
>
<!-- JNI nonsense -->
<meta-data android:name="android.app.lib_name" android:value="framePlayer"/>
<!-- Qt nonsense -->
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
</activity>
<activity
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
android:name=".QuestRenderActivity"
android:label="Frame Player"
android:launchMode="singleInstance"
android:screenOrientation="landscape"
android:excludeFromRecents="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,25 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PlayerWindow.h"
#include <QtWidgets/QFileDialog>
PlayerWindow::PlayerWindow() {
installEventFilter(this);
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
setSurfaceType(QSurface::OpenGLSurface);
create();
showFullScreen();
// Ensure the window is visible and the GL context is valid
QCoreApplication::processEvents();
_renderThread.initialize(this);
}
PlayerWindow::~PlayerWindow() {
}

View file

@ -0,0 +1,29 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <QtGui/QWindow>
#include <QtCore/QSettings>
#include <gpu/Forward.h>
#include "RenderThread.h"
// Create a simple OpenGL window that renders text in various ways
class PlayerWindow : public QWindow {
public:
PlayerWindow();
virtual ~PlayerWindow();
protected:
//bool eventFilter(QObject* obj, QEvent* event) override;
//void keyPressEvent(QKeyEvent* event) override;
private:
QSettings _settings;
RenderThread _renderThread;
};

View file

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

View file

@ -0,0 +1,44 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <QtCore/QElapsedTimer>
#include <GenericThread.h>
#include <shared/RateCounter.h>
#include <gl/Config.h>
#include <gl/Context.h>
#include <gpu/gl/GLBackend.h>
#include <ovr/VrHandler.h>
class RenderThread : public GenericThread, ovr::VrHandler {
using Parent = GenericThread;
public:
QWindow* _window{ nullptr };
std::mutex _mutex;
gpu::ContextPointer _gpuContext; // initialized during window creation
std::shared_ptr<gpu::Backend> _backend;
std::atomic<size_t> _presentCount{ 0 };
std::mutex _frameLock;
std::queue<gpu::FramePointer> _pendingFrames;
gpu::FramePointer _activeFrame;
uint32_t _externalTexture{ 0 };
glm::mat4 _correction;
void move(const glm::vec3& v);
void setup() override;
bool process() override;
void shutdown() override;
void handleInput();
void submitFrame(const gpu::FramePointer& frame);
void initialize(QWindow* window);
void renderFrame();
};

View file

@ -0,0 +1,56 @@
//
// Created by Bradley Austin Davis on 2018/11/22
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <android/log.h>
#include <QtGui/QGuiApplication>
#include <QtCore/QTimer>
#include <QtCore/QFileInfo>
#include <Trace.h>
#include "PlayerWindow.h"
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
const char * local=message.toStdString().c_str();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO,"Interface",local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN,"Interface",local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR,"Interface",local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
abort();
}
}
}
int main(int argc, char** argv) {
setupHifiApplication("gpuFramePlayer");
QGuiApplication app(argc, argv);
auto oldMessageHandler = qInstallMessageHandler(messageHandler);
DependencyManager::set<tracing::Tracer>();
PlayerWindow window;
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec");
app.exec();
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec done");
qInstallMessageHandler(oldMessageHandler);
return 0;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,86 @@
#include <functional>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QThread>
#include <QtCore/QStringList>
#include <QtCore/QStandardPaths>
#include <QtCore/QTextStream>
#include <QtCore/QObject>
#include <QtAndroidExtras/QAndroidJniObject>
#include <QtAndroidExtras/QtAndroid>
#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <shared/Storage.h>
#include <AddressManager.h>
#include <AndroidHelper.h>
#include <udt/PacketHeaders.h>
#include <OVR_Platform.h>
#include <OVR_Functions_Voip.h>
void initOculusPlatform(JNIEnv* env, jobject obj) {
static std::once_flag once;
std::call_once(once, [&]{
// static const char* appID = "2343652845669354";
// if (ovr_PlatformInitializeAndroid(appID, obj, env) != ovrPlatformInitialize_Success) {
// __android_log_write(ANDROID_LOG_WARN, "QQQ", "Failed to init platform SDK");
// return;
// }
// ovr_Voip_SetSystemVoipSuppressed(true);
});
}
extern "C" {
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_MainActivity_nativeInitOculusPlatform(JNIEnv* env, jobject obj) {
initOculusPlatform(env, obj);
}
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_MainActivity_nativeOnCreate(JNIEnv* env, jobject obj) {
initOculusPlatform(env, obj);
qRegisterMetaType<QAndroidJniObject>("QAndroidJniObject");
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() {
qWarning() << "QQQ" << __FUNCTION__ << "scheduling onAppLoadedComplete";
AndroidHelper::instance().moveToThread(qApp->thread());
QtAndroid::androidActivity().callMethod<void>("onAppLoadedComplete", "()V");
QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, nullptr, nullptr);
});
}
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_MainActivity_nativeOnDestroy(JNIEnv* env, jobject obj) {
}
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_SplashActivity_registerLoadCompleteListener(JNIEnv *env,
jobject instance) {
}
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_MainActivity_nativeOnPause(JNIEnv *env, jobject obj) {
AndroidHelper::instance().notifyEnterBackground();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_MainActivity_nativeOnResume(JNIEnv *env, jobject obj) {
AndroidHelper::instance().notifyEnterForeground();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_questInterface_receiver_HeadsetStateReceiver_notifyHeadsetOn(JNIEnv *env,
jobject instance,
jboolean pluggedIn) {
AndroidHelper::instance().notifyHeadsetOn(pluggedIn);
}
}

View file

@ -0,0 +1,70 @@
//
// MainActivity.java
// android/app/src/main/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.questInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import org.qtproject.qt5.android.bindings.QtActivity;
import java.util.Timer;
import java.util.TimerTask;
import io.highfidelity.utils.HifiUtils;
public class MainActivity extends QtActivity {
private native void nativeOnCreate();
private native void nativeOnDestroy();
private native void nativeOnPause();
private native void nativeOnResume();
@Override
public void onCreate(Bundle savedInstanceState) {
super.isLoading = true;
super.keepInterfaceRunning = true;
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
HifiUtils.upackAssets(getAssets(), getCacheDir().getAbsolutePath());
nativeOnCreate();
}
public void onAppLoadedComplete() {
Log.w("QQQ", "Returning to quest activity");
runOnUiThread(()->{
startActivity(new Intent(MainActivity.this, QuestActivity.class));
//moveTaskToBack(true);
});
}
@Override
protected void onPause() {
super.onPause();
if (!super.isLoading) {
nativeOnPause();
}
}
@Override
protected void onResume() {
super.onResume();
nativeOnResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
nativeOnDestroy();
}
}

View file

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

View file

@ -0,0 +1,25 @@
//
// MainActivity.java
// android/app/src/main/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.questInterface;
import android.content.Intent;
import android.os.Bundle;
import io.highfidelity.oculus.OculusMobileActivity;
public class QuestActivity extends OculusMobileActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startActivity(new Intent(this, MainActivity.class));
}
}

View file

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

View file

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

View file

@ -0,0 +1,7 @@
set(TARGET_NAME uiApp)
setup_hifi_library(AndroidExtras)
link_hifi_libraries(shared ktx shaders gl oculusMobile qml)
target_link_libraries(${TARGET_NAME} android log m)
target_opengl()
target_oculus_mobile()

25
android/apps/ui/proguard-rules.pro vendored Normal file
View file

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

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.highfidelity.ui"
android:installLocation="auto">
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application android:label="UI Test" android:allowBackup="false" android:name="org.qtproject.qt5.android.bindings.QtApplication">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity
android:name=".UiActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:excludeFromRecents="false"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode">
<meta-data android:name="android.app.lib_name" android:value="uiApp"/>
<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/webview/libqtwebview_android.so:plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,22 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PlayerWindow.h"
#include <QtWidgets/QFileDialog>
PlayerWindow::PlayerWindow() {
installEventFilter(this);
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
setSurfaceType(QSurface::OpenGLSurface);
create();
showFullScreen();
QCoreApplication::processEvents();
_renderThread.initialize(this);
}

View file

@ -0,0 +1,27 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <QtGui/QWindow>
#include <QtCore/QSettings>
#include "RenderThread.h"
// Create a simple OpenGL window that renders text in various ways
class PlayerWindow : public QWindow {
public:
PlayerWindow();
virtual ~PlayerWindow() = default;
protected:
//bool eventFilter(QObject* obj, QEvent* event) override;
//void keyPressEvent(QKeyEvent* event) override;
private:
RenderThread _renderThread;
};

View file

@ -0,0 +1,98 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RenderThread.h"
#include <mutex>
#include <jni.h>
#include <android/log.h>
#include <QtGui/QWindow>
#include <gl/GLHelpers.h>
#include <GLMHelpers.h>
#define MARGIN 48
void RenderThread::initialize(QWindow* window) {
std::unique_lock<std::mutex> lock(_frameLock);
setObjectName("RenderThread");
Parent::initialize();
_thread->setObjectName("RenderThread");
_size = window->size();
_glContext.setWindow(window);
_glContext.create();
_glContext.makeCurrent();
gl::setSwapInterval(0);
glGenFramebuffers(1, &_readFbo);
OffscreenSurface::setSharedContext(_glContext.qglContext());
// GPU library init
_glContext.doneCurrent();
_glContext.moveToThread(_thread);
_offscreen = std::make_shared<OffscreenSurface>();
_offscreen->resize({ _size.width() - (MARGIN * 2), _size.height() - (MARGIN * 2)});
_offscreen->load(QUrl("qrc://qml/main.qml"));
}
void RenderThread::releaseTexture() {
if (_uiTexture != 0) {
auto readFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
CHECK_GL_ERROR();
_discardLambda(_uiTexture, readFence);
_uiTexture = 0;
}
}
void RenderThread::setup() {
// Wait until the context has been moved to this thread
{ std::unique_lock<std::mutex> lock(_frameLock); }
_glContext.makeCurrent();
}
void RenderThread::shutdown() {
releaseTexture();
_glContext.doneCurrent();
}
bool RenderThread::process() {
float now = secTimestampNow();
float red = fabsf(sinf(now * PI));
_glContext.makeCurrent();
{
OffscreenSurface::TextureAndFence newTextureAndFence;
if (_offscreen->fetchTexture(newTextureAndFence)) {
releaseTexture();
const auto& newTexture = newTextureAndFence.first;
GLsync writeFence = static_cast<GLsync>(newTextureAndFence.second);
_uiTexture = newTexture;
glWaitSync(writeFence, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(writeFence);
}
}
glClearColor(1, red, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
if (_uiTexture != 0) {
auto uiSize = _offscreen->size();
glBindFramebuffer(GL_READ_FRAMEBUFFER, _readFbo);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _uiTexture, 0);
glBlitFramebuffer(
0, 0, uiSize.width(), uiSize.height(),
MARGIN, MARGIN, uiSize.width() + MARGIN, uiSize.height() + MARGIN,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
_glContext.swapBuffers();
return true;
}

View file

@ -0,0 +1,42 @@
//
// Created by Bradley Austin Davis on 2018/10/21
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <QtCore/QElapsedTimer>
#include <GenericThread.h>
#include <shared/RateCounter.h>
#include <gl/Config.h>
#include <gl/Context.h>
#include <qml/OffscreenSurface.h>
class RenderThread : public GenericThread {
using Parent = GenericThread;
using OffscreenSurface = hifi::qml::OffscreenSurface;
using OffscreenPtr = std::shared_ptr<OffscreenSurface>;
using DiscardLambda = std::function<void(uint32_t, void*)>;
public:
gl::Context _glContext;
std::mutex _mutex;
std::atomic<size_t> _presentCount{ 0 };
std::mutex _frameLock;
OffscreenPtr _offscreen;
QSize _size;
DiscardLambda _discardLambda { OffscreenSurface::getDiscardLambda() };
GLuint _readFbo { 0 };
GLuint _uiTexture { 0 };
void setup() override;
bool process() override;
void shutdown() override;
void initialize(QWindow* window);
private:
void releaseTexture();
};

View file

@ -0,0 +1,58 @@
//
// Created by Bradley Austin Davis on 2018/11/22
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <android/log.h>
#include <QtGui/QGuiApplication>
#include <Trace.h>
#include "PlayerWindow.h"
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
const char * local=message.toStdString().c_str();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG,"QQ",local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO,"QQ",local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN,"QQ",local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR,"QQ",local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL,"QQ",local);
abort();
}
}
}
void initWebView();
int main(int argc, char** argv) {
setupHifiApplication("uiApp");
QGuiApplication app(argc, argv);
initWebView();
auto oldMessageHandler = qInstallMessageHandler(messageHandler);
{
DependencyManager::set<tracing::Tracer>();
PlayerWindow window;
app.exec();
}
qInstallMessageHandler(oldMessageHandler);
return 0;
}

View file

@ -0,0 +1,21 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtWebView 1.1
WebView {
url: "https://old.reddit.com"
width: 640; height: 480
Rectangle {
id: blue
color: "blue"
anchors { margins: 48; top: parent.top; bottom: parent.bottom; left: parent.left; }
width: parent.width / 3
ColorAnimation on color {
from: "yellow";
to: "red";
loops: Animation.Infinite;
duration: 1000;
}
}
}

View file

@ -0,0 +1,5 @@
#include <QtWebView/QtWebView>
void initWebView() {
QtWebView::initialize();
}

View file

@ -0,0 +1,6 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>

View file

@ -0,0 +1,21 @@
//
// Created by Bradley Austin Davis on 2018/11/20
// Copyright 2013-2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
package io.highfidelity.ui;
import android.os.Bundle;
import org.qtproject.qt5.android.bindings.QtActivity;
public class UiActivity extends QtActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
//System.loadLibrary("uiApp");
super.onCreate(savedInstanceState);
}
}

View file

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

View file

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

View file

@ -0,0 +1,22 @@
TEMPLATE = app
QT += gui qml quick xml webview widgets
CONFIG += c++11
SOURCES += \
cpp/main.cpp \
cpp/PlayerWindow.cpp \
cpp/RenderThread.cpp \
cpp/resources.qrc
HEADERS += \
cpp/PlayerWindow.h \
cpp/RenderThread.h
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH = cpp
#DISTFILES += \
# cpp/*.qml

View file

@ -1,13 +1,5 @@
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 {
google()
@ -18,12 +10,6 @@ buildscript {
}
}
plugins {
id 'de.undercouch.download' version '3.3.0'
id "cz.malohlava" version "1.0.3"
id "io.github.http-builder-ng.http-plugin" version "0.1.1"
}
allprojects {
repositories {
google()
@ -32,16 +18,14 @@ allprojects {
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
VERSION_CODE = project.hasProperty('VERSION_CODE') ? project.getProperty('VERSION_CODE') : '0'
RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
STABLE_BUILD = project.hasProperty('STABLE_BUILD') ? project.getProperty('STABLE_BUILD') : '0'
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
appVersionCode = Integer.valueOf(VERSION_CODE ?: 1)
appVersionName = RELEASE_NUMBER ?: "1.0"
}
def appDir = new File(projectDir, 'apps/interface')
@ -142,108 +126,6 @@ task setupDependencies() {
task cleanDependencies(type: Delete) {
}
def runBreakpadDumpSyms = { buildType ->
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
def objDir = new File("${appDir}/build/intermediates/cmake/${buildType}/obj/arm64-v8a")
def stripDebugSymbol = "${appDir}/build/intermediates/transforms/stripDebugSymbol/${buildType}/0/lib/arm64-v8a/"
def outputDir = new File(breakpadDumpSymsDir, buildType)
if (!outputDir.exists()) {
outputDir.mkdirs()
}
objDir.eachFileRecurse (FileType.FILES) { file ->
if (file.name.endsWith('.so')) {
def output = file.name + ".sym"
def cmdArgs = [
file.toString(),
stripDebugSymbol
]
def result = exec {
workingDir HIFI_ANDROID_PRECOMPILED + '/breakpad/bin'
commandLine './dump_syms'
args cmdArgs
ignoreExitValue true
standardOutput = new BufferedOutputStream(new FileOutputStream(new File(outputDir, output)))
}
}
}
}
task runBreakpadDumpSymsDebug() {
doLast {
runBreakpadDumpSyms("debug");
}
}
task runBreakpadDumpSymsRelease() {
doLast {
runBreakpadDumpSyms("release");
}
}
task zipDumpSymsDebug(type: Zip, dependsOn: runBreakpadDumpSymsDebug) {
from (new File(breakpadDumpSymsDir, "debug").absolutePath)
archiveName "symbols-${RELEASE_NUMBER}-debug.zip"
destinationDir(new File("${appDir}/build/tmp/"))
}
task zipDumpSymsRelease(type: Zip, dependsOn: runBreakpadDumpSymsRelease) {
from (new File(breakpadDumpSymsDir, "release").absolutePath)
archiveName "symbols-${RELEASE_NUMBER}-release.zip"
destinationDir(new File("${appDir}/build/tmp/"))
}
task uploadBreakpadDumpSymsDebug(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsDebug) {
onlyIf {
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
}
config {
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
}
post {
request.uri.path = '/post'
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-debug.zip").bytes
request.contentType = 'application/octet-stream'
response.success {
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-debug.zip uploaded")
}
}
}
task uploadBreakpadDumpSymsRelease(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsRelease) {
onlyIf {
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
}
config {
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
}
post {
request.uri.path = '/post'
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-release.zip").bytes
request.contentType = 'application/octet-stream'
response.success {
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-release.zip uploaded")
}
}
}
task renameHifiACTaskDebug() {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/debug/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
task renameHifiACTaskRelease(type: Copy) {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/release/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// See the comment on the qtBundle task above

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,103 @@
//
// Created by Bradley Austin Davis on 2018/11/20
// Copyright 2013-2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
package io.highfidelity.oculus;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
/**
* Contains a native surface and forwards the activity lifecycle and surface lifecycle
* events to the OculusMobileDisplayPlugin
*/
public class OculusMobileActivity extends Activity implements SurfaceHolder.Callback {
private static final String TAG = OculusMobileActivity.class.getSimpleName();
static { System.loadLibrary("oculusMobile"); }
private native void nativeOnCreate();
private native static void nativeOnResume();
private native static void nativeOnPause();
private native static void nativeOnDestroy();
private native static void nativeOnSurfaceChanged(Surface s);
private SurfaceView mView;
private SurfaceHolder mSurfaceHolder;
public static void launch(Activity activity) {
if (activity != null) {
activity.runOnUiThread(()->{
activity.startActivity(new Intent(activity, OculusMobileActivity.class));
});
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.w(TAG, "QQQ onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Create a native surface for VR rendering (Qt GL surfaces are not suitable
// because of the lack of fine control over the surface callbacks)
mView = new SurfaceView(this);
setContentView(mView);
mView.getHolder().addCallback(this);
// Forward the create message to the JNI code
nativeOnCreate();
}
@Override
protected void onDestroy() {
Log.w(TAG, "QQQ onDestroy");
if (mSurfaceHolder != null) {
nativeOnSurfaceChanged(null);
}
nativeOnDestroy();
super.onDestroy();
}
@Override
protected void onResume() {
Log.w(TAG, "QQQ onResume");
super.onResume();
nativeOnResume();
}
@Override
protected void onPause() {
Log.w(TAG, "QQQ onPause");
nativeOnPause();
super.onPause();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceCreated");
nativeOnSurfaceChanged(holder.getSurface());
mSurfaceHolder = holder;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.w(TAG, "QQQ surfaceChanged");
nativeOnSurfaceChanged(holder.getSurface());
mSurfaceHolder = holder;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceDestroyed");
nativeOnSurfaceChanged(null);
mSurfaceHolder = null;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
Source: hifi-client-deps
Version: 0
Description: Collected dependencies for High Fidelity applications
Build-Depends: hifi-deps, glslang, nlohmann-json, openvr (windows), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), vulkanmemoryallocator
Build-Depends: hifi-deps, glslang, nlohmann-json, openvr (windows), sdl2, shaderc, spirv-cross, spirv-tools, vulkanmemoryallocator

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View file

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

View file

@ -1,33 +0,0 @@
//
// Client.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Client_h
#define hifi_Client_h
#include <QtCore/QObject>
#include <HifiSockAddr.h>
class Client : public QObject {
Q_OBJECT
public:
Client(QObject* parent = 0);
virtual void cleanupBeforeQuit() = 0;
protected:
void setupNetworking();
virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
private slots:
void processDatagrams();
};
#endif // hifi_Client_h

View file

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

View file

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

View file

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

View file

@ -1,58 +0,0 @@
//
// GVRMainWindow.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_GVRMainWindow_h
#define hifi_GVRMainWindow_h
#include <QtWidgets/QMainWindow>
#if defined(ANDROID) && defined(HAVE_LIBOVR)
#include <KeyState.h>
#endif
class QKeyEvent;
class QMenuBar;
class QVBoxLayout;
class GVRMainWindow : public QMainWindow {
Q_OBJECT
public:
GVRMainWindow(QWidget* parent = 0);
~GVRMainWindow();
public slots:
void showAddressBar();
void showLoginDialog();
void showLoginFailure();
#if defined(ANDROID) && defined(HAVE_LIBOVR)
OVR::KeyState& getBackKeyState() { return _backKeyState; }
#endif
protected:
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);
private slots:
void refreshLoginAction();
private:
void setupMenuBar();
#if defined(ANDROID) && defined(HAVE_LIBOVR)
OVR::KeyState _backKeyState;
bool _wasBackKeyDown;
#endif
QVBoxLayout* _mainLayout;
QMenuBar* _menuBar;
QAction* _loginAction;
};
#endif // hifi_GVRMainWindow_h

View file

@ -1,18 +0,0 @@
//
// InterfaceView.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 1/28/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InterfaceView.h"
InterfaceView::InterfaceView(QWidget* parent, Qt::WindowFlags flags) :
QOpenGLWidget(parent, flags)
{
}

View file

@ -1,23 +0,0 @@
//
// InterfaceView.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/28/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_InterfaceView_h
#define hifi_InterfaceView_h
#include <QtWidgets/QOpenGLWidget>
class InterfaceView : public QOpenGLWidget {
Q_OBJECT
public:
InterfaceView(QWidget* parent = 0, Qt::WindowFlags flags = 0);
};
#endif // hifi_InterfaceView_h

View file

@ -1,69 +0,0 @@
//
// LoginDialog.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 2015-02-03.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "LoginDialog.h"
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
LoginDialog::LoginDialog(QWidget* parent) :
QDialog(parent)
{
setupGUI();
setWindowTitle("Login");
setModal(true);
}
void LoginDialog::setupGUI() {
// setup a grid layout
QGridLayout* formGridLayout = new QGridLayout(this);
_usernameLineEdit = new QLineEdit(this);
QLabel* usernameLabel = new QLabel(this);
usernameLabel->setText("Username");
usernameLabel->setBuddy(_usernameLineEdit);
formGridLayout->addWidget(usernameLabel, 0, 0);
formGridLayout->addWidget(_usernameLineEdit, 1, 0);
_passwordLineEdit = new QLineEdit(this);
_passwordLineEdit->setEchoMode(QLineEdit::Password);
QLabel* passwordLabel = new QLabel(this);
passwordLabel->setText("Password");
passwordLabel->setBuddy(_passwordLineEdit);
formGridLayout->addWidget(passwordLabel, 2, 0);
formGridLayout->addWidget(_passwordLineEdit, 3, 0);
QDialogButtonBox* buttons = new QDialogButtonBox(this);
QPushButton* okButton = buttons->addButton(QDialogButtonBox::Ok);
QPushButton* cancelButton = buttons->addButton(QDialogButtonBox::Cancel);
okButton->setText("Login");
connect(cancelButton, &QPushButton::clicked, this, &QDialog::close);
connect(okButton, &QPushButton::clicked, this, &LoginDialog::loginButtonClicked);
formGridLayout->addWidget(buttons, 4, 0, 1, 2);
setLayout(formGridLayout);
}
void LoginDialog::loginButtonClicked() {
emit credentialsEntered(_usernameLineEdit->text(), _passwordLineEdit->text());
close();
}

View file

@ -1,34 +0,0 @@
//
// LoginDialog.h
// gvr-interface/src
//
// Created by Stephen Birarda on 2015-02-03.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_LoginDialog_h
#define hifi_LoginDialog_h
#include <QtWidgets/QDialog>
class QLineEdit;
class LoginDialog : public QDialog {
Q_OBJECT
public:
LoginDialog(QWidget* parent = 0);
signals:
void credentialsEntered(const QString& username, const QString& password);
private slots:
void loginButtonClicked();
private:
void setupGUI();
QLineEdit* _usernameLineEdit;
QLineEdit* _passwordLineEdit;
};
#endif // hifi_LoginDialog_h

View file

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

View file

@ -1,57 +0,0 @@
//
// RenderingClient.h
// gvr-interface/src
//
// Created by Stephen Birarda on 1/20/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderingClient_h
#define hifi_RenderingClient_h
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QTimer>
#include <AvatarData.h>
#include "Client.h"
class RenderingClient : public Client {
Q_OBJECT
public:
RenderingClient(QObject* parent = 0, const QString& launchURLString = QString());
const glm::vec3& getPosition() const { return _position; }
const glm::quat& getOrientation() const { return _orientation; }
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
static glm::vec3 getPositionForAudio() { return _instance->getPosition(); }
static glm::quat getOrientationForAudio() { return _instance->getOrientation(); }
virtual void cleanupBeforeQuit();
private slots:
void goToLocation(const glm::vec3& newPosition,
bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation);
void sendAvatarPacket();
private:
virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
static RenderingClient* _instance;
glm::vec3 _position;
glm::quat _orientation;
QTimer _avatarTimer;
AvatarData _fakeAvatar;
};
#endif // hifi_RenderingClient_h

View file

@ -1,41 +0,0 @@
//
// InterfaceActivity.java
// gvr-interface/java
//
// Created by Stephen Birarda on 1/26/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
package io.highfidelity.gvrinterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.WindowManager;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtActivity;
public class InterfaceActivity extends QtActivity {
public static native void handleHifiURL(String hifiURLString);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Get the intent that started this activity in case we have a hifi:// URL to parse
Intent intent = getIntent();
if (intent.getAction() == Intent.ACTION_VIEW) {
Uri data = intent.getData();
if (data.getScheme().equals("hifi")) {
handleHifiURL(data.toString());
}
}
}
}

View file

@ -1,28 +0,0 @@
//
// main.cpp
// gvr-interface/src
//
// Created by Stephen Birarda on 11/17/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GVRMainWindow.h"
#include "GVRInterface.h"
int main(int argc, char* argv[]) {
GVRInterface app(argc, argv);
GVRMainWindow mainWindow;
#ifdef ANDROID
mainWindow.showFullScreen();
#else
mainWindow.showMaximized();
#endif
app.setMainWindow(&mainWindow);
return app.exec();
}

View file

@ -1,51 +0,0 @@
//
// InterfaceBetaActivity.java
// gvr-interface/java
//
// Created by Stephen Birarda on 1/27/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
package io.highfidelity.gvrinterface;
import android.os.Bundle;
import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.UpdateManager;
public class InterfaceBetaActivity extends InterfaceActivity {
public String _hockeyAppID;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_hockeyAppID = getString(R.string.HockeyAppID);
checkForUpdates();
}
@Override
protected void onPause() {
super.onPause();
UpdateManager.unregister();
}
@Override
protected void onResume() {
super.onResume();
checkForCrashes();
}
private void checkForCrashes() {
CrashManager.register(this, _hockeyAppID);
}
private void checkForUpdates() {
// Remove this for store / production builds!
UpdateManager.register(this, _hockeyAppID);
}
}

View file

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

View file

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

View file

@ -202,6 +202,10 @@ if (WIN32)
set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF")
endif()
if (ANDROID)
set(PLATFORM_DISPLAY_PLUGINS oculusMobile oculusMobilePlugin)
endif()
# link required hifi libraries
link_hifi_libraries(
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
@ -211,7 +215,10 @@ link_hifi_libraries(
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
# Platform specific GL libraries
${PLATFORM_GL_BACKEND}
# Plaform specific display plugins libraries
${PLATFORM_DISPLAY_PLUGINS}
shaders
)
@ -265,7 +272,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
endforeach()
# include headers for interface and InterfaceConfig.
include_directories("${PROJECT_SOURCE_DIR}/src")
target_include_directories(${TARGET_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/src")
if (ANDROID)
find_library(ANDROID_LOG_LIB log)
@ -276,7 +283,7 @@ target_link_libraries(
${TARGET_NAME}
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::Widgets
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
Qt5::WebChannel
Qt5::WebChannel
${PLATFORM_QT_LIBRARIES}
)

View file

@ -1,9 +0,0 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
Text {
color: "white";
style: Text.Outline;
styleColor: "black";
font.pixelSize: 15;
}

View file

@ -1,410 +0,0 @@
import Hifi 1.0 as Hifi
import QtQuick 2.3
import '.'
Item {
id: stats
anchors.leftMargin: 300
objectName: "StatsItem"
property int modality: Qt.NonModal
implicitHeight: row.height
implicitWidth: row.width
visible: false
Component.onCompleted: {
stats.parentChanged.connect(fill);
fill();
}
Component.onDestruction: {
stats.parentChanged.disconnect(fill);
}
function fill() {
// This will cause a warning at shutdown, need to find another way to remove
// the warning other than filling the anchors to the parent
anchors.horizontalCenter = parent.horizontalCenter
}
Hifi.Stats {
id: root
objectName: "Stats"
implicitHeight: row.height
implicitWidth: row.width
anchors.horizontalCenter: parent.horizontalCenter
readonly property string bgColor: "#AA111111"
Row {
id: row
spacing: 8
Rectangle {
width: generalCol.width + 8;
height: generalCol.height + 8;
color: root.bgColor;
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: generalCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Servers: " + root.serverCount
}
StatText {
text: "Avatars: " + root.avatarCount
}
StatText {
text: "Game Rate: " + root.gameLoopRate
}
StatText {
visible: root.expanded
text: root.gameUpdateStats
}
StatText {
text: "Render Rate: " + root.renderrate.toFixed(2);
}
StatText {
text: "Present Rate: " + root.presentrate.toFixed(2);
}
StatText {
visible: root.expanded
text: " Present New Rate: " + root.presentnewrate.toFixed(2);
}
StatText {
visible: root.expanded
text: " Present Drop Rate: " + root.presentdroprate.toFixed(2);
}
StatText {
text: "Stutter Rate: " + root.stutterrate.toFixed(3);
visible: root.stutterrate != -1;
}
StatText {
text: "Missed Frame Count: " + root.appdropped;
visible: root.appdropped > 0;
}
StatText {
text: "Long Render Count: " + root.longrenders;
visible: root.longrenders > 0;
}
StatText {
text: "Long Submit Count: " + root.longsubmits;
visible: root.longsubmits > 0;
}
StatText {
text: "Long Frame Count: " + root.longframes;
visible: root.longframes > 0;
}
StatText {
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
}
StatText {
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
}
StatText {
visible: root.expanded
text: "Asset Mbps In/Out: " + root.assetMbpsIn.toFixed(2) + "/" + root.assetMbpsOut.toFixed(2)
}
StatText {
visible: root.expanded
text: "Avatars Updated: " + root.updatedAvatarCount
}
StatText {
visible: root.expanded
text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount
}
}
}
Rectangle {
width: pingCol.width + 8
height: pingCol.height + 8
color: root.bgColor;
visible: root.audioPing != -2
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: pingCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Audio ping/loss: " + root.audioPing + "/" + root.audioPacketLoss + "%"
}
StatText {
text: "Avatar ping: " + root.avatarPing
}
StatText {
text: "Entities avg ping: " + root.entitiesPing
}
StatText {
text: "Asset ping: " + root.assetPing
}
StatText {
visible: root.expanded;
text: "Messages max ping: " + root.messagePing
}
}
}
Rectangle {
width: geoCol.width + 8
height: geoCol.height + 8
color: root.bgColor;
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: geoCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Position: " + root.position.x.toFixed(1) + ", " +
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
}
StatText {
text: "Speed: " + root.speed.toFixed(1)
}
StatText {
text: "Yaw: " + root.yaw.toFixed(1)
}
StatText {
visible: root.expanded;
text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " +
root.avatarMixerInPps + "pps";
}
StatText {
visible: root.expanded;
text: "Avatar Mixer Out: " + root.avatarMixerOutKbps + " kbps, " +
root.avatarMixerOutPps + "pps, " +
root.myAvatarSendRate.toFixed(2) + "hz";
}
StatText {
visible: root.expanded;
text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " +
root.audioMixerInPps + "pps";
}
StatText {
visible: root.expanded;
text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " +
root.audioMixerOutPps + "pps";
}
StatText {
visible: root.expanded;
text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " +
"Silent: " + root.audioSilentInboundPPS + " pps";
}
StatText {
visible: root.expanded;
text: "Audio Out Mic: " + root.audioOutboundPPS + " pps, " +
"Silent: " + root.audioSilentOutboundPPS + " pps";
}
StatText {
visible: root.expanded;
text: "Audio Codec: " + root.audioCodec + " Noise Gate: " +
root.audioNoiseGate;
}
StatText {
visible: root.expanded;
text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps";
}
StatText {
visible: root.expanded;
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
", Pending: " + root.downloadsPending;
}
StatText {
visible: root.expanded;
text: "Processing: " + root.processing +
", Pending: " + root.processingPending;
}
StatText {
visible: root.expanded && root.downloadUrls.length > 0;
text: "Download URLs:"
}
ListView {
width: geoCol.width
height: root.downloadUrls.length * 15
visible: root.expanded && root.downloadUrls.length > 0;
model: root.downloadUrls
delegate: StatText {
visible: root.expanded;
text: modelData.length > 30
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
: modelData
}
}
}
}
Rectangle {
width: octreeCol.width + 8
height: octreeCol.height + 8
color: root.bgColor;
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: octreeCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Render Engine: " + root.engineFrameTime.toFixed(1) + " ms"
}
StatText {
text: "Batch: " + root.batchFrameTime.toFixed(1) + " ms"
}
StatText {
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
}
StatText {
text: "Drawcalls: " + root.drawcalls
}
StatText {
text: "Triangles: " + root.triangles +
" / Material Switches: " + root.materialSwitches
}
StatText {
visible: root.expanded;
text: "GPU Free Memory: " + root.gpuFreeMemory + " MB";
}
StatText {
visible: root.expanded;
text: "GPU Textures: ";
}
StatText {
visible: root.expanded;
text: " Count: " + root.gpuTextures;
}
StatText {
visible: root.expanded;
text: " Pressure State: " + root.gpuTextureMemoryPressureState;
}
StatText {
text: " Resource Allocated / Populated / Pending: ";
}
StatText {
text: " " + root.gpuTextureResourceMemory + " / " + root.gpuTextureResourcePopulatedMemory + " / " + root.texturePendingTransfers + " MB";
}
StatText {
visible: root.expanded;
text: " Resident Memory: " + root.gpuTextureResidentMemory + " MB";
}
StatText {
visible: root.expanded;
text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB";
}
StatText {
visible: root.expanded;
text: " External Memory: " + root.gpuTextureExternalMemory + " MB";
}
StatText {
visible: root.expanded;
text: "GPU Buffers: "
}
StatText {
visible: root.expanded;
text: " Count: " + root.gpuBuffers;
}
StatText {
visible: root.expanded;
text: " Memory: " + root.gpuBufferMemory + " MB";
}
StatText {
visible: root.expanded;
text: "GL Swapchain Memory: " + root.glContextSwapchainMemory + " MB";
}
StatText {
visible: root.expanded;
text: "QML Texture Memory: " + root.qmlTextureMemory + " MB";
}
StatText {
visible: root.expanded;
text: "Items rendered / considered: " +
root.itemRendered + " / " + root.itemConsidered;
}
StatText {
visible: root.expanded;
text: " out of view: " + root.itemOutOfView +
" too small: " + root.itemTooSmall;
}
StatText {
visible: root.expanded;
text: "Shadows rendered / considered: " +
root.shadowRendered + " / " + root.shadowConsidered;
}
StatText {
visible: root.expanded;
text: " out of view: " + root.shadowOutOfView +
" too small: " + root.shadowTooSmall;
}
StatText {
visible: !root.expanded
text: "Octree Elements Server: " + root.serverElements +
" Local: " + root.localElements;
}
StatText {
visible: root.expanded
text: "Octree Sending Mode: " + root.sendingMode;
}
StatText {
visible: root.expanded
text: "Octree Packets to Process: " + root.packetStats;
}
StatText {
visible: root.expanded
text: "Octree Elements - ";
}
StatText {
visible: root.expanded
text: "\tServer: " + root.serverElements +
" Internal: " + root.serverInternal +
" Leaves: " + root.serverLeaves;
}
StatText {
visible: root.expanded
text: "\tLocal: " + root.localElements +
" Internal: " + root.localInternal +
" Leaves: " + root.localLeaves;
}
StatText {
visible: root.expanded
text: "LOD: " + root.lodStatus;
}
}
}
}
Rectangle {
y: 250
visible: root.timingExpanded
width: perfText.width + 8
height: perfText.height + 8
color: root.bgColor;
StatText {
x: 4; y: 4
id: perfText
font.family: root.monospaceFont
text: "------------------------------------------ Function " +
"--------------------------------------- --msecs- -calls--\n" +
root.timingStats;
}
}
Connections {
target: root.parent
onWidthChanged: {
root.x = root.parent.width - root.width;
}
}
}
}

View file

@ -1,6 +1,6 @@
import QtQuick 2.5
import QtWebChannel 1.0
import QtWebEngine 1.5
import QtWebView 1.1
import controlsUit 1.0
import "styles" as HifiStyles
@ -207,24 +207,6 @@ ScrollingWindow {
url: "https://highfidelity.com/"
profile: FileTypeProfile;
// Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.Deferred
worldId: WebEngineScript.MainWorld
}
// Detect when may want to raise and lower keyboard.
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
anchors.top: buttons.bottom
anchors.topMargin: 8
anchors.bottom: parent.bottom
@ -242,7 +224,7 @@ ScrollingWindow {
}
onLoadingChanged: {
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
if (loadRequest.status === WebView.LoadSucceededStatus) {
addressBar.text = loadRequest.url
}
root.loadingChanged(loadRequest.status);

View file

@ -1,292 +0,0 @@
//
// LinkAccountBody.qml
//
// Created by Clement on 7/18/16
// 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
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import controlsUit 1.0
import stylesUit 1.0
Item {
id: linkAccountBody
clip: true
height: 300
width: root.pane.width
property bool failAfterSignUp: false
function login() {
mainTextContainer.visible = false
toggleLoading(true)
loginDialog.login(usernameField.text, passwordField.text)
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
QtObject {
id: d
readonly property int minWidth: 1440
readonly property int maxWidth: 3840
readonly property int minHeight: 150
readonly property int maxHeight: 660
function resize() {
var targetWidth = Math.max(titleWidth, form.contentWidth);
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
4 * hifi.dimensions.contentSpacing.y + form.height +
hifi.dimensions.contentSpacing.y + buttons.height;
if (additionalInformation.visible) {
targetWidth = Math.max(targetWidth, additionalInformation.width);
targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height
}
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
parent.height = 420;
/*root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y);*/
}
}
function toggleLoading(isLoading) {
linkAccountSpinner.visible = isLoading
form.visible = !isLoading
if (loginDialog.isSteamRunning()) {
additionalInformation.visible = !isLoading
}
leftButton.visible = !isLoading
buttons.visible = !isLoading
}
BusyIndicator {
id: linkAccountSpinner
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: hifi.dimensions.contentSpacing.y
}
visible: false
running: true
width: 144
height: 144
}
ShortcutText {
id: mainTextContainer
anchors {
top: parent.top
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y / 2
}
visible: false
text: qsTr("Username or password incorrect.")
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
Column {
id: form
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 0 // hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.y / 2
TextField {
id: usernameField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Username or Email")
}
TextField {
id: passwordField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Password")
echoMode: TextInput.Password
Keys.onReturnPressed: linkAccountBody.login()
}
}
InfoItem {
id: additionalInformation
anchors {
top: form.bottom
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
visible: loginDialog.isSteamRunning()
text: qsTr("Your steam account informations will not be exposed to other users.")
wrapMode: Text.WordWrap
color: hifi.colors.baseGrayHighlight
lineHeight: 3
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
Keyboard {
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
Row {
id: leftButton
anchors {
left: parent.left
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y / 2
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Sign Up")
visible: !loginDialog.isSteamRunning()
onClicked: {
bodyLoader.setSource("SignUpBody.qml")
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}
Row {
id: buttons
anchors {
right: parent.right
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y / 2
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
color: hifi.buttons.blue
onClicked: {
Qt.inputMethod.hide();
linkAccountBody.login();
}
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: {
Qt.inputMethod.hide();
root.tryDestroy();
}
}
}
Component.onCompleted: {
root.title = qsTr("Sign Into High Fidelity")
root.iconText = "<"
keyboardEnabled = HMD.active;
d.resize();
if (failAfterSignUp) {
mainTextContainer.text = "Account created successfully."
mainTextContainer.visible = true
}
//usernameField.forceActiveFocus();
}
Connections {
target: loginDialog
onHandleLoginCompleted: {
console.log("Login Succeeded, linking steam account")
if (loginDialog.isSteamRunning()) {
loginDialog.linkSteam()
} else {
bodyLoader.setSource("../WelcomeBody.qml", { "welcomeBack" : true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
onHandleLoginFailed: {
console.log("Login Failed")
mainTextContainer.visible = true
toggleLoading(false)
}
onHandleLinkCompleted: {
console.log("Link Succeeded")
bodyLoader.setSource("../WelcomeBody.qml", { "welcomeBack" : true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLinkFailed: {
console.log("Link Failed")
toggleLoading(false)
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
linkAccountBody.login()
break
}
}
}

View file

@ -1,297 +0,0 @@
//
// SignUpBody.qml
//
// Created by Stephen Birarda on 7 Dec 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import controlsUit 1.0
import stylesUit 1.0
Item {
id: signupBody
clip: true
height: root.pane.height
width: root.pane.width
function signup() {
mainTextContainer.visible = false
toggleLoading(true)
loginDialog.signup(emailField.text, usernameField.text, passwordField.text)
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
QtObject {
id: d
readonly property int minWidth: 960
readonly property int maxWidth: 2560
readonly property int minHeight: 240
readonly property int maxHeight: 1480
function resize() {
var targetWidth = Math.max(titleWidth, form.contentWidth);
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
4 * hifi.dimensions.contentSpacing.y + form.height +
hifi.dimensions.contentSpacing.y + buttons.height;
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
//parent.height = 650;
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight));
}
}
function toggleLoading(isLoading) {
linkAccountSpinner.visible = isLoading
form.visible = !isLoading
leftButton.visible = !isLoading
buttons.visible = !isLoading
}
BusyIndicator {
id: linkAccountSpinner
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: hifi.dimensions.contentSpacing.y
}
visible: false
running: true
width: 48
height: 48
}
ShortcutText {
id: mainTextContainer
anchors {
top: parent.top
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
visible: false
text: qsTr("There was an unknown error while creating your account.")
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
horizontalAlignment: Text.AlignLeft
}
Column {
id: form
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 0; // 2 * hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.y / 2
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: emailField
anchors {
verticalCenter: parent.verticalCenter
}
width: 780
placeholderText: "Email"
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: usernameField
anchors {
verticalCenter: parent.verticalCenter
}
width: 780
placeholderText: "Username"
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
text: qsTr("No spaces / special chars.")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.blueAccent
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: passwordField
anchors {
verticalCenter: parent.verticalCenter
}
width: 780
placeholderText: "Password"
echoMode: TextInput.Password
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
text: qsTr("At least 6 characters")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.blueAccent
}
}
}
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
Keyboard {
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
Row {
id: leftButton
anchors {
left: parent.left
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y// / 2
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Existing User")
onClicked: {
bodyLoader.setSource("LinkAccountBody.qml")
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}
Row {
id: buttons
anchors {
right: parent.right
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y / 2
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Sign Up")
color: hifi.buttons.blue
onClicked: signupBody.signup()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.destroy()
}
}
Component.onCompleted: {
root.title = qsTr("Create an Account")
root.iconText = "<"
keyboardEnabled = HMD.active;
d.resize();
emailField.forceActiveFocus();
}
Connections {
target: loginDialog
onHandleSignupCompleted: {
console.log("Sign Up Succeeded");
// now that we have an account, login with that username and password
loginDialog.login(usernameField.text, passwordField.text)
}
onHandleSignupFailed: {
console.log("Sign Up Failed")
toggleLoading(false)
mainTextContainer.text = errorString
mainTextContainer.visible = true
d.resize();
}
onHandleLoginCompleted: {
bodyLoader.setSource("../WelcomeBody.qml", { "welcomeBack": false })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginFailed: {
// we failed to login, show the LoginDialog so the user will try again
bodyLoader.setSource("LinkAccountBody.qml", { "failAfterSignUp": true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
signupBody.signup()
break
}
}
}

View file

@ -9,7 +9,7 @@
//
import QtQuick 2.5
import QtWebEngine 1.1
import QtWebView 1.1
import QtWebChannel 1.0
import "windows" as Windows
@ -62,36 +62,9 @@ Windows.ScrollingWindow {
url: "about:blank"
anchors.fill: parent
focus: true
profile: HFWebEngineProfile;
property string userScriptUrl: ""
// Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// Detect when may want to raise and lower keyboard.
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: webview.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
function onWebEventReceived(event) {
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));

View file

@ -1,6 +1,6 @@
import QtQuick 2.5
import QtWebChannel 1.0
import QtWebEngine 1.5
import QtWebView 1.1
import "controls"
import controlsUit 1.0 as HifiControls
@ -30,7 +30,7 @@ Item {
webview.profile = profile;
}
WebEngineView {
WebView {
id: webview
objectName: "webEngineView"
x: 0
@ -38,36 +38,8 @@ Item {
width: parent.width
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
profile: HFWebEngineProfile;
property string userScriptUrl: ""
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: webview.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
property string newUrl: ""
Component.onCompleted: {
@ -83,17 +55,13 @@ Item {
web.address = url;
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onLoadingChanged: {
keyboardRaised = false;
punctuationMode = false;
keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
if (WebView.LoadStartedStatus == loadRequest.status) {
urlAppend(loadRequest.url.toString())
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {

View file

@ -1,7 +1,7 @@
//
// Web3DOverlay.qml
//
// Created by David Rowe on 16 Dec 2016.
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -9,23 +9,68 @@
//
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "controls" as Controls
Item {
Controls.WebView {
// This is for JS/QML communication, which is unused in a Web3DOverlay,
// but not having this here results in spurious warnings about a
// missing signal
signal sendToScript(var message);
function onWebEventReceived(event) {
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
property string url
RadialGradient {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#262626" }
GradientStop { position: 1.0; color: "#000000" }
}
}
Component.onCompleted: {
eventBridge.webEventReceived.connect(onWebEventReceived);
function shortUrl(url) {
var hostBegin = url.indexOf("://");
if (hostBegin > -1) {
url = url.substring(hostBegin + 3);
}
var portBegin = url.indexOf(":");
if (portBegin > -1) {
url = url.substring(0, portBegin);
}
var pathBegin = url.indexOf("/");
if (pathBegin > -1) {
url = url.substring(0, pathBegin);
}
if (url.length > 45) {
url = url.substring(0, 45);
}
return url;
}
}
Text {
id: urlText
text: shortUrl(url)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.fill: parent
anchors.rightMargin: 10
anchors.leftMargin: 10
font.family: "Cairo"
font.weight: Font.DemiBold
font.pointSize: 48
fontSizeMode: Text.Fit
color: "#FFFFFF"
minimumPixelSize: 5
}
Image {
id: hand
source: "../../icons/hand.svg"
width: 300
height: 300
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottomMargin: 100
anchors.rightMargin: 100
}
}

View file

@ -1,5 +1,5 @@
import QtQuick 2.7
import QtWebEngine 1.5
import QtWebView 1.1
import QtWebChannel 1.0
import QtQuick.Controls 2.2
@ -67,7 +67,7 @@ Item {
}
function onLoadingChanged(loadRequest) {
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
if (WebView.LoadStartedStatus === loadRequest.status) {
// Required to support clicking on "hifi://" links
var url = loadRequest.url.toString();
@ -79,52 +79,25 @@ Item {
}
}
if (WebEngineView.LoadFailedStatus === loadRequest.status) {
console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
if (WebView.LoadFailedStatus === loadRequest.status) {
console.log("Tablet Web View failed to load url: " + loadRequest.url.toString());
}
if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
if (WebView.LoadSucceededStatus === loadRequest.status) {
//disable Chromium's scroll bars
}
}
WebEngineView {
WebView {
id: webViewCore
width: parent.width
height: parent.height
profile: HFWebEngineProfile;
settings.pluginsEnabled: true
settings.touchIconsEnabled: true
settings.allowRunningInsecureContent: true
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: flick.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);

View file

@ -1,5 +1,4 @@
import QtQuick 2.7
import QtWebEngine 1.5
import controlsUit 1.0 as HiFiControls
import "../styles" as HifiStyles
import stylesUit 1.0

View file

@ -1,82 +0,0 @@
//
// ImageButton.qml
// interface/resources/qml/controlsUit
//
// Created by Gabriel Calero & Cristian Duarte on 12 Oct 2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Layouts 1.3
import "../stylesUit" as HifiStyles
Item {
id: button
property string text: ""
property string source : ""
property string hoverSource : ""
property real fontSize: 10
property string fontColor: "#FFFFFF"
property string hoverFontColor: "#000000"
signal clicked();
Rectangle {
color: "transparent"
anchors.fill: parent
Image {
id: image
anchors.fill: parent
source: button.source
}
HifiStyles.FiraSansRegular {
id: buttonText
anchors.centerIn: parent
text: button.text
color: button.fontColor
font.pixelSize: button.fontSize
}
MouseArea {
anchors.fill: parent
onClicked: button.clicked();
onEntered: {
button.state = "hover state";
}
onExited: {
button.state = "base state";
}
}
}
states: [
State {
name: "hover state"
PropertyChanges {
target: image
source: button.hoverSource
}
PropertyChanges {
target: buttonText
color: button.hoverFontColor
}
},
State {
name: "base state"
PropertyChanges {
target: image
source: button.source
}
PropertyChanges {
target: buttonText
color: button.fontColor
}
}
]
}

View file

@ -9,9 +9,9 @@
//
import QtQuick 2.7
import QtWebEngine 1.5
import QtWebView 1.1
WebEngineView {
WebView {
id: root
Component.onCompleted: {
@ -24,7 +24,7 @@ WebEngineView {
onLoadingChanged: {
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
if (WebView.LoadStartedStatus == loadRequest.status) {
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
@ -34,5 +34,11 @@ WebEngineView {
}
}
signal newViewRequested();
signal windowCloseRequested();
signal featurePermissionRequested();
property var profile: null;
WebSpinner { }
}

View file

@ -9,10 +9,10 @@
//
import QtQuick 2.5
import QtWebEngine 1.5
import QtWebView 1.1
AnimatedImage {
property WebEngineView webview: parent
property WebView webview: parent
source: "../../icons/loader-snake-64-w.gif"
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
playing: visible

View file

@ -1,26 +0,0 @@
//
// FocusHack.qml
//
// Created by Bradley Austin Davis on 21 Jan 2015
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
FocusScope {
id: root
objectName: "FocusHack"
TextInput {
id: textInput;
focus: true
width: 10; height: 10
onActiveFocusChanged: root.destroy()
}
function start() {
}
}

View file

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

View file

@ -1,71 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../controls" as HifiControls
import ".."
Item {
id: actionBar
x:0
y:0
width: 300
height: 300
z: -1
signal sendToScript(var message);
signal windowClosed();
property bool shown: true
onShownChanged: {
actionBar.visible = shown;
}
Rectangle {
anchors.fill : parent
color: "transparent"
Flow {
id: flowMain
spacing: 10
flow: Flow.TopToBottom
layoutDirection: Flow.TopToBottom
anchors.fill: parent
anchors.margins: 4
}
}
Component.onCompleted: {
// put on bottom
x = 7;
y = 7;
width = 300;
height = 300;
}
function addButton(properties) {
var component = Qt.createComponent("button.qml");
if (component.status == Component.Ready) {
var button = component.createObject(flowMain);
// copy all properites to button
var keys = Object.keys(properties).forEach(function (key) {
button[key] = properties[key];
});
return button;
} else if( component.status == Component.Error) {
console.log("Load button errors " + component.errorString());
}
}
function urlHelper(src) {
if (src.match(/\bhttp/)) {
return src;
} else {
return "../../../" + src;
}
}
}

View file

@ -1,84 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../controls" as HifiControls
import ".."
Item {
id: bar
x:0
y:0
width: 300
height: 300
z: -1
signal sendToScript(var message);
signal windowClosed();
property bool shown: true
onShownChanged: {
bar.visible = shown;
}
Rectangle {
anchors.fill : parent
color: "transparent"
Flow {
id: flowMain
spacing: 10
flow: Flow.TopToBottom
layoutDirection: Flow.TopToBottom
anchors.fill: parent
anchors.margins: 4
}
}
function relocateAndResize(newWindowWidth, newWindowHeight) {
x = newWindowWidth-328;
y = 19;
width = 300;
height = 300;
}
function onWindowGeometryChanged(rect) {
relocateAndResize(rect.width, rect.height);
}
Component.onCompleted: {
relocateAndResize(parent.width, parent.height);
Window.geometryChanged.connect(onWindowGeometryChanged); // In devices with bars appearing at startup we should listen for this
}
Component.onDestruction: {
Window.geometryChanged.disconnect(onWindowGeometryChanged);
}
function addButton(properties) {
var component = Qt.createComponent("button.qml");
if (component.status == Component.Ready) {
var button = component.createObject(flowMain);
// copy all properites to button
var keys = Object.keys(properties).forEach(function (key) {
button[key] = properties[key];
});
return button;
} else if( component.status == Component.Error) {
console.log("Load button errors " + component.errorString());
}
}
function urlHelper(src) {
if (src.match(/\bhttp/)) {
return src;
} else {
return "../../../" + src;
}
}
}

View file

@ -1,117 +0,0 @@
//
// AvatarOption.qml
// interface/resources/qml/hifi/android
//
// Created by Cristian Duarte & Gabriel Calero on 12 Oct 2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick.Layouts 1.3
import QtQuick 2.5
import controlsUit 1.0 as HifiControlsUit
ColumnLayout {
id: itemRoot
property string type: "";
property string thumbnailUrl: "";
property string avatarUrl: "";
property string avatarName: "";
property bool avatarSelected: false;
property string methodName: "";
property string actionText: "";
spacing: 4 * 3
signal sendToParentQml(var message);
Image {
id: itemImage
Layout.preferredWidth: 250 * 3
Layout.preferredHeight: 140 * 3
source: thumbnailUrl
asynchronous: true
fillMode: Image.PreserveAspectFit
MouseArea {
id: itemArea
anchors.fill: parent
hoverEnabled: true
enabled: true
onClicked: {
if (type=="avatar") {
if (!avatarSelected) sendToParentQml({ method: "selectAvatar", params: { avatarUrl: avatarUrl } });
} else {
sendToParentQml({ method: methodName, params: { } });
}
}
}
}
Text {
id: itemName
text: avatarName
color: "#FFFFFF"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.horizontalCenter: itemImage.horizontalCenter
font.pointSize: 5*3
wrapMode: Text.WordWrap
width: parent
MouseArea {
id: itemNameArea
anchors.fill: parent
hoverEnabled: true
enabled: true
onClicked: {
if (type=="avatar") {
if (!avatarSelected) sendToParentQml({ method: "selectAvatar", params: { avatarUrl: avatarUrl } });
} else {
sendToParentQml({ method: methodName, params: { } });
}
}
}
}
HifiControlsUit.ImageButton {
width: 140*3
height: 35*3
text: type=="extra" ? actionText: "CHOOSE"
source: "../../../../icons/button.svg"
hoverSource: "../../../../icons/button-a.svg"
fontSize: 18*3
fontColor: "#2CD8FF"
hoverFontColor: "#FFFFFF"
anchors {
horizontalCenter: itemName.horizontalCenter
}
visible: !avatarSelected
onClicked: {
if (type=="avatar") {
if (!avatarSelected) sendToParentQml({ method: "selectAvatar", params: { avatarUrl: avatarUrl } });
} else {
sendToParentQml({ method: methodName, params: { } });
}
}
}
Image {
id: tickImage
width: 35 * 3
height: 35 * 3
source: "../../../icons/tick.svg"
anchors {
horizontalCenter: itemName.horizontalCenter
}
visible: avatarSelected
}
Component.onCompleted:{
sendToParentQml.connect(sendToScript);
}
}

View file

@ -1,63 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import Qt.labs.settings 1.0
import "../desktop" as OriginalDesktop
import ".."
import "."
import "./toolbars"
OriginalDesktop.Desktop {
id: desktop
MouseArea {
id: hoverWatch
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
scrollGestureEnabled: false // we don't need/want these
onEntered: ApplicationCompositor.reticleOverDesktop = true
onExited: ApplicationCompositor.reticleOverDesktop = false
acceptedButtons: Qt.NoButton
}
Component { id: toolbarBuilder; Toolbar { } }
// This used to create sysToolbar dynamically with a call to getToolbar() within onCompleted.
// Beginning with QT 5.6, this stopped working, as anything added to toolbars too early got
// wiped during startup.
Toolbar {
id: sysToolbar;
objectName: "com.highfidelity.interface.toolbar.system";
// Magic: sysToolbar.x and y come from settings, and are bound before the properties specified here are applied.
x: sysToolbar.x;
y: sysToolbar.y;
}
property var toolbars: (function (map) { // answer dictionary preloaded with sysToolbar
map[sysToolbar.objectName] = sysToolbar;
return map; })({});
Component.onCompleted: {
}
// Accept a download through the webview
property bool webViewProfileSetup: false
property string currentUrl: ""
property string adaptedPath: ""
property string tempDir: ""
// Create or fetch a toolbar with the given name
function getToolbar(name) {
var result = toolbars[name];
if (!result) {
result = toolbars[name] = toolbarBuilder.createObject(desktop, {});
result.objectName = name;
}
return result;
}
}

View file

@ -1,56 +0,0 @@
//
// HifiAndroidConstants.qml
// interface/resources/qml/+android
//
// Created by Gabriel Calero & Cristian Duarte on 23 Oct 2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.4
import QtQuick.Window 2.2
Item {
id: android
readonly property alias dimen: dimen
readonly property alias color: color
Item {
id: dimen
readonly property bool atLeast1440p: Screen.width >= 2560 && Screen.height >= 1440
readonly property real windowLessWidth: atLeast1440p ? 378 : 284
readonly property real windowLessHeight: atLeast1440p ? 192 : 144
readonly property real windowZ: 100
readonly property real headerHeight: atLeast1440p ? 276 : 207
readonly property real headerIconPosX: atLeast1440p ? 90 : 67
readonly property real headerIconPosY: atLeast1440p ? 108 : 81
readonly property real headerIconWidth: atLeast1440p ? 111 : 83
readonly property real headerIconHeight: atLeast1440p ? 111 : 83
readonly property real headerIconTitleDistance: atLeast1440p ? 151 : 113
readonly property real headerHideWidth: atLeast1440p ? 150 : 112
readonly property real headerHideHeight: atLeast1440p ? 150 : 112
readonly property real headerHideRightMargin: atLeast1440p ? 110 : 82
readonly property real headerHideTopMargin: atLeast1440p ? 90 : 67
readonly property real headerHideIconWidth: atLeast1440p ? 70 : 52
readonly property real headerHideIconHeight: atLeast1440p ? 45 : 33
readonly property real headerHideTextTopMargin: atLeast1440p ? 36 : 27
readonly property real botomHudWidth: 366
readonly property real botomHudHeight: 180
}
Item {
id: color
readonly property color gradientTop: "#4E4E4E"
readonly property color gradientBottom: "#242424"
}
}

View file

@ -1,71 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../controls" as HifiControls
import ".."
Item {
id: bar
x:300
y:0
width: 300
height: 300
z: -1
signal sendToScript(var message);
signal windowClosed();
property bool shown: true
onShownChanged: {
bar.visible = shown;
}
Rectangle {
anchors.fill : parent
color: "transparent"
Flow {
id: flowMain
spacing: 10
flow: Flow.TopToBottom
layoutDirection: Flow.TopToBottom
anchors.fill: parent
anchors.margins: 4
}
}
Component.onCompleted: {
// put on bottom
x = 300;
y = 0;
width = 300;
height = 300;
}
function addButton(properties) {
var component = Qt.createComponent("button.qml");
if (component.status == Component.Ready) {
var button = component.createObject(flowMain);
// copy all properites to button
var keys = Object.keys(properties).forEach(function (key) {
button[key] = properties[key];
});
return button;
} else if( component.status == Component.Error) {
console.log("Load button errors " + component.errorString());
}
}
function urlHelper(src) {
if (src.match(/\bhttp/)) {
return src;
} else {
return "../../../" + src;
}
}
}

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