mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 13:53:38 +02:00
supporting layered framebuffer
This commit is contained in:
commit
a578eab831
1485 changed files with 66214 additions and 34251 deletions
28
.gitignore
vendored
28
.gitignore
vendored
|
@ -14,13 +14,17 @@ Makefile
|
|||
|
||||
# Android Studio
|
||||
*.iml
|
||||
*.class
|
||||
local.properties
|
||||
android/gradle*
|
||||
android/.gradle
|
||||
android/app/src/main/jniLibs
|
||||
android/app/libs
|
||||
android/app/src/main/res/values/libs.xml
|
||||
android/app/src/main/assets/bundled
|
||||
android/**/src/main/jniLibs
|
||||
android/**/libs
|
||||
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
|
||||
|
@ -83,9 +87,6 @@ npm-debug.log
|
|||
# Android studio files
|
||||
*___jb_old___
|
||||
|
||||
# Generated assets for Android
|
||||
android/app/src/main/assets
|
||||
|
||||
# Resource binary file
|
||||
interface/compiledResources
|
||||
|
||||
|
@ -94,3 +95,16 @@ interface/resources/GPUCache/*
|
|||
|
||||
# package lock file for JSDoc tool
|
||||
tools/jsdoc/package-lock.json
|
||||
|
||||
# Python compile artifacts
|
||||
**/__pycache__
|
||||
|
||||
# ignore unneeded unity project files for avatar exporter
|
||||
tools/unity-avatar-exporter/Library
|
||||
tools/unity-avatar-exporter/Logs
|
||||
tools/unity-avatar-exporter/Packages
|
||||
tools/unity-avatar-exporter/ProjectSettings
|
||||
tools/unity-avatar-exporter/Temp
|
||||
server-console/package-lock.json
|
||||
vcpkg/
|
||||
/tools/nitpick/compiledResources
|
||||
|
|
12
BUILD.md
12
BUILD.md
|
@ -48,6 +48,18 @@ The path it needs to be set to will depend on where and how Qt5 was installed. e
|
|||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.10.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||
|
||||
#### Vcpkg
|
||||
|
||||
Hifi uses vcpkg to download and build dependencies.
|
||||
You do not need to install vcpkg.
|
||||
|
||||
Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory.
|
||||
However, those files can potentially get cleaned up by the OS, so in order to avoid this and having to redo the lengthy build step, you can set the following environment variable:
|
||||
|
||||
export HIFI_VCPKG_BASE=/path/to/directory
|
||||
|
||||
Where /path/to/directory is the path to a directory where you wish the build files to get stored.
|
||||
|
||||
#### Generating build files
|
||||
|
||||
Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean.
|
||||
|
|
|
@ -6,13 +6,8 @@ Building is currently supported on OSX, Windows and Linux platforms, but develop
|
|||
|
||||
You will need the following tools to build Android targets.
|
||||
|
||||
* [Gradle](https://gradle.org/install/)
|
||||
* [Android Studio](https://developer.android.com/studio/index.html)
|
||||
|
||||
### Gradle
|
||||
|
||||
Install gradle version 4.1 or higher. Following the instructions to install via [SDKMAN!](http://sdkman.io/install.html) is recommended.
|
||||
|
||||
### Android Studio
|
||||
|
||||
Download the Android Studio installer and run it. Once installed, at the welcome screen, click configure in the lower right corner and select SDK manager
|
||||
|
@ -29,6 +24,8 @@ From the SDK Tools tab select the following
|
|||
* Android SDK Tools
|
||||
* NDK (even if you have the NDK installed separately)
|
||||
|
||||
Make sure the NDK installed version is 18 (or higher)
|
||||
|
||||
# Environment
|
||||
|
||||
Setting up the environment for android builds requires some additional steps
|
||||
|
@ -51,17 +48,17 @@ Enter the repository `android` directory
|
|||
|
||||
`cd hifi/android`
|
||||
|
||||
Execute a gradle pre-build setup. This step should only need to be done once
|
||||
Execute two gradle pre-build steps. This steps should only need to be done once, unless you're working on the Android dependencies
|
||||
|
||||
`gradle setupDependencies`
|
||||
`./gradlew extractDependencies`
|
||||
|
||||
`./gradlew setupDependencies`
|
||||
|
||||
# Building & Running
|
||||
|
||||
* Open Android Studio
|
||||
* Choose _Open Existing Android Studio Project_
|
||||
* Navigate to the `hifi` repository and choose the `android` folder and select _OK_
|
||||
* If Android Studio asks you if you want to use the Gradle wrapper, select cancel and tell it where your local gradle installation is. If you used SDKMAN to install gradle it will be located in `$HOME/.sdkman/candidates/gradle/current/`
|
||||
* From the _Build_ menu select _Make Project_
|
||||
* Once the build completes, from the _Run_ menu select _Run App_
|
||||
|
||||
|
|
|
@ -91,19 +91,73 @@ In a server, it does not make sense to compile interface
|
|||
|
||||
### Running the software
|
||||
|
||||
#### Domain server
|
||||
|
||||
Running domain server:
|
||||
```bash
|
||||
./domain-server/domain-server
|
||||
```
|
||||
|
||||
#### Assignment clients
|
||||
|
||||
Running assignment client:
|
||||
```bash
|
||||
./assignment-client/assignment-client -n 6
|
||||
```
|
||||
|
||||
#### Interface
|
||||
|
||||
Running interface:
|
||||
```bash
|
||||
./interface/interface
|
||||
```
|
||||
|
||||
Go to localhost in the running interface.
|
||||
|
||||
##### Ubuntu 18.04 only
|
||||
|
||||
In Ubuntu 18.04 there is a problem related with NVidia driver library version.
|
||||
|
||||
It can be workarounded following these steps:
|
||||
|
||||
Uninstall incompatible nvtt libraries:
|
||||
```bash
|
||||
sudo apt-get remove libnvtt2 libnvtt-dev
|
||||
```
|
||||
|
||||
Install libssl1.0-dev:
|
||||
```bash
|
||||
sudo apt-get -y install libssl1.0-dev
|
||||
```
|
||||
|
||||
Clone castano nvidia-texture-tools:
|
||||
```
|
||||
git clone https://github.com/castano/nvidia-texture-tools
|
||||
cd nvidia-texture-tools/
|
||||
```
|
||||
|
||||
Make these changes in repo:
|
||||
* In file **VERSION** set `2.2.1`
|
||||
* In file **configure**:
|
||||
* set `build="release"`
|
||||
* set `-DNVTT_SHARED=1`
|
||||
|
||||
Configure, build and install:
|
||||
```
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Link compiled files:
|
||||
```
|
||||
sudo ln -s /usr/local/lib/libnvcore.so /usr/lib/libnvcore.so
|
||||
sudo ln -s /usr/local/lib/libnvimage.so /usr/lib/libnvimage.so
|
||||
sudo ln -s /usr/local/lib/libnvmath.so /usr/lib/libnvmath.so
|
||||
sudo ln -s /usr/local/lib/libnvtt.so /usr/lib/libnvtt.so
|
||||
```
|
||||
|
||||
After running this steps you can run interface:
|
||||
```
|
||||
interface/interface
|
||||
```
|
||||
|
|
65
BUILD_QUEST.md
Normal file
65
BUILD_QUEST.md
Normal file
|
@ -0,0 +1,65 @@
|
|||
Please read the [general build guide](BUILD.md) for information on building other platform. Only Quest specific instructions are found in this file.
|
||||
|
||||
# Dependencies
|
||||
|
||||
Building is currently supported on OSX, Windows and Linux platforms, but developers intending to do work on the library dependencies are strongly urged to use 64 bit Linux as a build platform
|
||||
|
||||
You will need the following tools to build Android targets.
|
||||
|
||||
* [Android Studio](https://developer.android.com/studio/index.html)
|
||||
|
||||
### Android Studio
|
||||
|
||||
Download the Android Studio installer and run it. Once installed, at the welcome screen, click configure in the lower right corner and select SDK manager
|
||||
|
||||
From the SDK Platforms tab, select API levels 24 and 26.
|
||||
|
||||
From the SDK Tools tab select the following
|
||||
|
||||
* Android SDK Build-Tools
|
||||
* GPU Debugging Tools
|
||||
* CMake (even if you have a separate CMake installation)
|
||||
* LLDB
|
||||
* Android SDK Platform-Tools
|
||||
* Android SDK Tools
|
||||
* NDK (even if you have the NDK installed separately)
|
||||
|
||||
Make sure the NDK installed version is 18 (or higher)
|
||||
|
||||
# Environment
|
||||
|
||||
Setting up the environment for android builds requires some additional steps
|
||||
|
||||
#### Set up machine specific Gradle properties
|
||||
|
||||
Create a `gradle.properties` file in $HOME/.gradle. Edit the file to contain the following
|
||||
|
||||
HIFI_ANDROID_PRECOMPILED=<your_home_directory>/Android/hifi_externals
|
||||
HIFI_ANDROID_KEYSTORE=<key_store_directory>/<keystore_name>.jks
|
||||
HIFI_ANDROID_KEYSTORE_PASSWORD=<password>
|
||||
HIFI_ANDROID_KEY_ALIAS=<key_alias>
|
||||
HIFI_ANDROID_KEY_PASSWORD=<key_password>
|
||||
|
||||
Note, do not use `$HOME` for the path. It must be a fully qualified path name.
|
||||
|
||||
### Setup the repository
|
||||
|
||||
Clone the repository
|
||||
|
||||
`git clone https://github.com/highfidelity/hifi.git`
|
||||
|
||||
Enter the repository `android` directory
|
||||
|
||||
`cd hifi/android`
|
||||
|
||||
# Building & Running
|
||||
|
||||
* Open Android Studio
|
||||
* Choose _Open Existing Android Studio Project_
|
||||
* Navigate to the `hifi` repository and choose the `android` folder and select _OK_
|
||||
* Open Gradle.settings and comment out any projects not necessary
|
||||
* From _File_ menu select _Sync with File System_ to resync Gradle settings
|
||||
* From the _Build_ menu select _Make Project_
|
||||
* From
|
||||
* Once the build completes, from the _Run_ menu select _Run App_
|
||||
|
19
BUILD_WIN.md
19
BUILD_WIN.md
|
@ -35,20 +35,7 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables
|
|||
* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
|
||||
* Set "Variable value": `C:\Qt\5.10.1\msvc2017_64\lib\cmake`
|
||||
|
||||
### Step 5. Installing [vcpkg](https://github.com/Microsoft/vcpkg)
|
||||
|
||||
* Clone the VCPKG [repository](https://github.com/Microsoft/vcpkg)
|
||||
* Follow the instructions in the [readme](https://github.com/Microsoft/vcpkg/blob/master/README.md) to bootstrap vcpkg
|
||||
* Note, you may need to do these in a _Developer Command Prompt_
|
||||
* Set an environment variable VCPKG_ROOT to the location of the cloned repository
|
||||
* Close and re-open any command prompts after setting the environment variable so that they will pick up the change
|
||||
|
||||
### Step 6. Installing OpenSSL via vcpkg
|
||||
|
||||
* In the vcpkg directory, install the 64 bit OpenSSL package with the command `.\vcpkg install openssl:x64-windows`
|
||||
* Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl`
|
||||
|
||||
### Step 7. Running CMake to Generate Build Files
|
||||
### Step 5. Running CMake to Generate Build Files
|
||||
|
||||
Run Command Prompt from Start and run the following commands:
|
||||
```
|
||||
|
@ -60,7 +47,7 @@ cmake .. -G "Visual Studio 15 Win64"
|
|||
|
||||
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
|
||||
|
||||
### Step 8. Making a Build
|
||||
### Step 6. Making a Build
|
||||
|
||||
Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
|
||||
|
||||
|
@ -68,7 +55,7 @@ Change the Solution Configuration (menu ribbon under the menu bar, next to the g
|
|||
|
||||
Run from the menu bar `Build > Build Solution`.
|
||||
|
||||
### Step 9. Testing Interface
|
||||
### Step 7. Testing Interface
|
||||
|
||||
Create another environment variable (see Step #4)
|
||||
* Set "Variable name": `_NO_DEBUG_HEAP`
|
||||
|
|
|
@ -12,7 +12,7 @@ target_python()
|
|||
|
||||
if (HIFI_ANDROID )
|
||||
execute_process(
|
||||
COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --android --build-root ${CMAKE_BINARY_DIR}
|
||||
COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --android ${HIFI_ANDROID_APP} --build-root ${CMAKE_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
else()
|
||||
|
@ -52,6 +52,7 @@ else()
|
|||
set(MOBILE 0)
|
||||
endif()
|
||||
|
||||
set(HIFI_USE_OPTIMIZED_IK OFF)
|
||||
set(BUILD_CLIENT_OPTION ON)
|
||||
set(BUILD_SERVER_OPTION ON)
|
||||
set(BUILD_TESTS_OPTION OFF)
|
||||
|
@ -80,15 +81,42 @@ endif()
|
|||
if (ANDROID)
|
||||
set(GLES_OPTION ON)
|
||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||
add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
|
||||
if (
|
||||
(${HIFI_ANDROID_APP} STREQUAL "questInterface") OR
|
||||
(${HIFI_ANDROID_APP} STREQUAL "questFramePlayer") OR
|
||||
(${HIFI_ANDROID_APP} STREQUAL "framePlayer")
|
||||
)
|
||||
# We know the quest hardware has this extension, so we can force the use of instanced stereo
|
||||
add_definitions(-DHAVE_EXT_clip_cull_distance)
|
||||
# We can also use multiview stereo techniques
|
||||
add_definitions(-DHAVE_OVR_multiview2)
|
||||
add_definitions(-DHAVE_OVR_multiview)
|
||||
# We can also use our own foveated textures
|
||||
add_definitions(-DHAVE_QCOM_texture_foveated)
|
||||
|
||||
# if set, the application itself or some library it depends on MUST implement
|
||||
# `DisplayPluginList getDisplayPlugins()` and `InputPluginList getInputPlugins()`
|
||||
add_definitions(-DCUSTOM_INPUT_PLUGINS)
|
||||
add_definitions(-DCUSTOM_DISPLAY_PLUGINS)
|
||||
set(PLATFORM_PLUGIN_LIBRARIES oculusMobile oculusMobilePlugin)
|
||||
endif()
|
||||
|
||||
# Allow client code to use preprocessor macros to distinguish between quest and non-quest builds
|
||||
if (${HIFI_ANDROID_APP} STREQUAL "questInterface")
|
||||
add_definitions(-DANDROID_APP_QUEST_INTERFACE)
|
||||
elseif(${HIFI_ANDROID_APP} STREQUAL "interface")
|
||||
add_definitions(-DANDROID_APP_INTERFACE)
|
||||
endif()
|
||||
else ()
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine)
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine Xml)
|
||||
endif ()
|
||||
|
||||
if (USE_GLES AND (NOT ANDROID))
|
||||
set(DISABLE_QML_OPTION ON)
|
||||
endif()
|
||||
|
||||
|
||||
option(HIFI_USE_OPTIMIZED_IK "USE OPTIMIZED IK" ${HIFI_USE_OPTIMIZED_IK_OPTION})
|
||||
option(BUILD_CLIENT "Build client components" ${BUILD_CLIENT_OPTION})
|
||||
option(BUILD_SERVER "Build server components" ${BUILD_SERVER_OPTION})
|
||||
option(BUILD_TESTS "Build tests" ${BUILD_TESTS_OPTION})
|
||||
|
@ -108,8 +136,10 @@ set(PLATFORM_QT_GL OpenGL)
|
|||
|
||||
if (USE_GLES)
|
||||
add_definitions(-DUSE_GLES)
|
||||
add_definitions(-DGPU_POINTER_STORAGE_SHARED)
|
||||
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gles)
|
||||
else()
|
||||
add_definitions(-DGPU_POINTER_STORAGE_RAW)
|
||||
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gl)
|
||||
endif()
|
||||
|
||||
|
@ -117,6 +147,7 @@ foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS})
|
|||
list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}")
|
||||
endforeach()
|
||||
|
||||
MESSAGE(STATUS "USE OPTIMIZED IK: " ${HIFI_USE_OPTIMIZED_IK})
|
||||
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})
|
||||
MESSAGE(STATUS "Build client: " ${BUILD_CLIENT})
|
||||
MESSAGE(STATUS "Build tests: " ${BUILD_TESTS})
|
||||
|
@ -162,6 +193,10 @@ find_package( Threads )
|
|||
add_definitions(-DGLM_FORCE_RADIANS)
|
||||
add_definitions(-DGLM_ENABLE_EXPERIMENTAL)
|
||||
add_definitions(-DGLM_FORCE_CTOR_INIT)
|
||||
if (HIFI_USE_OPTIMIZED_IK)
|
||||
MESSAGE(STATUS "SET THE USE IK DEFINITION ")
|
||||
add_definitions(-DHIFI_USE_OPTIMIZED_IK)
|
||||
endif()
|
||||
set(HIFI_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries")
|
||||
|
||||
set(EXTERNAL_PROJECT_PREFIX "project")
|
||||
|
@ -174,7 +209,7 @@ set_packaging_parameters()
|
|||
|
||||
# FIXME hack to work on the proper Android toolchain
|
||||
if (ANDROID)
|
||||
add_subdirectory(android/app)
|
||||
add_subdirectory(android/apps/${HIFI_ANDROID_APP})
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
|
71
INSTALL.md
71
INSTALL.md
|
@ -5,6 +5,7 @@ During generation, CMake should produce an `install` target and a `package` targ
|
|||
### Install
|
||||
|
||||
The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
|
||||
This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt`
|
||||
|
||||
### Packaging
|
||||
|
||||
|
@ -14,17 +15,67 @@ To produce an installer, run the `package` target.
|
|||
|
||||
To produce an executable installer on Windows, the following are required:
|
||||
|
||||
- [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.0b3
|
||||
- [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c
|
||||
- [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6
|
||||
- [Inetc Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
|
||||
- [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
|
||||
- [nsisSlideshow Plug-in for Nullsoft](http://nsis.sourceforge.net/NsisSlideshow_plug-in) - 1.7
|
||||
- [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
|
||||
- [ApplicationID plug-in for Nullsoft](http://nsis.sourceforge.net/ApplicationID_plug-in) - 1.0
|
||||
1. [7-zip](<https://www.7-zip.org/download.html>)
|
||||
|
||||
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.
|
||||
1. [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.04
|
||||
Install using defaults (will install to `C:\Program Files (x86)\NSIS`)
|
||||
1. [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c
|
||||
1. Extract Zip
|
||||
1. Copy `UAC.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `Plugins\x86-ansi\UAC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugins\x86-unicode\UAC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
1. [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6 (use the link marked **nsProcess_1_6.7z**)
|
||||
1. Extract Zip
|
||||
1. Copy `Include\nsProcess.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `Plugins\nsProcess.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugins\nsProcessW.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [InetC Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
|
||||
1. Extract Zip
|
||||
1. Copy `Plugin\x86-ansi\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugin\x86-unicode\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
|
||||
1. Extract Zip
|
||||
1. Copy `NSISpre.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `NSISpre.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
|
||||
1. [nsisSlideshow Plug-in for Nullsoft](<http://wiz0u.free.fr/prog/nsisSlideshow/>) - 1.7
|
||||
1. Extract Zip
|
||||
1. Copy `bin\nsisSlideshow.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `bin\nsisSlideshowW.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
|
||||
1. Download both Zips and unzip
|
||||
1. Copy `nsisunz\Release\nsisunz.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `NSISunzU\Plugin unicode\nsisunz.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [ApplicationID plug-in for Nullsoft]() - 1.0
|
||||
1. Download [`Pre-built DLLs`](<https://github.com/connectiblutz/NSIS-ApplicationID/releases/download/1.1/NSIS-ApplicationID.zip>)
|
||||
1. Extract Zip
|
||||
1. Copy `Release\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `ReleaseUnicode\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||
1. Install version 10.15.0 LTS
|
||||
|
||||
1. Perform a clean cmake from a new terminal.
|
||||
1. Open the `hifi.sln` Solution and select the Release configuration.
|
||||
1. Build the Solution.
|
||||
1. Build `packaged-server-console` (found under **Server Console**)
|
||||
This will add 2 folders to `build\server-console\` -
|
||||
`server-console-win32-x64` and `x64`
|
||||
1. Build CMakeTargets->PACKAGE
|
||||
Installer is now available in `build\_CPack_Packages\win64\NSIS`
|
||||
|
||||
#### OS X
|
||||
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||
Install version 10.15.0 LTS
|
||||
|
||||
Run the `package` target to create an Apple Disk Image (.dmg).
|
||||
1. Perform a clean cmake.
|
||||
1. Perform a Release build of ALL_BUILD
|
||||
1. Perform a Release build of `packaged-server-console`
|
||||
This will add a folder to `build\server-console\` -
|
||||
Sandbox-darwin-x64
|
||||
1. Perform a Release build of `package`
|
||||
Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
5
android/apps/framePlayer/CMakeLists.txt
Normal file
5
android/apps/framePlayer/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
set(TARGET_NAME framePlayer)
|
||||
setup_hifi_library(AndroidExtras)
|
||||
link_hifi_libraries(shared ktx shaders qml gpu gl ${PLATFORM_GL_BACKEND})
|
||||
target_link_libraries(${TARGET_NAME} android log m)
|
||||
target_opengl()
|
41
android/apps/framePlayer/build.gradle
Normal file
41
android/apps/framePlayer/build.gradle
Normal file
|
@ -0,0 +1,41 @@
|
|||
import com.android.builder.core.BuilderConstants
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
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
|
||||
}
|
||||
|
||||
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
|
||||
|
||||
variantFilter { variant ->
|
||||
def build = variant.buildType.name
|
||||
if (build == BuilderConstants.RELEASE) {
|
||||
variant.setIgnore(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
|
||||
implementation project(':qt')
|
||||
}
|
38
android/apps/framePlayer/src/main/AndroidManifest.xml
Normal file
38
android/apps/framePlayer/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?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-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<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="org.qtproject.qt5.android.bindings.QtActivity"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="landscape"
|
||||
android:excludeFromRecents="false"
|
||||
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"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
6
android/apps/framePlayer/src/main/cpp/FramePlayer.qrc
Normal file
6
android/apps/framePlayer/src/main/cpp/FramePlayer.qrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>qml/main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
91
android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp
Normal file
91
android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// 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 <QtCore/QFileInfo>
|
||||
#include <QtGui/QImageReader>
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
||||
#include <gpu/Frame.h>
|
||||
#include <gpu/FrameIO.h>
|
||||
|
||||
PlayerWindow::PlayerWindow() {
|
||||
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
create();
|
||||
showFullScreen();
|
||||
|
||||
// Make sure the window has been created by processing events
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
// Start the rendering thread
|
||||
_renderThread.initialize(this, &_surface);
|
||||
|
||||
// Start the UI
|
||||
_surface.resize(size());
|
||||
connect(&_surface, &hifi::qml::OffscreenSurface::rootContextCreated, this, [](QQmlContext* context){
|
||||
context->setContextProperty("FRAMES_FOLDER", "file:assets:/frames");
|
||||
});
|
||||
_surface.load("qrc:///qml/main.qml");
|
||||
|
||||
// Connect the UI handler
|
||||
QObject::connect(_surface.getRootItem(), SIGNAL(loadFile(QString)),
|
||||
this, SLOT(loadFile(QString))
|
||||
);
|
||||
|
||||
// Turn on UI input events
|
||||
installEventFilter(&_surface);
|
||||
}
|
||||
|
||||
PlayerWindow::~PlayerWindow() {
|
||||
}
|
||||
|
||||
// static const char* FRAME_FILE = "assets:/frames/20190110_1635.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 PlayerWindow::loadFile(QString filename) {
|
||||
QString realFilename = QUrl(filename).toLocalFile();
|
||||
if (QFileInfo(realFilename).exists()) {
|
||||
auto frame = gpu::readFrame(realFilename.toStdString(), _renderThread._externalTexture, &textureLoader);
|
||||
_surface.pause();
|
||||
_renderThread.submitFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerWindow::touchEvent(QTouchEvent* event) {
|
||||
// Super basic input handling when the 3D scene is active.... tap with two finders to return to the
|
||||
// QML UI
|
||||
static size_t touches = 0;
|
||||
switch (event->type()) {
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
touches = std::max<size_t>(touches, event->touchPoints().size());
|
||||
break;
|
||||
|
||||
case QEvent::TouchEnd:
|
||||
if (touches >= 2) {
|
||||
_renderThread.submitFrame(nullptr);
|
||||
_surface.resume();
|
||||
}
|
||||
touches = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
35
android/apps/framePlayer/src/main/cpp/PlayerWindow.h
Normal file
35
android/apps/framePlayer/src/main/cpp/PlayerWindow.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// 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 <qml/OffscreenSurface.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include "RenderThread.h"
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class PlayerWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlayerWindow();
|
||||
virtual ~PlayerWindow();
|
||||
|
||||
protected:
|
||||
void touchEvent(QTouchEvent *ev) override;
|
||||
|
||||
public slots:
|
||||
void loadFile(QString filename);
|
||||
|
||||
private:
|
||||
hifi::qml::OffscreenSurface _surface;
|
||||
QSettings _settings;
|
||||
RenderThread _renderThread;
|
||||
};
|
162
android/apps/framePlayer/src/main/cpp/RenderThread.cpp
Normal file
162
android/apps/framePlayer/src/main/cpp/RenderThread.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// 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 <QtGui/QWindow>
|
||||
|
||||
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::setup() {
|
||||
// Wait until the context has been moved to this thread
|
||||
{ std::unique_lock<std::mutex> lock(_frameLock); }
|
||||
|
||||
makeCurrent();
|
||||
// Disable vsync for profiling
|
||||
::gl::setSwapInterval(0);
|
||||
|
||||
glClearColor(1, 1, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
|
||||
// GPU library init
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
_backend = _gpuContext->getBackend();
|
||||
_gpuContext->beginFrame();
|
||||
_gpuContext->endFrame();
|
||||
makeCurrent();
|
||||
|
||||
|
||||
glGenFramebuffers(1, &_uiFbo);
|
||||
glGenTextures(1, &_externalTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, _externalTexture);
|
||||
static const glm::u8vec4 color{ 0 };
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);
|
||||
|
||||
glClearColor(0, 1, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
}
|
||||
|
||||
void RenderThread::initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen) {
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
setObjectName("RenderThread");
|
||||
Parent::initialize();
|
||||
|
||||
_offscreen = offscreen;
|
||||
_window = window;
|
||||
_glContext.setWindow(_window);
|
||||
_glContext.create();
|
||||
_glContext.makeCurrent();
|
||||
|
||||
hifi::qml::OffscreenSurface::setSharedContext(_glContext.qglContext());
|
||||
glClearColor(1, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
_glContext.doneCurrent();
|
||||
_glContext.moveToThread(_thread);
|
||||
_thread->setObjectName("RenderThread");
|
||||
}
|
||||
|
||||
void RenderThread::shutdown() {
|
||||
_activeFrame.reset();
|
||||
while (!_pendingFrames.empty()) {
|
||||
_gpuContext->consumeFrameUpdates(_pendingFrames.front());
|
||||
_pendingFrames.pop();
|
||||
}
|
||||
_gpuContext->shutdown();
|
||||
_gpuContext.reset();
|
||||
}
|
||||
|
||||
void RenderThread::renderFrame() {
|
||||
auto windowSize = _window->geometry().size();
|
||||
uvec2 readFboSize;
|
||||
uint32_t readFbo{ 0 };
|
||||
|
||||
if (_activeFrame) {
|
||||
const auto &frame = _activeFrame;
|
||||
_backend->recycle();
|
||||
_backend->syncCache();
|
||||
_gpuContext->enableStereo(frame->stereoState._enable);
|
||||
if (frame && !frame->batches.empty()) {
|
||||
_gpuContext->executeFrame(frame);
|
||||
}
|
||||
auto &glBackend = static_cast<gpu::gl::GLBackend&>(*_backend);
|
||||
readFbo = glBackend.getFramebufferID(frame->framebuffer);
|
||||
readFboSize = frame->framebuffer->getSize();
|
||||
CHECK_GL_ERROR();
|
||||
} else {
|
||||
hifi::qml::OffscreenSurface::TextureAndFence newTextureAndFence;
|
||||
if (_offscreen->fetchTexture(newTextureAndFence)) {
|
||||
if (_uiTexture != 0) {
|
||||
auto readFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_offscreen->getDiscardLambda()(_uiTexture, readFence);
|
||||
_uiTexture = 0;
|
||||
}
|
||||
|
||||
glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)newTextureAndFence.second);
|
||||
_uiTexture = newTextureAndFence.first;
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _uiFbo);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _uiTexture, 0);
|
||||
}
|
||||
|
||||
if (_uiTexture != 0) {
|
||||
readFbo = _uiFbo;
|
||||
readFboSize = { windowSize.width(), windowSize.height() };
|
||||
}
|
||||
}
|
||||
|
||||
if (readFbo) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
|
||||
glBlitFramebuffer(
|
||||
0, 0, readFboSize.x, readFboSize.y,
|
||||
0, 0, windowSize.width(), windowSize.height(),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
} else {
|
||||
glClearColor(1, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
_glContext.swapBuffers();
|
||||
}
|
||||
|
||||
void RenderThread::updateFrame() {
|
||||
std::queue<gpu::FramePointer> pendingFrames;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
pendingFrames.swap(_pendingFrames);
|
||||
}
|
||||
|
||||
while (!pendingFrames.empty()) {
|
||||
_activeFrame = pendingFrames.front();
|
||||
pendingFrames.pop();
|
||||
if (_activeFrame) {
|
||||
_gpuContext->consumeFrameUpdates(_activeFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderThread::process() {
|
||||
updateFrame();
|
||||
makeCurrent();
|
||||
renderFrame();
|
||||
return true;
|
||||
}
|
54
android/apps/framePlayer/src/main/cpp/RenderThread.h
Normal file
54
android/apps/framePlayer/src/main/cpp/RenderThread.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// 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 <GenericThread.h>
|
||||
|
||||
#include <gl/Context.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
#include <qml/OffscreenSurface.h>
|
||||
|
||||
class RenderThread : public GenericThread {
|
||||
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;
|
||||
hifi::qml::OffscreenSurface* _offscreen{ nullptr };
|
||||
|
||||
gl::Context _glContext;
|
||||
uint32_t _uiTexture{ 0 };
|
||||
uint32_t _uiFbo{ 0 };
|
||||
|
||||
void move(const glm::vec3& v);
|
||||
void setup() override;
|
||||
bool process() override;
|
||||
void shutdown() override;
|
||||
|
||||
void initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen);
|
||||
|
||||
void submitFrame(const gpu::FramePointer& frame);
|
||||
void updateFrame();
|
||||
void renderFrame();
|
||||
|
||||
bool makeCurrent() {
|
||||
return _glContext.makeCurrent();
|
||||
}
|
||||
|
||||
void doneCurrent() {
|
||||
_glContext.doneCurrent();
|
||||
}
|
||||
};
|
54
android/apps/framePlayer/src/main/cpp/main.cpp
Normal file
54
android/apps/framePlayer/src/main/cpp/main.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// 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;
|
||||
app.exec();
|
||||
qInstallMessageHandler(oldMessageHandler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
36
android/apps/framePlayer/src/main/cpp/qml/main.qml
Normal file
36
android/apps/framePlayer/src/main/cpp/qml/main.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Dialogs 1.1
|
||||
import Qt.labs.folderlistmodel 2.11
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
|
||||
FolderListModel {
|
||||
id: folderModel
|
||||
folder: FRAMES_FOLDER
|
||||
nameFilters: ["*.json"]
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDelegate
|
||||
Text {
|
||||
text: fileName
|
||||
font.pointSize: 36
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.loadFile(folderModel.folder + "/" + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: folderModel
|
||||
delegate: fileDelegate
|
||||
}
|
||||
|
||||
signal loadFile(string filename);
|
||||
}
|
3
android/apps/framePlayer/src/main/res/values/strings.xml
Normal file
3
android/apps/framePlayer/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">GPU Frame Player</string>
|
||||
</resources>
|
|
@ -4,10 +4,10 @@ link_hifi_libraries(shared task networking gl gpu qml image fbx hfm render-utils
|
|||
target_opengl()
|
||||
target_bullet()
|
||||
|
||||
set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../interface")
|
||||
set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../interface")
|
||||
add_subdirectory("${INTERFACE_DIR}" "libraries/interface")
|
||||
include_directories("${INTERFACE_DIR}/src")
|
||||
set(HIFI_CODEC_PLUGIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../plugins/hifiCodec")
|
||||
set(HIFI_CODEC_PLUGIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/hifiCodec")
|
||||
add_subdirectory("${HIFI_CODEC_PLUGIN_DIR}" "libraries/hifiCodecPlugin")
|
||||
|
||||
target_link_libraries(native-lib android log m interface)
|
||||
|
@ -15,16 +15,3 @@ target_link_libraries(native-lib android log m interface)
|
|||
set(GVR_ROOT "${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/")
|
||||
target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers" "libraries/ui/src")
|
||||
target_link_libraries(native-lib "${GVR_ROOT}/libraries/libgvr.so" ui)
|
||||
|
||||
# finished libraries
|
||||
# core -> qt
|
||||
# networking -> openssl, tbb
|
||||
# fbx -> draco
|
||||
# physics -> bullet
|
||||
# entities-renderer -> polyvox
|
||||
|
||||
# unfinished libraries
|
||||
# image -> nvtt (doesn't look good, but can be made optional)
|
||||
# script-engine -> quazip (probably not required for the android client)
|
||||
|
||||
|
|
@ -1,5 +1,37 @@
|
|||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
|
@ -19,17 +51,17 @@ android {
|
|||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DHIFI_ANDROID=1',
|
||||
'-DHIFI_ANDROID_APP=interface',
|
||||
'-DANDROID_PLATFORM=android-24',
|
||||
'-DANDROID_TOOLCHAIN=clang',
|
||||
'-DANDROID_STL=c++_shared',
|
||||
'-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake',
|
||||
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
|
||||
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
|
||||
'-DRELEASE_TYPE=' + RELEASE_TYPE,
|
||||
'-DSTABLE_BUILD=' + STABLE_BUILD,
|
||||
'-DDISABLE_QML=OFF',
|
||||
'-DDISABLE_KTX_CACHE=OFF',
|
||||
'-DUSE_BREAKPAD=' + (System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_TOKEN") ? 'ON' : 'OFF');
|
||||
targets = ['native-lib']
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
|
@ -72,7 +104,7 @@ android {
|
|||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path '../../CMakeLists.txt'
|
||||
path '../../../CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,6 +114,7 @@ android {
|
|||
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()
|
||||
|
@ -97,7 +130,7 @@ android {
|
|||
|
||||
// Copy the compiled resources generated by the external native build
|
||||
copy {
|
||||
from new File(projectDir, "../../interface/compiledResources")
|
||||
from new File(projectDir, "../../../interface/compiledResources")
|
||||
into outputDir
|
||||
duplicatesStrategy DuplicatesStrategy.INCLUDE
|
||||
eachFile { details ->
|
||||
|
@ -108,7 +141,7 @@ android {
|
|||
|
||||
// Copy the scripts directory
|
||||
copy {
|
||||
from new File(projectDir, "../../scripts")
|
||||
from new File(projectDir, "../../../scripts")
|
||||
into new File(outputDir, "scripts")
|
||||
duplicatesStrategy DuplicatesStrategy.INCLUDE
|
||||
eachFile { details->
|
||||
|
@ -123,12 +156,6 @@ android {
|
|||
assetList.each { file -> out.println(file) }
|
||||
}
|
||||
}
|
||||
|
||||
variant.outputs.all {
|
||||
if (RELEASE_NUMBER != '0') {
|
||||
outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,23 +166,24 @@ dependencies {
|
|||
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation 'com.android.support:design:26.1.0'
|
||||
compile 'com.android.support:support-v4:26.1.0'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:support-vector-drawable:26.1.0'
|
||||
api 'com.android.support:support-v4:26.1.0'
|
||||
api 'com.android.support:appcompat-v7:26.1.0'
|
||||
api 'com.android.support:support-vector-drawable:26.1.0'
|
||||
|
||||
implementation 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:recyclerview-v7:26.1.0'
|
||||
compile 'com.android.support:cardview-v7:26.1.0'
|
||||
api 'com.android.support:recyclerview-v7:26.1.0'
|
||||
api 'com.android.support:cardview-v7:26.1.0'
|
||||
|
||||
compile 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
api 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
api 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
compile 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
api 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
api 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
compile 'com.sothree.slidinguppanel:library:3.4.0'
|
||||
api 'com.sothree.slidinguppanel:library:3.4.0'
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
|
||||
implementation project(':qt')
|
||||
}
|
25
android/apps/interface/proguard-rules.pro
vendored
Normal file
25
android/apps/interface/proguard-rules.pro
vendored
Normal 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
|
|
@ -1,7 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.hifiinterface">
|
||||
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
|
||||
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
44
android/apps/interface/src/main/assets/avatars.json
Normal file
44
android/apps/interface/src/main/assets/avatars.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"avatars": [
|
||||
{
|
||||
"name": "Wooden Mannequin",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/7fe80a1e-f445-4800-9e89-40e677b03bee/large/hifi-mp-7fe80a1e-f445-4800-9e89-40e677b03bee.jpg",
|
||||
"url": "qrc:////meshes/defaultAvatar_full.fst"
|
||||
},
|
||||
{
|
||||
"name": "Anime-Styled Boy",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/46e0fd52-3cff-462f-ba97-927338d88295/thumbnail/hifi-mp-46e0fd52-3cff-462f-ba97-927338d88295.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst"
|
||||
},
|
||||
{
|
||||
"name": "Anime-Styled Girl",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/1e7e43f6-1757-44d3-baa4-756827d96311/large/hifi-mp-1e7e43f6-1757-44d3-baa4-756827d96311.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst"
|
||||
},
|
||||
{
|
||||
"name": "Last Legends: Male Avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/28569047-6f1a-4100-af67-8054ec397cc3/thumbnail/hifi-mp-28569047-6f1a-4100-af67-8054ec397cc3.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst"
|
||||
},
|
||||
{
|
||||
"name": "Last Legends: Female Avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/8d823be5-6197-4418-b984-eb94160ed956/thumbnail/hifi-mp-8d823be5-6197-4418-b984-eb94160ed956.jpg",
|
||||
"url": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/46e0fd52-3cff-462f-ba97-927338d88295/thumbnail/hifi-mp-46e0fd52-3cff-462f-ba97-927338d88295.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Matthew: Photo-real avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/b652081b-a199-425e-ae5c-7815721bdc09/thumbnail/hifi-mp-b652081b-a199-425e-ae5c-7815721bdc09.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst"
|
||||
},
|
||||
{
|
||||
"name": "Priscilla: Photo real avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339/thumbnail/hifi-mp-e7565f93-8bc5-47c2-b6eb-b3b31d4a1339.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst"
|
||||
},
|
||||
{
|
||||
"name": "H1-F1 Optical Interpreter bot",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/469c8b66-e3c2-47fb-9820-e306b1dd15c4/large/hifi-mp-469c8b66-e3c2-47fb-9820-e306b1dd15c4.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/469c8b66-e3c2-47fb-9820-e306b1dd15c4-v1/optical_interpreter[1].fst"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -149,7 +149,7 @@ void unpackAndroidAssets() {
|
|||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject instance, jobject asset_mgr) {
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject instance, jobject asset_mgr) {
|
||||
g_assetManager = AAssetManager_fromJava(env, asset_mgr);
|
||||
qRegisterMetaType<QAndroidJniObject>("QAndroidJniObject");
|
||||
__interfaceActivity = QAndroidJniObject(instance);
|
||||
|
@ -493,6 +493,34 @@ Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(J
|
|||
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_getDisplayName(JNIEnv *env,
|
||||
jobject instance) {
|
||||
|
||||
QString displayName = AndroidHelper::instance().getDisplayName();
|
||||
return env->NewStringUTF(displayName.toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_setDisplayName(JNIEnv *env,
|
||||
jobject instance,
|
||||
jstring name_) {
|
||||
const char *c_name = env->GetStringUTFChars(name_, 0);
|
||||
const QString name = QString::fromUtf8(c_name);
|
||||
env->ReleaseStringUTFChars(name_, c_name);
|
||||
AndroidHelper::instance().setDisplayName(name);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_setAvatarUrl(JNIEnv *env,
|
||||
jobject instance,
|
||||
jstring url_) {
|
||||
const char *url = env->GetStringUTFChars(url_, 0);
|
||||
QString avatarUrl = QString::fromUtf8(url);
|
||||
AndroidHelper::instance().setMyAvatarUrl(avatarUrl);
|
||||
env->ReleaseStringUTFChars(url_, url);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_MainActivity_logout(JNIEnv *env, jobject instance) {
|
||||
DependencyManager::get<AccountManager>()->logout();
|
|
@ -24,6 +24,7 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -31,6 +32,7 @@ import android.view.WindowManager;
|
|||
import android.widget.FrameLayout;
|
||||
import android.widget.SlidingDrawer;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
import org.qtproject.qt5.android.QtLayout;
|
||||
import org.qtproject.qt5.android.QtSurface;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
|
@ -53,6 +55,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
public static final String DOMAIN_URL = "url";
|
||||
public static final String EXTRA_GOTO_USERNAME = "gotousername";
|
||||
private static final String TAG = "Interface";
|
||||
public static final String EXTRA_ARGS = "args";
|
||||
private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
|
||||
private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
|
||||
private static final int NORMAL_DPI = 160;
|
||||
|
@ -61,7 +64,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
private HeadsetStateReceiver headsetStateReceiver;
|
||||
|
||||
//public static native void handleHifiURL(String hifiURLString);
|
||||
private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager);
|
||||
private native void nativeOnCreate(AssetManager assetManager);
|
||||
private native void nativeOnDestroy();
|
||||
private native void nativeGotoUrl(String url);
|
||||
private native void nativeGoToUser(String username);
|
||||
|
@ -77,6 +80,8 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
|
||||
private boolean nativeEnterBackgroundCallEnqueued = false;
|
||||
private SlidingDrawer mWebSlidingDrawer;
|
||||
private boolean mStartInDomain;
|
||||
private boolean isLoading;
|
||||
// private GvrApi gvrApi;
|
||||
// Opaque native pointer to the Application C++ object.
|
||||
// This object is owned by the InterfaceActivity instance and passed to the native methods.
|
||||
|
@ -90,10 +95,16 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.isLoading = true;
|
||||
isLoading = true;
|
||||
Intent intent = getIntent();
|
||||
if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) {
|
||||
if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) {
|
||||
intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL));
|
||||
} else if (intent.hasExtra(EXTRA_ARGS)) {
|
||||
String args = intent.getStringExtra(EXTRA_ARGS);
|
||||
if (!TextUtils.isEmpty(args)) {
|
||||
mStartInDomain = true;
|
||||
intent.putExtra("applicationArguments", args);
|
||||
}
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
@ -114,7 +125,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
assetManager = getResources().getAssets();
|
||||
|
||||
//nativeGvrApi =
|
||||
nativeOnCreate(this, assetManager /*, gvrApi.getNativeGvrContext()*/);
|
||||
nativeOnCreate(assetManager /*, gvrApi.getNativeGvrContext()*/);
|
||||
|
||||
final View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
|
||||
|
@ -124,7 +135,10 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
getActionBar().hide();
|
||||
}
|
||||
});
|
||||
startActivity(new Intent(this, SplashActivity.class));
|
||||
Intent splashIntent = new Intent(this, SplashActivity.class);
|
||||
splashIntent.putExtra(SplashActivity.EXTRA_START_IN_DOMAIN, mStartInDomain);
|
||||
startActivity(splashIntent);
|
||||
|
||||
mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
|
||||
headsetStateReceiver = new HeadsetStateReceiver();
|
||||
}
|
||||
|
@ -132,7 +146,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (super.isLoading) {
|
||||
if (isLoading) {
|
||||
nativeEnterBackgroundCallEnqueued = true;
|
||||
} else {
|
||||
nativeEnterBackground();
|
||||
|
@ -159,15 +173,33 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
super.onResume();
|
||||
nativeEnterForeground();
|
||||
surfacesWorkaround();
|
||||
keepInterfaceRunning = false;
|
||||
registerReceiver(headsetStateReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
|
||||
//gvrApi.resumeTracking();
|
||||
}
|
||||
|
||||
@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
|
||||
|
@ -350,7 +382,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
}
|
||||
|
||||
public void onAppLoadedComplete() {
|
||||
super.isLoading = false;
|
||||
isLoading = false;
|
||||
if (nativeEnterBackgroundCallEnqueued) {
|
||||
nativeEnterBackground();
|
||||
}
|
||||
|
@ -381,7 +413,6 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
|
||||
@Override
|
||||
public void onExpand() {
|
||||
keepInterfaceRunning = true;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -33,13 +33,15 @@ import com.squareup.picasso.Picasso;
|
|||
import io.highfidelity.hifiinterface.fragment.FriendsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.HomeFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.PolicyFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.ProfileFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.SettingsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.SignupFragment;
|
||||
import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
|
||||
HomeFragment.OnHomeInteractionListener,
|
||||
FriendsFragment.OnHomeInteractionListener {
|
||||
FriendsFragment.OnHomeInteractionListener,
|
||||
ProfileFragment.OnProfileInteractionListener {
|
||||
|
||||
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar;
|
||||
public static final String DEFAULT_FRAGMENT = "Home";
|
||||
|
@ -61,6 +63,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
private View mProfilePanel;
|
||||
private TextView mLogoutOption;
|
||||
private MenuItem mPeopleMenuItem;
|
||||
private MenuItem mProfileMenuItem;
|
||||
|
||||
private boolean backToScene;
|
||||
private String backToUrl;
|
||||
|
@ -83,6 +86,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
|
||||
mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people);
|
||||
|
||||
mProfileMenuItem = mNavigationView.getMenu().findItem(R.id.action_profile);
|
||||
|
||||
updateDebugMenu(mNavigationView.getMenu());
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
|
@ -162,6 +167,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true, true);
|
||||
}
|
||||
|
||||
private void loadProfileFragment() {
|
||||
Fragment fragment = ProfileFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.profile), getString(R.string.tagFragmentProfile), true, true);
|
||||
}
|
||||
|
||||
private void loadSettingsFragment() {
|
||||
SettingsFragment fragment = SettingsFragment.newInstance();
|
||||
|
||||
|
@ -261,6 +272,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
case R.id.action_people:
|
||||
loadPeopleFragment();
|
||||
return true;
|
||||
case R.id.action_profile:
|
||||
loadProfileFragment();
|
||||
break;
|
||||
case R.id.action_debug_settings:
|
||||
loadSettingsFragment();
|
||||
return true;
|
||||
|
@ -351,6 +365,21 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
goToUser(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelProfileEdit() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleteProfileEdit() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvatarChosen() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
@Override
|
||||
public void onSuccess() {
|
|
@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -27,9 +28,14 @@ public class PermissionChecker extends Activity {
|
|||
private static final boolean CHOOSE_AVATAR_ON_STARTUP = false;
|
||||
private static final String TAG = "Interface";
|
||||
|
||||
private static final String EXTRA_ARGS = "args";
|
||||
private String mArgs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mArgs =(getIntent().getStringExtra(EXTRA_ARGS));
|
||||
|
||||
Intent myIntent = new Intent(this, BreakpadUploaderService.class);
|
||||
startService(myIntent);
|
||||
if (CHOOSE_AVATAR_ON_STARTUP) {
|
||||
|
@ -76,6 +82,11 @@ public class PermissionChecker extends Activity {
|
|||
|
||||
private void launchActivityWithPermissions(){
|
||||
Intent i = new Intent(this, InterfaceActivity.class);
|
||||
|
||||
if (!TextUtils.isEmpty(mArgs)) {
|
||||
i.putExtra(EXTRA_ARGS, mArgs);
|
||||
}
|
||||
|
||||
startActivity(i);
|
||||
finish();
|
||||
}
|
|
@ -7,6 +7,9 @@ import android.view.View;
|
|||
|
||||
public class SplashActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_START_IN_DOMAIN = "start-in-domain";
|
||||
private boolean mStartInDomain;
|
||||
|
||||
private native void registerLoadCompleteListener();
|
||||
|
||||
@Override
|
||||
|
@ -36,13 +39,27 @@ public class SplashActivity extends Activity {
|
|||
}
|
||||
|
||||
public void onAppLoadedComplete() {
|
||||
if (HifiUtils.getInstance().isUserLoggedIn()) {
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
} else {
|
||||
Intent menuIntent = new Intent(this, LoginMenuActivity.class);
|
||||
menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
|
||||
startActivity(menuIntent);
|
||||
if (!mStartInDomain) {
|
||||
if (HifiUtils.getInstance().isUserLoggedIn()) {
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
} else {
|
||||
Intent menuIntent = new Intent(this, LoginMenuActivity.class);
|
||||
menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
|
||||
startActivity(menuIntent);
|
||||
}
|
||||
}
|
||||
SplashActivity.this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(EXTRA_START_IN_DOMAIN, mStartInDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
mStartInDomain = savedInstanceState.getBoolean(EXTRA_START_IN_DOMAIN, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package io.highfidelity.hifiinterface.fragment;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.AvatarProvider;
|
||||
import io.highfidelity.hifiinterface.view.AvatarAdapter;
|
||||
|
||||
public class ProfileFragment extends Fragment {
|
||||
|
||||
private TextView mDisplayName;
|
||||
|
||||
private Button mOkButton;
|
||||
private OnProfileInteractionListener mListener;
|
||||
private AvatarProvider mAvatarsProvider;
|
||||
|
||||
private native String getDisplayName();
|
||||
private native void setDisplayName(String name);
|
||||
private native void setAvatarUrl(String url);
|
||||
|
||||
public ProfileFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
public static ProfileFragment newInstance() {
|
||||
ProfileFragment fragment = new ProfileFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_profile, container, false);
|
||||
|
||||
mDisplayName = rootView.findViewById(R.id.displayName);
|
||||
mDisplayName.setText(getDisplayName());
|
||||
mDisplayName.setOnEditorActionListener((textView, actionId, keyEvent) -> onDisplayNameEditorAction(textView, actionId, keyEvent));
|
||||
|
||||
mOkButton = rootView.findViewById(R.id.okButton);
|
||||
mOkButton.setOnClickListener(view -> onOkButtonClicked());
|
||||
|
||||
rootView.findViewById(R.id.cancel).setOnClickListener(view -> onCancelProfileEdit());
|
||||
|
||||
RecyclerView avatarsView = rootView.findViewById(R.id.gridview);
|
||||
int numberOfColumns = 1;
|
||||
mAvatarsProvider = new AvatarProvider(getContext());
|
||||
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
|
||||
avatarsView.setLayoutManager(gridLayoutMgr);
|
||||
AvatarAdapter avatarAdapter = new AvatarAdapter(getContext(), mAvatarsProvider);
|
||||
avatarsView.setAdapter(avatarAdapter);
|
||||
avatarAdapter.loadAvatars();
|
||||
|
||||
avatarAdapter.setClickListener((view, position, avatar) -> {
|
||||
setAvatarUrl(avatar.avatarUrl);
|
||||
if (mListener != null) {
|
||||
mListener.onAvatarChosen();
|
||||
}
|
||||
});
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void onOkButtonClicked() {
|
||||
setDisplayName(mDisplayName.getText().toString());
|
||||
View view = getActivity().getCurrentFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
if (mListener != null) {
|
||||
mListener.onCompleteProfileEdit();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onDisplayNameEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
mOkButton.performClick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onCancelProfileEdit() {
|
||||
View view = getActivity().getCurrentFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
if (mListener != null) {
|
||||
mListener.onCancelProfileEdit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the back pressed event and returns true if it was managed by this Fragment
|
||||
* @return
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnProfileInteractionListener) {
|
||||
mListener = (OnProfileInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnProfileInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnProfileInteractionListener {
|
||||
void onCancelProfileEdit();
|
||||
void onCompleteProfileEdit();
|
||||
void onAvatarChosen();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.AvatarAdapter;
|
||||
|
||||
/**
|
||||
* Created by gcalero on 1/21/19
|
||||
*/
|
||||
public class AvatarProvider {
|
||||
|
||||
private static final String AVATARS_JSON = "avatars.json";
|
||||
private static final String JSON_FIELD_NAME = "name";
|
||||
private static final String JSON_FIELD_URL = "url";
|
||||
private static final String JSON_FIELD_IMAGE = "preview_image";
|
||||
private static final String JSON_FIELD_AVATARS_ARRAY = "avatars";
|
||||
private final Context mContext;
|
||||
|
||||
public interface AvatarsCallback {
|
||||
void retrieveOk(List<AvatarAdapter.Avatar> avatars);
|
||||
void retrieveError(Exception e, String message);
|
||||
}
|
||||
|
||||
public AvatarProvider(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void retrieve(AvatarsCallback avatarsCallback)
|
||||
{
|
||||
try {
|
||||
JSONObject obj = new JSONObject(loadJSONFromAssets());
|
||||
JSONArray m_jArry = obj.getJSONArray(JSON_FIELD_AVATARS_ARRAY);
|
||||
ArrayList<AvatarAdapter.Avatar> avatars = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < m_jArry.length(); i++) {
|
||||
JSONObject jo_inside = m_jArry.getJSONObject(i);
|
||||
AvatarAdapter.Avatar anAvatar = new AvatarAdapter.Avatar();
|
||||
anAvatar.avatarName = jo_inside.getString(JSON_FIELD_NAME);
|
||||
anAvatar.avatarPreviewUrl = jo_inside.getString(JSON_FIELD_IMAGE);
|
||||
anAvatar.avatarUrl = jo_inside.getString(JSON_FIELD_URL);
|
||||
avatars.add(anAvatar);
|
||||
}
|
||||
avatarsCallback.retrieveOk(avatars);
|
||||
} catch (IOException e) {
|
||||
avatarsCallback.retrieveError(e, "Failed retrieving avatar JSON");
|
||||
} catch (JSONException e) {
|
||||
avatarsCallback.retrieveError(e, "Failed parsing avatar JSON");
|
||||
}
|
||||
}
|
||||
|
||||
private String loadJSONFromAssets() throws IOException {
|
||||
String json = null;
|
||||
InputStream is = mContext.getAssets().open(AVATARS_JSON);
|
||||
int size = is.available();
|
||||
byte[] buffer = new byte[size];
|
||||
is.read(buffer);
|
||||
is.close();
|
||||
json = new String(buffer, "UTF-8");
|
||||
return json;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package io.highfidelity.hifiinterface.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.AvatarProvider;
|
||||
|
||||
/**
|
||||
* Created by gcalero on 1/21/19
|
||||
*/
|
||||
public class AvatarAdapter extends RecyclerView.Adapter<AvatarAdapter.ViewHolder> {
|
||||
|
||||
private static final String TAG = "Interface";
|
||||
private final Context mContext;
|
||||
private final LayoutInflater mInflater;
|
||||
private final AvatarProvider mProvider;
|
||||
private List<Avatar> mAvatars = new ArrayList<>();
|
||||
private ItemClickListener mClickListener;
|
||||
|
||||
public AvatarAdapter(Context context, AvatarProvider provider) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = mInflater.inflate(R.layout.avatar_item, parent, false);
|
||||
return new AvatarAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
AvatarAdapter.Avatar anAvatar = mAvatars.get(position);
|
||||
assert(holder.mName != null);
|
||||
holder.mName.setText(anAvatar.avatarName);
|
||||
Uri uri = Uri.parse(anAvatar.avatarPreviewUrl);
|
||||
Picasso.get().load(uri).into(holder.mPreviewImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mAvatars.size();
|
||||
}
|
||||
|
||||
public void loadAvatars() {
|
||||
mProvider.retrieve(new AvatarProvider.AvatarsCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<AvatarAdapter.Avatar> avatars) {
|
||||
mAvatars = new ArrayList<>(avatars);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveError(Exception e, String message) {
|
||||
Log.e(TAG, message, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setClickListener(ItemClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position, Avatar avatar);
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
TextView mName;
|
||||
ImageView mPreviewImage;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mName = itemView.findViewById(R.id.avatarName);
|
||||
assert (mName != null);
|
||||
mPreviewImage = itemView.findViewById(R.id.avatarPreview);
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int position= getAdapterPosition();
|
||||
if (mClickListener != null) {
|
||||
mClickListener.onItemClick(view, position, mAvatars.get(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Avatar {
|
||||
public String avatarName;
|
||||
public String avatarUrl;
|
||||
public String avatarPreviewUrl;
|
||||
|
||||
public Avatar() { }
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
17
android/apps/interface/src/main/res/drawable/ic_launcher.xml
Normal file
17
android/apps/interface/src/main/res/drawable/ic_launcher.xml
Normal 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>
|
23
android/apps/interface/src/main/res/layout/avatar_item.xml
Normal file
23
android/apps/interface/src/main/res/layout/avatar_item.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp">
|
||||
<TextView
|
||||
android:id="@+id/avatarName"
|
||||
android:fontFamily="@font/raleway_bold"
|
||||
android:textSize="20sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/colorButton1" />
|
||||
<ImageView
|
||||
android:id="@+id/avatarPreview"
|
||||
android:background="@color/backgroundDark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="190dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatarName"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue