From bc32a923f179875f3b18ea6ad87fbd778f255bb9 Mon Sep 17 00:00:00 2001 From: Miguel Maurizio Date: Mon, 3 Aug 2020 17:54:32 +0100 Subject: [PATCH] Remove Multiplayer from sample games --- README.md | 4 - samples-android/ButtonClicker/CMakeLists.txt | 89 --- samples-android/ButtonClicker/README.md | 17 - samples-android/ButtonClicker/build.gradle | 56 -- .../ButtonClicker/proguard-project.txt | 20 - .../src/main/AndroidManifest.xml | 57 -- .../src/main/assets/Shaders/ShaderPlain.fsh | 42 - .../main/assets/Shaders/VS_ShaderPlain.vsh | 64 -- .../main/cpp/ButtonClickerNativeActivity.cpp | 746 ------------------ .../main/cpp/ButtonClickerNativeActivity.h | 189 ----- .../ButtonClickerNativeActivity_Engine.cpp | 343 -------- .../ButtonClickerApplication.java | 25 - .../ButtonClickerNativeActivity.java | 143 ---- .../main/res/drawable-hdpi/ic_launcher.png | Bin 8203 -> 0 bytes .../main/res/drawable-ldpi/ic_launcher.png | Bin 2556 -> 0 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 4064 -> 0 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 13541 -> 0 bytes .../ButtonClicker/src/main/res/values/ids.xml | 9 - .../src/main/res/values/strings.xml | 5 - .../src/main/res/values/styles.xml | 20 - .../TbmpSkeletonNative/CMakeLists.txt | 84 -- .../TbmpSkeletonNative/build.gradle | 57 -- .../TbmpSkeletonNative/proguard-project.txt | 20 - .../src/main/AndroidManifest.xml | 59 -- .../src/main/assets/Shaders/ShaderPlain.fsh | 42 - .../main/assets/Shaders/VS_ShaderPlain.vsh | 64 -- .../main/cpp/TBMPSkeletonNativeActivity.cpp | 746 ------------------ .../src/main/cpp/TBMPSkeletonNativeActivity.h | 163 ---- .../cpp/TBMPSkeletonNativeActivity_Engine.cpp | 341 -------- .../tbmpskel/TBMPSkeletonApplication.java | 25 - .../tbmpskel/TBMPSkeletonNativeActivity.java | 144 ---- .../main/res/drawable-hdpi/ic_launcher.png | Bin 8203 -> 0 bytes .../main/res/drawable-ldpi/ic_launcher.png | Bin 2556 -> 0 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 4064 -> 0 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 13541 -> 0 bytes .../src/main/res/layout/widgets.xml | 8 - .../src/main/res/values/ids.xml | 9 - .../src/main/res/values/strings.xml | 5 - .../src/main/res/values/styles.xml | 20 - samples-android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- samples-android/settings.gradle | 2 - 42 files changed, 3 insertions(+), 3621 deletions(-) delete mode 100644 samples-android/ButtonClicker/CMakeLists.txt delete mode 100644 samples-android/ButtonClicker/README.md delete mode 100644 samples-android/ButtonClicker/build.gradle delete mode 100644 samples-android/ButtonClicker/proguard-project.txt delete mode 100644 samples-android/ButtonClicker/src/main/AndroidManifest.xml delete mode 100644 samples-android/ButtonClicker/src/main/assets/Shaders/ShaderPlain.fsh delete mode 100644 samples-android/ButtonClicker/src/main/assets/Shaders/VS_ShaderPlain.vsh delete mode 100644 samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.cpp delete mode 100644 samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.h delete mode 100644 samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity_Engine.cpp delete mode 100644 samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerApplication.java delete mode 100644 samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerNativeActivity.java delete mode 100644 samples-android/ButtonClicker/src/main/res/drawable-hdpi/ic_launcher.png delete mode 100644 samples-android/ButtonClicker/src/main/res/drawable-ldpi/ic_launcher.png delete mode 100644 samples-android/ButtonClicker/src/main/res/drawable-mdpi/ic_launcher.png delete mode 100644 samples-android/ButtonClicker/src/main/res/drawable-xhdpi/ic_launcher.png delete mode 100644 samples-android/ButtonClicker/src/main/res/values/ids.xml delete mode 100644 samples-android/ButtonClicker/src/main/res/values/strings.xml delete mode 100644 samples-android/ButtonClicker/src/main/res/values/styles.xml delete mode 100644 samples-android/TbmpSkeletonNative/CMakeLists.txt delete mode 100644 samples-android/TbmpSkeletonNative/build.gradle delete mode 100644 samples-android/TbmpSkeletonNative/proguard-project.txt delete mode 100644 samples-android/TbmpSkeletonNative/src/main/AndroidManifest.xml delete mode 100644 samples-android/TbmpSkeletonNative/src/main/assets/Shaders/ShaderPlain.fsh delete mode 100644 samples-android/TbmpSkeletonNative/src/main/assets/Shaders/VS_ShaderPlain.vsh delete mode 100644 samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.cpp delete mode 100644 samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.h delete mode 100644 samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity_Engine.cpp delete mode 100644 samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonApplication.java delete mode 100644 samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonNativeActivity.java delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/drawable-hdpi/ic_launcher.png delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/drawable-ldpi/ic_launcher.png delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/drawable-mdpi/ic_launcher.png delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/drawable-xhdpi/ic_launcher.png delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/layout/widgets.xml delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/values/ids.xml delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/values/strings.xml delete mode 100644 samples-android/TbmpSkeletonNative/src/main/res/values/styles.xml diff --git a/README.md b/README.md index 11cbff9..ad56f9e 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,6 @@ These samples illustrate how to use Google Play Game Services with your Android * **minimalist**: Demonstrates a minimal use case for the Google Play Game Services API. -* **TbmpSkeletonNative**: A trivial, turn-based multiplayer game. Many players can play together in this thrilling game, in which they send a shared gamestate string back and forth until someone finishes or cancels, or the second-to-last player leaves. Be the last one standing! - -* **Button-Clicker**: Demonstrates real-time multiplayer using invites or quickmatch - * **Teapots**: Demonstrates use of the Leaderboard and Achievement APIs. Pre-requisites diff --git a/samples-android/ButtonClicker/CMakeLists.txt b/samples-android/ButtonClicker/CMakeLists.txt deleted file mode 100644 index 1b66741..0000000 --- a/samples-android/ButtonClicker/CMakeLists.txt +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2017 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -## - -# For more information about using CMake with Android Studio, read the -# documentation: https://d.android.com/studio/projects/add-native-code.html - -# Sets the minimum version of CMake required to build the native library. - -cmake_minimum_required(VERSION 3.4.1) - -#include the native part of JUI Helper -add_subdirectory (${JUI_HELPER_PATH} - "${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libjuihelper") - -#include the native part of NDKHelper -add_subdirectory (${NDK_HELPER_PATH} - "${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libndkhelper") - -#include the GPG C++ SDK -add_library(gpg_sdk STATIC IMPORTED) -set_target_properties(gpg_sdk PROPERTIES IMPORTED_LOCATION - ${GPG_SDK_PATH}/lib/c++/${ANDROID_ABI}/libgpg.a) - -# build native_app_glue as a static lib -add_library(native_app_glue STATIC - ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) - -# build cpufeatures as a static lib -add_library(cpufeatures STATIC - ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c) - -# Export ANativeActivity_onCreate(), -# Refer to: https://github.com/android-ndk/ndk/issues/381. -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") - -# Creates and names a library, sets it as either STATIC -# or SHARED, and provides the relative paths to its source code. -# You can define multiple libraries, and CMake builds them for you. -# Gradle automatically packages shared libraries with your APK. - -add_library( # Sets the name of the library. - ButtonClickerNativeActivity - - # Sets the library as a shared library. - SHARED - - # Provides a relative path to your source file(s). - src/main/cpp/ButtonClickerNativeActivity.cpp - src/main/cpp/ButtonClickerNativeActivity_Engine.cpp - ${TEAPOT_RENDERER_PATH}/TeapotRenderer.cpp - ) - -target_include_directories(ButtonClickerNativeActivity PRIVATE - ${ANDROID_NDK}/sources/android/native_app_glue - ${ANDROID_NDK}/sources/android/cpufeatures - ${GPG_SDK_PATH}/include - ${juihelper_SOURCE_DIR}/src/main/cpp - ${ndkhelper_SOURCE_DIR}/src/main/cpp - ${TEAPOT_RENDERER_PATH} -) - -# Specifies libraries CMake should link to your target library. You -# can link multiple libraries, such as libraries you define in this -# build script, prebuilt third-party libraries, or system libraries. - -target_link_libraries(ButtonClickerNativeActivity - gpg_sdk - native_app_glue - cpufeatures - juihelper - ndkhelper - log - android - EGL - GLESv2 - z -) diff --git a/samples-android/ButtonClicker/README.md b/samples-android/ButtonClicker/README.md deleted file mode 100644 index 8d6e970..0000000 --- a/samples-android/ButtonClicker/README.md +++ /dev/null @@ -1,17 +0,0 @@ -Google Play game services - ButtonClickerNative C++ Samples -=========================================== -Copyright (C) 2014 Google Inc. - -

Contents

- -ButtonClickerNative: The sample demonstrates how to use real time multiplayer gaming feature with C++ code with Google Play game services native SDK. - -

How to run a sample

- -Please follow steps described in [Getting Started for C++](https://developers.google.com/games/services/cpp/GettingStartedNativeClient) - -

Support

- -First of all, take a look at our [troubleshooting guide](https://developers.google.com/games/services/android/troubleshooting). Most setup issues can be solved by following this guide. - -If your question is not answered by the troubleshooting guide, we encourage you to post your question to [stackoverflow.com](stackoverflow.com) with tag "google-play-games". Our team answers questions there reguarly. diff --git a/samples-android/ButtonClicker/build.gradle b/samples-android/ButtonClicker/build.gradle deleted file mode 100644 index 34e85c0..0000000 --- a/samples-android/ButtonClicker/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 (C) Google LLC - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin : 'com.android.application' - -android { - compileSdkVersion 27 - defaultConfig { - // - // REPLACE THE APPLICATION ID with your bundle ID - // - applicationId "com.google.example.games.ReplaceMe" - versionCode 1 - versionName "1.0" - - minSdkVersion 14 - targetSdkVersion 27 - - ndk.abiFilters 'x86', 'armeabi-v7a', 'arm64-v8a' - - externalNativeBuild { - cmake { - cppFlags "-std=c++11 -frtti -Wall -Werror" - arguments "-DJUI_HELPER_PATH=${project(':Common:JuiHelper').projectDir}", - "-DNDK_HELPER_PATH=${project(':Common:NDKHelper').projectDir}", - "-DGPG_SDK_PATH=${project(':Common:gpg-sdk').projectDir}/gpg-cpp-sdk/android", - "-DTEAPOT_RENDERER_PATH=${project(':Common').projectDir}/TeapotRenderer", - "-DANDROID_STL=c++_static", - "-DANDROID_TOOLCHAIN=clang" - } - } - } - - externalNativeBuild { - cmake.path "CMakeLists.txt" - } -} - -dependencies { - implementation project(":Common:JuiHelper") - implementation 'com.google.android.gms:play-services-games:11.8.0' - implementation 'com.google.android.gms:play-services-nearby:11.8.0' - implementation 'com.android.support:support-v4:27.0.2' -} diff --git a/samples-android/ButtonClicker/proguard-project.txt b/samples-android/ButtonClicker/proguard-project.txt deleted file mode 100644 index f2fe155..0000000 --- a/samples-android/ButtonClicker/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# 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 *; -#} diff --git a/samples-android/ButtonClicker/src/main/AndroidManifest.xml b/samples-android/ButtonClicker/src/main/AndroidManifest.xml deleted file mode 100644 index 63831da..0000000 --- a/samples-android/ButtonClicker/src/main/AndroidManifest.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples-android/ButtonClicker/src/main/assets/Shaders/ShaderPlain.fsh b/samples-android/ButtonClicker/src/main/assets/Shaders/ShaderPlain.fsh deleted file mode 100644 index 75ae290..0000000 --- a/samples-android/ButtonClicker/src/main/assets/Shaders/ShaderPlain.fsh +++ /dev/null @@ -1,42 +0,0 @@ - -// -// ShaderPlain.fsh -// - -#define USE_PHONG (1) - -uniform lowp vec3 vMaterialAmbient; -uniform mediump vec4 vMaterialSpecular; - -varying lowp vec4 colorDiffuse; - -#if USE_PHONG -uniform highp vec3 vLight0; -varying mediump vec3 position; -varying mediump vec3 normal; -#else -varying lowp vec4 colorSpecular; -#endif - -/* - Standard phong fragment shader implementation. - Take object space light position and normal, derive a specular intensity. -*/ -void main() { -#if USE_PHONG - mediump vec3 - halfVector = normalize(-vLight0 + position); - mediump - float NdotH = max(dot(normalize(normal), halfVector), 0.0); - mediump - float fPower = vMaterialSpecular.w; - mediump - float specular = pow(NdotH, fPower); - - lowp vec4 - colorSpecular = vec4(vMaterialSpecular.xyz * specular, 1); - gl_FragColor = colorDiffuse + colorSpecular; -#else - gl_FragColor = colorDiffuse + colorSpecular; -#endif -} \ No newline at end of file diff --git a/samples-android/ButtonClicker/src/main/assets/Shaders/VS_ShaderPlain.vsh b/samples-android/ButtonClicker/src/main/assets/Shaders/VS_ShaderPlain.vsh deleted file mode 100644 index a62e4a8..0000000 --- a/samples-android/ButtonClicker/src/main/assets/Shaders/VS_ShaderPlain.vsh +++ /dev/null @@ -1,64 +0,0 @@ -// -// ShaderPlain.vsh -// - -#define USE_PHONG (1) - -attribute highp vec3 myVertex; -attribute highp vec3 myNormal; -attribute mediump vec2 myUV; -attribute mediump vec4 myBone; - -varying mediump vec2 texCoord; -varying lowp vec4 colorDiffuse; - -#if USE_PHONG -varying mediump vec3 position; -varying mediump vec3 normal; -#else -varying lowp vec4 colorSpecular; -#endif - -uniform highp mat4 uMVMatrix; -uniform highp mat4 uPMatrix; - -uniform highp vec3 vLight0; - -uniform lowp vec4 vMaterialDiffuse; -uniform lowp vec3 vMaterialAmbient; -uniform lowp vec4 vMaterialSpecular; - -/* - Standard vertex shader shader implementation. - Transform vertices and normal, - derive diffuse value and pass object space light source - position to fragment shader for phong shading. -*/ -void main(void) { - highp vec4 - p = vec4(myVertex, 1); - gl_Position = uPMatrix * p; - - texCoord = myUV; - - highp vec3 - worldNormal = vec3( - mat3(uMVMatrix[0].xyz, uMVMatrix[1].xyz, uMVMatrix[2].xyz) * myNormal); - highp vec3 - ecPosition = p.xyz; - - colorDiffuse = dot(worldNormal, normalize(-vLight0 + ecPosition)) - * vMaterialDiffuse + vec4(vMaterialAmbient, 1); - -#if USE_PHONG - normal = worldNormal; - position = ecPosition; -#else - highp vec3 halfVector = normalize(ecPosition - vLight0); - - highp float NdotH = max(-dot(worldNormal, halfVector), 0.0); - float fPower = vMaterialSpecular.w; - highp float specular = min( pow(NdotH, fPower), 1.0); - colorSpecular = vec4(vMaterialSpecular.xyz * specular, 1); -#endif -} diff --git a/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.cpp b/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.cpp deleted file mode 100644 index 49ce929..0000000 --- a/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.cpp +++ /dev/null @@ -1,746 +0,0 @@ -// Copyright (c) 2014 Google. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -/* - * This file demonstrates, - * - How to use RTMP features in C++ code with gpg native client, including - * - Sign in to gpg service - * - How to handle RTMP callbacks - * - How to initiate RTMP matchs via several ways (QuickMatch, Invitation etc) - * - How to send reliable/unreliable packets to peers - * - Setup management UI and game UI - */ - -/* - * Include files - */ -#include "ButtonClickerNativeActivity.h" - -/* - * Initialize GooglePlayGameServices via gpg::GameServices::Builder - * In the build, it's setting several callbacks such as auth status changes, - * receiving invitations etc. - */ -void Engine::InitGooglePlayGameServices() { - if (service_ != nullptr) { - return; - } - - gpg::AndroidInitialization::android_main(app_); - - // Game Services have not been initialized, create a new Game Services. - gpg::AndroidPlatformConfiguration platform_configuration; - platform_configuration.SetActivity(app_->activity->clazz); - - gpg::GameServices::Builder builder; - service_ = - builder.SetOnAuthActionStarted([this](gpg::AuthOperation op) { - // This callback is invoked when auth - // action started - // While auth action is going on, disable - // auth and related UI - OnAuthActionStarted(op); - }) - .SetOnAuthActionFinished([this](gpg::AuthOperation op, - gpg::AuthStatus status) { - // This callback is invoked when auth action finished - // Check status code and update UI to signed-in state - OnAuthActionFinished(op, status); - }) - .SetOnMultiplayerInvitationEvent([this]( - gpg::MultiplayerEvent event, std::string match_id, - gpg::MultiplayerInvitation invitation) { - // Invoked when invitation has been received - // It can be received from the Play Game app, a notification, or - // live while the app is running. - // (e.g. Though PlayGam app, while the app is running) - LOGI("MultiplayerInvitationEvent callback"); - - if (event == - gpg::MultiplayerEvent::UPDATED_FROM_APP_LAUNCH) { - - // In this case, an invitation has been accepted already - // in notification or in Play game app - gpg::RealTimeMultiplayerManager::RealTimeRoomResponse result = - service_->RealTimeMultiplayer().AcceptInvitationBlocking( - invitation, this); - if (gpg::IsSuccess(result.status)) { - room_ = result.room; - service_->RealTimeMultiplayer().ShowWaitingRoomUI( - room_, MIN_PLAYERS, - [this](gpg::RealTimeMultiplayerManager:: - WaitingRoomUIResponse const &waitResult) { - EnableUI(true); - if (gpg::IsSuccess(waitResult.status)) { - PlayGame(); - } - }); - } else { - LeaveGame(); - } - } else { - // Otherwise, show default inbox and let players to accept an - // invitation - ShowRoomInbox(); - } - }) - .Create(platform_configuration); -} - -/* - * Callback: Authentication action started - * - * gpg::AuthOperation op : SIGN_IN = 1, SIGN_OUT = 2 - * - */ -void Engine::OnAuthActionStarted(gpg::AuthOperation op) { - if(!initialized_resources_) { - return; - } - - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, op]() { - EnableUI(false); - if (op == gpg::AuthOperation::SIGN_IN) { - status_text_->SetAttribute("Text", "Signing In..."); - } else { - status_text_->SetAttribute("Text", "Signing Out..."); - } - }); -} - -/* - * Callback: Authentication action finishes - * - * gpg::AuthOperation op : SIGN_IN = 1, SIGN_OUT = 2 - * - */ -void Engine::OnAuthActionFinished(gpg::AuthOperation op, - gpg::AuthStatus status) { - if (gpg::IsSuccess(status)) { - service_->Players().FetchSelf([this]( - gpg::PlayerManager::FetchSelfResponse const &response) { - if (gpg::IsSuccess(response.status)) { - self_id_ = response.data.Id(); - } - }); - } - - if(!initialized_resources_) { - return; - } - - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, status]() { - EnableUI(true); - button_sign_in_->SetAttribute( - "Text", gpg::IsSuccess(status) ? "Sign Out" : "Sign In"); - - status_text_->SetAttribute( - "Text", gpg::IsSuccess(status) ? "Signed In" : "Signed Out"); - }); -} - -/* - * Show room inbox - */ -void Engine::ShowRoomInbox() { - service_->RealTimeMultiplayer().ShowRoomInboxUI([this]( - gpg::RealTimeMultiplayerManager::RoomInboxUIResponse const &response) { - if (gpg::IsSuccess(response.status)) { - gpg::RealTimeMultiplayerManager::RealTimeRoomResponse result = - service_->RealTimeMultiplayer().AcceptInvitationBlocking( - response.invitation, this); - if (gpg::IsSuccess(result.status)) { - room_ = result.room; - service_->RealTimeMultiplayer().ShowWaitingRoomUI( - room_, MIN_PLAYERS, - [this]( - gpg::RealTimeMultiplayerManager::WaitingRoomUIResponse const & - waitResult) { - EnableUI(true); - if (gpg::IsSuccess(waitResult.status)) { - PlayGame(); - } - }); - } else - EnableUI(true); // Go back to original state for retry - } else { - LOGI("Invalid response status"); - EnableUI(true); // Go back to original state for retry - } - }); - EnableUI(false); -} - -/* - * Quick match - * - Create a match with minimal setting and play the game - */ -void Engine::QuickMatch() { - gpg::RealTimeRoomConfig config = - gpg::RealTimeRoomConfig::Builder() - .SetMinimumAutomatchingPlayers(MIN_PLAYERS) - .SetMaximumAutomatchingPlayers(MAX_PLAYERS) - .Create(); - - service_->RealTimeMultiplayer().CreateRealTimeRoom( - config, this, - [this](gpg::RealTimeMultiplayerManager::RealTimeRoomResponse const & - response) { - LOGI("created a room %d", response.status); - if (gpg::IsSuccess(response.status)) { - room_ = response.room; - service_->RealTimeMultiplayer().ShowWaitingRoomUI( - room_, MIN_PLAYERS, - [this]( - gpg::RealTimeMultiplayerManager::WaitingRoomUIResponse const & - waitResult) { - EnableUI(true); - if (gpg::IsSuccess(waitResult.status)) { - PlayGame(); - } - }); - } else - EnableUI(true); // Go back to original state for retry - }); - EnableUI(false); -} - -/* - * Invite friends - * - Show Player Select UI via ShowPlayerSelectUI, - * - When the UI is finished, create match and show game UI - */ -void Engine::InviteFriend() { - service_->RealTimeMultiplayer().ShowPlayerSelectUI( - MIN_PLAYERS, MAX_PLAYERS, true, - [this](gpg::RealTimeMultiplayerManager::PlayerSelectUIResponse const & - response) { - LOGI("inviting friends %d", response.status); - if (gpg::IsSuccess(response.status)) { - // Create room - gpg::RealTimeRoomConfig config = - gpg::RealTimeRoomConfig::Builder() - .PopulateFromPlayerSelectUIResponse(response) - .Create(); - - auto roomResponse = - service_->RealTimeMultiplayer().CreateRealTimeRoomBlocking(config, - this); - if (gpg::IsSuccess(roomResponse.status)) { - room_ = roomResponse.room; - service_->RealTimeMultiplayer().ShowWaitingRoomUI( - room_, MIN_PLAYERS, - [this](gpg::RealTimeMultiplayerManager:: - WaitingRoomUIResponse const &waitResult) { - EnableUI(true); - if (gpg::IsSuccess(waitResult.status)) { - PlayGame(); - } - }); - } else - EnableUI(true); // Go back to original state for retry - } else - EnableUI(true); // Go back to original state for retry - }); - EnableUI(false); -} - -/* - * Broadcast my score to peers - */ -void Engine::BroadcastScore(bool bFinal) { - std::vector v; - if (!bFinal) { - v.push_back('U'); - v.push_back(static_cast(score_counter_)); - service_->RealTimeMultiplayer().SendUnreliableMessageToOthers(room_, v); - } else { - v.push_back('F'); - v.push_back(static_cast(score_counter_)); - - const std::vector participants = - room_.Participants(); - for (gpg::MultiplayerParticipant participant : participants) { - service_->RealTimeMultiplayer().SendReliableMessage( - room_, participant, v, [](gpg::MultiplayerStatus const &) {}); - } - } -} - -/* - * Got message from peers - * room : The room which from_participant is in. - * from_participant : The participant who sent the data. - * data : The data which was received. - * is_reliable : Whether the data was sent using the unreliable or - * reliable mechanism. - * In this app, packet format is defined as: - * 1 byte: indicate score type 'F': final score 'U' updating score - * 1 byte: score - */ -void Engine::OnDataReceived(gpg::RealTimeRoom const &room, - gpg::MultiplayerParticipant const &from_participant, - std::vector data, bool is_reliable) { - if (data[0] == 'F' && is_reliable) { - // Got final score - players_score_[from_participant.Id()].score = data[1]; - players_score_[from_participant.Id()].finished = true; - LOGI("Got final data from name:%s ID:%s", - from_participant.DisplayName().c_str(), from_participant.Id().c_str()); - } else if (data[0] == 'U' && !is_reliable) { - // Got current score - uint8_t score = players_score_[from_participant.Id()].score; - players_score_[from_participant.Id()].score = std::max(score, data[1]); - LOGI("Got data from name:%s ID:%s", - from_participant.DisplayName().c_str(), from_participant.Id().c_str()); - } - UpdateScore(); -} - -/* - * Room status change callback - */ -void Engine::OnRoomStatusChanged(gpg::RealTimeRoom const &room) { - room_ = room; -} - -/* - * Invoked when participant status changed - */ -void Engine::OnParticipantStatusChanged( - gpg::RealTimeRoom const &room, - gpg::MultiplayerParticipant const &participant) { - - // Update participant status - LOGI("Participant %s status changed: %d", participant.Id().c_str(), - participant.Status()); - - if (participant.Status() != gpg::ParticipantStatus::JOINED) { - { - std::lock_guard lock(mutex_); - if (players_score_.find(participant.Id()) != players_score_.end()) { - players_score_[participant.Id()].finished = true; - } - } - UpdateScore(); - } -} - -/* - * Play games UI that is in your turn - */ -void Engine::PlayGame() { - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this]() { - LOGI("Playing match"); - if (dialog_) delete dialog_; - - // Start game - InitializeGame(); - - // - // Using jui_helper, a support library, to create and bind gameplay buttons. - // - dialog_ = new jui_helper::JUIDialog(app_->activity); - - // Setting up labels - time_text_ = new jui_helper::JUITextView("0:00"); - time_text_->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_TOP, - jui_helper::LAYOUT_PARAMETER_TRUE); - time_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - time_text_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, 18.f); - time_text_->SetAttribute("Padding", 10, 10, 10, 10); - - my_score_text_ = new jui_helper::JUITextView("000"); - my_score_text_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, time_text_); - my_score_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - my_score_text_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, - 24.f); - my_score_text_->SetAttribute("Padding", 10, 10, 10, 10); - - // Take Turn Button - button_play_ = new jui_helper::JUIButton("Click me!"); - button_play_->SetCallback([this](jui_helper::JUIView *view, - const int32_t message) { - switch (message) { - case jui_helper::JUICALLBACK_BUTTON_UP: { - if (!playing_) return; - score_counter_++; - UpdateScore(); - UpdateTime(); - - // Broadcast my score to others via unreliable protocol - BroadcastScore(false); - } - } - }); - - button_play_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button_play_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, my_score_text_); - - const int32_t labelWidth = 600; - const int32_t labelHeight = 300; - scores_text_ = new jui_helper::JUITextView("0:00"); - scores_text_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, button_play_); - scores_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - scores_text_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, 18.f); - scores_text_->SetAttribute("MinimumWidth", labelWidth); - scores_text_->SetAttribute("MinimumHeight", labelHeight); - scores_text_->SetAttribute("Padding", 10, 10, 10, 10); - - UpdateScore(); - - dialog_->AddView(my_score_text_); - dialog_->AddView(button_play_); - dialog_->AddView(time_text_); - dialog_->AddView(scores_text_); - - dialog_->SetAttribute("Title", "Click the button"); - dialog_->SetCallback( - jui_helper::JUICALLBACK_DIALOG_DISMISSED, - [this](jui_helper::JUIDialog *dialog, const int32_t message) { - LOGI("Dialog dismissed"); - LeaveGame(); - dialog_ = nullptr; - }); - - dialog_->Show(); - LOGI("Showing dialog"); - - // - // Invoke time counter periodically - // - std::thread([this]() { - ndk_helper::JNIHelper &helper = - *ndk_helper::JNIHelper::GetInstance(); - helper.AttachCurrentThread(); - while (UpdateTime()) { - std::chrono::milliseconds d(100); - std::this_thread::sleep_for(d); - } - // Broadcast my score to others via reliable protocol - BroadcastScore(true); - - UpdateScore(); - helper.DetachCurrentThread(); - }).detach(); - }); -} - -/* - * Update game UI when some player's score is updated - */ -void Engine::UpdateScore() { - // Lock mutex since this one can be called from multiple thread, - // gpg callback tread and UI callback thread - std::lock_guard lock(mutex_); - - size_t SIZE = 64; - char str[SIZE]; - snprintf(str, SIZE, "%03d", score_counter_); - std::string str_myscore(str); - - snprintf(str, SIZE, "My score: %03d %s\n", score_counter_, - playing_ ? "" : "*"); - std::string allstr(str); - - // Append other player - std::vector participants = room_.Participants(); - for (gpg::MultiplayerParticipant participant : participants) { - LOGI("Participant name:%s ID:%s", participant.DisplayName().c_str(), - participant.Id().c_str()); - if (participant.HasPlayer()) - LOGI("self:%s PlayerID:%s", self_id_.c_str(), - participant.Player().Id().c_str()); - - if (participant.HasPlayer() && - participant.Player().Id().compare(self_id_) == 0) - continue; // Skip local player - - int32_t score = 0; - bool finished = false; - if (players_score_.find(participant.Id()) != players_score_.end()) { - score = players_score_[participant.Id()].score; - finished = players_score_[participant.Id()].finished; - } - - LOGI("Status %d", participant.Status()); - snprintf(str, SIZE, "%s: %03d %s\n", participant.DisplayName().c_str(), - score, finished ? "*" : ""); - allstr += str; - } - - // Update game UI, UI update needs to be performed in UI thread - ndk_helper::JNIHelper::GetInstance() - ->RunOnUiThread([this, str_myscore, allstr]() { - my_score_text_->SetAttribute("Text", str_myscore.c_str()); - scores_text_->SetAttribute("Text", allstr.c_str()); - }); -} - -/* - * Update game timer and game UI - */ -bool Engine::UpdateTime() { - // UpdateTime() is invoked from other thread asynchronously - // So need to Lock mutex - std::lock_guard lock(mutex_); - - if (!playing_) return false; - - double current_time = monitor_.GetCurrentTime(); - current_time -= start_time_ + 1.0; - - if (current_time >= GAME_DURATION) { - // finish game - playing_ = false; - current_time = GAME_DURATION; - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this]() { - button_play_->SetAttribute("Enabled", false); - }); - } - - // Update game UI, UI update needs to be performed in UI thread - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, current_time]() { - size_t SIZE = 64; - char str[SIZE]; - snprintf(str, SIZE, "0:%02.0f", GAME_DURATION - current_time); - time_text_->SetAttribute("Text", const_cast(str)); - }); - - return true; -} - -/* - * Initialize game state - */ -void Engine::InitializeGame() { - // Lock mutex - std::lock_guard lock(mutex_); - - playing_ = true; - - start_time_ = monitor_.GetCurrentTime(); - score_counter_ = 0; - players_score_.clear(); -} - -/* - * Leave game - */ -void Engine::LeaveGame() { - // Lock mutex - std::lock_guard lock(mutex_); - - service_->RealTimeMultiplayer().LeaveRoom( - room_, [](gpg::ResponseStatus const &status) {}); - - LOGI("Game is over"); - playing_ = false; -} - -/* - * Initialize game management UI, - * invoking jui_helper functions to create java UIs - */ -void Engine::InitUI() { - // The window initialization - jui_helper::JUIWindow::Init(app_->activity, JUIHELPER_CLASS_NAME); - - // Show toast with app label - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([]() { - if(NULL == jui_helper::JUIWindow::GetInstance()->GetContext()) { - return; - } - jui_helper::JUIToast toast( - ndk_helper::JNIHelper::GetInstance()->GetAppLabel()); - toast.Show(); - }); - - // - // Using jui_helper, a support library, to create and bind game management - // UIs. - // - - // - // Buttons - // - // Sign in button. - button_sign_in_ = new jui_helper::JUIButton( - service_->IsAuthorized() ? "Sign Out" : "Sign In"); - SetParameters(button_sign_in_, nullptr); - button_sign_in_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button_sign_in_->SetCallback([this](jui_helper::JUIView *view, - const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - if (service_->IsAuthorized()) { - service_->SignOut(); - } else { - service_->StartAuthorizationUI(); - } - } - }); - button_sign_in_->SetMargins(0, 0, 0, 50); - - jui_helper::JUIWindow::GetInstance()->AddView(button_sign_in_); - - button_quick_match_ = new jui_helper::JUIButton("Quick match"); - SetParameters(button_quick_match_, button_sign_in_); - button_quick_match_->SetCallback([this](jui_helper::JUIView *view, - const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - QuickMatch(); - } - }); - jui_helper::JUIWindow::GetInstance()->AddView(button_quick_match_); - - button_invite_ = new jui_helper::JUIButton("Invite Friends!"); - SetParameters(button_invite_, button_quick_match_); - button_invite_->SetCallback([this](jui_helper::JUIView *view, - const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - InviteFriend(); - } - }); - jui_helper::JUIWindow::GetInstance()->AddView(button_invite_); - - button_matches_ = new jui_helper::JUIButton("All my invitations"); - SetParameters(button_matches_, button_invite_); - button_matches_->SetCallback([this](jui_helper::JUIView *view, - const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - ShowRoomInbox(); - } - }); - jui_helper::JUIWindow::GetInstance()->AddView(button_matches_); - - status_text_ = new jui_helper::JUITextView( - service_->IsAuthorized() ? "Signed In." : "Signed Out."); - status_text_->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_BOTTOM, - jui_helper::LAYOUT_PARAMETER_TRUE); - status_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - jui_helper::JUIWindow::GetInstance()->AddView(status_text_); - - textViewFPS_ = new jui_helper::JUITextView("0.0FPS"); - textViewFPS_->SetAttribute("Gravity", jui_helper::ATTRIBUTE_GRAVITY_LEFT); - textViewFPS_->SetAttribute("TextColor", 0xffffffff); - textViewFPS_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, 18.f); - textViewFPS_->SetAttribute("Padding", 10, 10, 10, 10); - jui_helper::JUIWindow::GetInstance()->AddView(textViewFPS_); - - // Init play game services - InitGooglePlayGameServices(); - - return; -} - -/* - * Set common parameters to button - */ -void Engine::SetParameters(jui_helper::JUIButton *button, - jui_helper::JUIView *below) { - const int32_t buttonWidth = 600; - button->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button->SetAttribute("MinimumWidth", buttonWidth); - - if (below != nullptr) - button->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, below); -} - -/* - * Enable/Disable management UI - */ -void Engine::EnableUI(bool enable) { - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, enable]() { - button_sign_in_->SetAttribute("Enabled", enable); - - bool b = enable; - if (service_->IsAuthorized() == false) b = false; - button_quick_match_->SetAttribute("Enabled", b); - button_invite_->SetAttribute("Enabled", b); - button_matches_->SetAttribute("Enabled", b); - }); -} - -/* - * JNI functions those manage activity lifecycle - */ -extern "C" { -/* - * These callbacks are necessary to work Google Play Game Services UIs properly - * - * For apps which target Android 2.3 or 3.x devices (API Version prior to 14), - * Play Game Services has no way to automatically receive Activity lifecycle - * callbacks. In these cases, Play Game Services relies on the owning Activity - * to notify it of lifecycle events. Any Activity which owns a GameServices - * object should call the AndroidSupport::* functions from within their own - * lifecycle callback functions. The arguments in these functions match those - * provided by Android, so no additional processing is necessary. - */ -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivityResult( - JNIEnv *env, jobject thiz, jobject activity, jint requestCode, - jint resultCode, jobject data) { - gpg::AndroidSupport::OnActivityResult(env, activity, requestCode, resultCode, - data); -} - -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivityCreated( - JNIEnv *env, jobject thiz, jobject activity, jobject saved_instance_state) { - gpg::AndroidSupport::OnActivityCreated(env, activity, saved_instance_state); -} - -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivityDestroyed( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityDestroyed(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivityPaused( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityPaused(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivityResumed( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityResumed(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivitySaveInstanceState( - JNIEnv *env, jobject thiz, jobject activity, jobject out_state) { - gpg::AndroidSupport::OnActivitySaveInstanceState(env, activity, out_state); -} - -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivityStarted( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityStarted(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_nativeOnActivityStopped( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityStopped(env, activity); -} -} diff --git a/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.h b/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.h deleted file mode 100644 index f0f1777..0000000 --- a/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity.h +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) 2014 Google. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#ifndef TBMPSLEKETONNATIVIACTIVITY_H_ -#define TBMPSLEKETONNATIVIACTIVITY_H_ - -/* - * Include files - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -// For GPGS -#include "gpg/gpg.h" - -#include "TeapotRenderer.h" -#include "NDKHelper.h" -#include "JavaUI.h" - -/* - * Preprocessors - */ - -// Class name of helper function -#define HELPER_CLASS_NAME "com.sample.helper.NDKHelper" -// Class name of JUIhelper function -#define JUIHELPER_CLASS_NAME "com.sample.helper.JUIHelper" -// Share object name of helper function library -#define HELPER_CLASS_SONAME "ButtonClickerNativeActivity" - -// -// Also set "com.google.android.gms.games.APP_ID" in AndrdoiManifest.xml -// - -const int32_t MIN_PLAYERS = 1; -const int32_t MAX_PLAYERS = 3; -const double GAME_DURATION = 20.0; - -struct PLAYER_STATUS { - uint8_t score; - bool finished; -}; -/* - * Engine class of the sample - */ -struct android_app; -class Engine : public gpg::IRealTimeEventListener { - public: - // GPG-related methods - void InitGooglePlayGameServices(); - void InviteFriend(); - void ShowRoomInbox(); - - void InitializeGame(); - void PlayGame(); - void LeaveGame(); - void QuickMatch(); - - void BroadcastScore(bool bFinal); - - // Event handling - static void HandleCmd(struct android_app *app, int32_t cmd); - static int32_t HandleInput(android_app *app, AInputEvent *event); - - // Engine life cycles - Engine(); - ~Engine(); - void SetState(android_app *state); - int InitDisplay(const int32_t cmd); - void LoadResources(); - void UnloadResources(); - void DrawFrame(); - void TermDisplay(const int32_t cmd); - void TrimMemory(); - bool IsReady(); - - // IRealTimeEventListener members - virtual void OnRoomStatusChanged(gpg::RealTimeRoom const &room); - - virtual void OnParticipantStatusChanged( - gpg::RealTimeRoom const &room, - gpg::MultiplayerParticipant const &participant); - - virtual void OnDataReceived( - gpg::RealTimeRoom const &room, - gpg::MultiplayerParticipant const &from_participant, - std::vector data, bool is_reliable); - - // We are not using these callbacks below - // because the app just waits for the room to become active, - // no need to inspect individual changes - virtual void OnConnectedSetChanged(gpg::RealTimeRoom const &room) {} - - virtual void OnP2PConnected(gpg::RealTimeRoom const &room, - gpg::MultiplayerParticipant const &participant) {} - virtual void OnP2PDisconnected( - gpg::RealTimeRoom const &room, - gpg::MultiplayerParticipant const &participant) {} - - private: - // Callbacks for GPG authentication. - void OnAuthActionStarted(gpg::AuthOperation op); - void OnAuthActionFinished(gpg::AuthOperation op, gpg::AuthStatus status); - - void UpdateScore(); - bool UpdateTime(); - - void EnableUI(bool enable); - void InitUI(); - void TransformPosition(ndk_helper::Vec2 &vec); - void UpdateFPS(float fFPS); - void SetParameters(jui_helper::JUIButton *button, jui_helper::JUIView *below); - - std::unique_ptr service_; // gpg service instance - std::unordered_map players_score_; // hashmap to - // keep - // tracking of - // player - // scores - gpg::RealTimeRoom room_; // room status. This variable is updated each time - // the room status is updated - // in OnRoomStatusChanged() - int32_t score_counter_; // Score counter of local player - bool playing_; // Am I playing a game? - std::string self_id_; // Local player's ID - double start_time_; // Game start time - - // synchronization primitive to synchronize - // UIThread, Timer thread and gpg callback thread - mutable std::mutex mutex_; - - // Renderer of a teapot - TeapotRenderer renderer_; - - // GLContext instance - ndk_helper::GLContext *gl_context_; - - bool initialized_resources_; - bool has_focus_; - - // Helpers for touch control - ndk_helper::DoubletapDetector doubletap_detector_; - ndk_helper::PinchDetector pinch_detector_; - ndk_helper::DragDetector drag_detector_; - ndk_helper::PerfMonitor monitor_; - ndk_helper::TapCamera tap_camera_; - - // JUI text view to show FPS - jui_helper::JUITextView *textViewFPS_; - - // Native acitivity app instance - android_app *app_; - - // JUI dialog-related UI stuff here - jui_helper::JUIDialog *dialog_; - - jui_helper::JUIButton *button_sign_in_; - jui_helper::JUIButton *button_quick_match_; - jui_helper::JUIButton *button_invite_; - jui_helper::JUIButton *button_matches_; - - jui_helper::JUITextView *status_text_; - - jui_helper::JUITextView *time_text_; - jui_helper::JUITextView *my_score_text_; - jui_helper::JUITextView *scores_text_; - jui_helper::JUIButton *button_play_; -}; - -#endif // TBMPSLEKETONNATIVIACTIVITY_H_ diff --git a/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity_Engine.cpp b/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity_Engine.cpp deleted file mode 100644 index 6b7df0d..0000000 --- a/samples-android/ButtonClicker/src/main/cpp/ButtonClickerNativeActivity_Engine.cpp +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright (c) 2014 Google. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -/* - * Include files - */ -#include "ButtonClickerNativeActivity.h" - -/* - * Ctor - */ -Engine::Engine() - : initialized_resources_(false), - has_focus_(false), - textViewFPS_(nullptr), - app_(nullptr), - dialog_(nullptr), - button_sign_in_(nullptr), - button_invite_(nullptr), - status_text_(nullptr) { - gl_context_ = ndk_helper::GLContext::GetInstance(); -} - -/* - * Dtor - */ -Engine::~Engine() { - jui_helper::JUIWindow::GetInstance()->Close(); - delete dialog_; -} - -/** - * Called when GPU resources need to be claimed (e.g. Textures, Shaders etc) - * Initialize all GPU resources here. - */ -void Engine::LoadResources() { - renderer_.Init(); - renderer_.Bind(&tap_camera_); -} - -/** - * Called when GPU resources need to be cleaned up when GL Context has been lost - * Cleanup all GPU resources here - */ -void Engine::UnloadResources() { renderer_.Unload(); } - -/** - * Initialize an EGL context for the current display. - */ -int Engine::InitDisplay(const int32_t cmd) { - if (!initialized_resources_) { - gl_context_->Init(app_->window); - InitUI(); - LoadResources(); - initialized_resources_ = true; - } else { - // initialize OpenGL ES and EGL - if (EGL_SUCCESS != gl_context_->Resume(app_->window)) { - UnloadResources(); - LoadResources(); - } - - jui_helper::JUIWindow::GetInstance()->Resume(app_->activity, cmd); - } - - // Enable culling OpenGL state - glEnable(GL_CULL_FACE); - - // Enabled depth test OpenGL state - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - - // Note that screen size might have been changed - glViewport(0, 0, gl_context_->GetScreenWidth(), - gl_context_->GetScreenHeight()); - renderer_.UpdateViewport(); - - tap_camera_.SetFlip(1.f, -1.f, -1.f); - tap_camera_.SetPinchTransformFactor(2.f, 2.f, 8.f); - - return 0; -} - -/** - * Just the current frame in the display. - */ -void Engine::DrawFrame() { - float fFPS; - if (monitor_.Update(fFPS)) { - UpdateFPS(fFPS); - } - renderer_.Update(monitor_.GetCurrentTime()); - - // Just fill the screen with a color. - glClearColor(0.5f, 0.5f, 0.5f, 1.f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - renderer_.Render(); - - // Swap - if (EGL_SUCCESS != gl_context_->Swap()) { - UnloadResources(); - LoadResources(); - } -} - -/** - * Tear down the EGL context currently associated with the display. - */ -void Engine::TermDisplay(const int32_t cmd) { - gl_context_->Suspend(); - jui_helper::JUIWindow::GetInstance()->Suspend(cmd); -} - -void Engine::TrimMemory() { - LOGI("Trimming memory"); - gl_context_->Invalidate(); -} - -/** - * Process the next input event. - * In the handler, it passes touch events to gesture detector helper class - * Based on gesture detection, the app moves camera, model view accordingly - */ -int32_t Engine::HandleInput(android_app *app, AInputEvent *event) { - Engine *eng = (Engine *)app->userData; - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { - ndk_helper::GESTURE_STATE doubleTapState = - eng->doubletap_detector_.Detect(event); - ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect(event); - ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect(event); - - // Double tap detector has a priority over other detectors - if (doubleTapState == ndk_helper::GESTURE_STATE_ACTION) { - // Detect double tap - eng->tap_camera_.Reset(true); - } else { - // Handle drag state - if (dragState & ndk_helper::GESTURE_STATE_START) { - // Otherwise, start dragging - ndk_helper::Vec2 v; - eng->drag_detector_.GetPointer(v); - eng->TransformPosition(v); - eng->tap_camera_.BeginDrag(v); - } else if (dragState & ndk_helper::GESTURE_STATE_MOVE) { - ndk_helper::Vec2 v; - eng->drag_detector_.GetPointer(v); - eng->TransformPosition(v); - eng->tap_camera_.Drag(v); - } else if (dragState & ndk_helper::GESTURE_STATE_END) { - eng->tap_camera_.EndDrag(); - } - - // Handle pinch state - if (pinchState & ndk_helper::GESTURE_STATE_START) { - // Start new pinch - ndk_helper::Vec2 v1; - ndk_helper::Vec2 v2; - eng->pinch_detector_.GetPointers(v1, v2); - eng->TransformPosition(v1); - eng->TransformPosition(v2); - eng->tap_camera_.BeginPinch(v1, v2); - } else if (pinchState & ndk_helper::GESTURE_STATE_MOVE) { - // Multi touch - // Start new pinch - ndk_helper::Vec2 v1; - ndk_helper::Vec2 v2; - eng->pinch_detector_.GetPointers(v1, v2); - eng->TransformPosition(v1); - eng->TransformPosition(v2); - eng->tap_camera_.Pinch(v1, v2); - } - } - return 1; - } - return 0; -} - -/** - * Process the next main command. - */ -void Engine::HandleCmd(struct android_app *app, int32_t cmd) { - Engine *eng = (Engine *)app->userData; - switch (cmd) { - case APP_CMD_SAVE_STATE: - break; - case APP_CMD_INIT_WINDOW: - if (app->window != NULL) { - eng->InitDisplay(APP_CMD_INIT_WINDOW); - eng->DrawFrame(); - } - - break; - case APP_CMD_TERM_WINDOW: - // Note that JUI helper needs to know if a window has been terminated - eng->TermDisplay(APP_CMD_TERM_WINDOW); - - eng->has_focus_ = false; - break; - case APP_CMD_START: - break; - case APP_CMD_STOP: - break; - case APP_CMD_RESUME: - jui_helper::JUIWindow::GetInstance()->Resume(app->activity, - APP_CMD_RESUME); - break; - case APP_CMD_GAINED_FOCUS: - // Start animation - eng->has_focus_ = true; - jui_helper::JUIWindow::GetInstance()->Resume(app->activity, - APP_CMD_GAINED_FOCUS); - break; - case APP_CMD_LOST_FOCUS: - // Also stop animating. - eng->has_focus_ = false; - eng->DrawFrame(); - break; - case APP_CMD_LOW_MEMORY: - // Free up GL resources - eng->TrimMemory(); - break; - case APP_CMD_CONFIG_CHANGED: - // Configuration changes - eng->TermDisplay(APP_CMD_CONFIG_CHANGED); - eng->InitDisplay(APP_CMD_CONFIG_CHANGED); - break; - case APP_CMD_DESTROY: - ndk_helper::JNIHelper::GetInstance()->DetachCurrentThread(); - break; - } -} - -/* - * Misc - */ -void Engine::SetState(android_app *state) { - app_ = state; - doubletap_detector_.SetConfiguration(app_->config); - drag_detector_.SetConfiguration(app_->config); - pinch_detector_.SetConfiguration(app_->config); -} - -bool Engine::IsReady() { - if (has_focus_) return true; - - return false; -} - -void Engine::TransformPosition(ndk_helper::Vec2 &vec) { - vec = ndk_helper::Vec2(2.0f, 2.0f) * vec / - ndk_helper::Vec2(gl_context_->GetScreenWidth(), - gl_context_->GetScreenHeight()) - - ndk_helper::Vec2(1.f, 1.f); -} - -void Engine::UpdateFPS(float fFPS) { - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([fFPS, this]() { - const int32_t count = 64; - char str[count]; - snprintf(str, count, "%2.2f FPS", fFPS); - textViewFPS_->SetAttribute("Text", (const char *)str); - }); - - return; -} - -Engine g_engine; - -/** - * This is the main entry point of a native application that is using - * android_native_app_glue. It runs in its own thread, with its own - * event loop for receiving input events and doing other things. - */ -void android_main(android_app *state) { - - g_engine.SetState(state); - - // Init helper functions - ndk_helper::JNIHelper::Init(state->activity, HELPER_CLASS_NAME, - HELPER_CLASS_SONAME); - - // Init play game services - g_engine.InitGooglePlayGameServices(); - - state->userData = &g_engine; - state->onAppCmd = Engine::HandleCmd; - state->onInputEvent = Engine::HandleInput; - - // loop waiting for stuff to do. - while (1) { - // Read all pending events. - int id; - int events; - android_poll_source *source; - - // If not animating, we will block forever waiting for events. - // If animating, we loop until all events are read, then continue - // to draw the next frame of animation. - while ((id = ALooper_pollAll(g_engine.IsReady() ? 0 : -1, NULL, &events, - (void **)&source)) >= 0) { - // Process this event. - if (source != NULL) source->process(state, source); - - // Check if we are exiting. - if (state->destroyRequested != 0) { - g_engine.TermDisplay(APP_CMD_TERM_WINDOW); - return; - } - } - - if (g_engine.IsReady()) { - // Drawing is throttled to the screen update rate, so there - // is no need to do timing here. - g_engine.DrawFrame(); - } - } -} - -extern "C" { -JNIEXPORT void -Java_com_google_example_games_ButtonClicker_ButtonClickerNativeActivity_OnPauseHandler( - JNIEnv *env) { - // This call is to suppress 'E/WindowManager(): android.view.WindowLeaked...' - // errors. - // Since orientation change events in NativeActivity comes later than - // expected, we can not dismiss - // popupWindow gracefully from NativeActivity. - // So we are releasing popupWindows explicitly triggered from Java callback - // through JNI call. - jui_helper::JUIWindow::GetInstance()->Suspend(APP_CMD_PAUSE); -} -} diff --git a/samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerApplication.java b/samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerApplication.java deleted file mode 100644 index d5830c1..0000000 --- a/samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerApplication.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.example.games.ButtonClicker; - -import android.app.Application; - -public class ButtonClickerApplication extends Application { - public void onCreate() { - super.onCreate(); - } -} diff --git a/samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerNativeActivity.java b/samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerNativeActivity.java deleted file mode 100644 index 898d104..0000000 --- a/samples-android/ButtonClicker/src/main/java/com/google/example/games/ButtonClicker/ButtonClickerNativeActivity.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.example.games.ButtonClicker; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.NativeActivity; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; - -public class ButtonClickerNativeActivity extends NativeActivity { - // Load SO - static { - System.loadLibrary("ButtonClickerNativeActivity"); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // Hide toolbar - int SDK_INT = android.os.Build.VERSION.SDK_INT; - if (SDK_INT >= 19) { - setImmersiveSticky(); - - View decorView = getWindow().getDecorView(); - decorView - .setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { - @Override - public void onSystemUiVisibilityChange(int visibility) { - setImmersiveSticky(); - } - }); - } - nativeOnActivityCreated(this, savedInstanceState); - } - - @SuppressLint("InlinedApi") - @SuppressWarnings("deprecation") - protected void onResume() { - super.onResume(); - - // Hide toolbar - int SDK_INT = android.os.Build.VERSION.SDK_INT; - if (SDK_INT >= 11 && SDK_INT < 14) { - getWindow().getDecorView().setSystemUiVisibility( - View.STATUS_BAR_HIDDEN); - } else if (SDK_INT >= 14 && SDK_INT < 19) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_LOW_PROFILE); - } else if (SDK_INT >= 19) { - setImmersiveSticky(); - } - - nativeOnActivityResumed(this); - } - - @SuppressLint("InlinedApi") - void setImmersiveSticky() { - View decorView = getWindow().getDecorView(); - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - } - - protected void onPause() { - super.onPause(); - // This call is to suppress 'E/WindowManager(): - // android.view.WindowLeaked...' errors. - // Since orientation change events in NativeActivity comes later than - // expected, we can not dismiss - // popupWindow gracefully from NativeActivity. - // So we are releasing popupWindows explicitly triggered from Java - // callback through JNI call. - OnPauseHandler(); - - nativeOnActivityPaused(this); - } - - protected void onDestroy() { - super.onDestroy(); - nativeOnActivityDestroyed(this); - } - - protected void onStart() { - super.onStart(); - nativeOnActivityStarted(this); - } - - protected void onStop() { - super.onStop(); - nativeOnActivityStopped(this); - } - - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - nativeOnActivitySaveInstanceState(this, outState); - } - - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - nativeOnActivityResult(this, requestCode,resultCode, data); - } - - // Implemented in C++. - public static native void nativeOnActivityResult(Activity activity, - int requestCode, int resultCode, Intent data); - - public static native void nativeOnActivityCreated(Activity activity, - Bundle savedInstanceState); - - private static native void nativeOnActivityDestroyed(Activity activity); - - private static native void nativeOnActivityPaused(Activity activity); - - private static native void nativeOnActivityResumed(Activity activity); - - private static native void nativeOnActivitySaveInstanceState( - Activity activity, Bundle outState); - - private static native void nativeOnActivityStarted(Activity activity); - - private static native void nativeOnActivityStopped(Activity activity); - - native public void OnPauseHandler(); -} diff --git a/samples-android/ButtonClicker/src/main/res/drawable-hdpi/ic_launcher.png b/samples-android/ButtonClicker/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index ea01cbf83706f5ddf4016b7a20d15bf5ff00fa72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8203 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 S+rjPt0000GmlA diff --git a/samples-android/ButtonClicker/src/main/res/drawable-mdpi/ic_launcher.png b/samples-android/ButtonClicker/src/main/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 8f808970c1991083869d91081f3fda8e33152203..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4064 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 Sl%PTY0000gb)(kA!u-y7T4nL1PRHU^ZcLp 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 diff --git a/samples-android/ButtonClicker/src/main/res/values/ids.xml b/samples-android/ButtonClicker/src/main/res/values/ids.xml deleted file mode 100644 index cd729a8..0000000 --- a/samples-android/ButtonClicker/src/main/res/values/ids.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - REPLACE_ME - - diff --git a/samples-android/ButtonClicker/src/main/res/values/strings.xml b/samples-android/ButtonClicker/src/main/res/values/strings.xml deleted file mode 100644 index facc182..0000000 --- a/samples-android/ButtonClicker/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - ButtonClicker - - diff --git a/samples-android/ButtonClicker/src/main/res/values/styles.xml b/samples-android/ButtonClicker/src/main/res/values/styles.xml deleted file mode 100644 index 4a10ca4..0000000 --- a/samples-android/ButtonClicker/src/main/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/samples-android/TbmpSkeletonNative/CMakeLists.txt b/samples-android/TbmpSkeletonNative/CMakeLists.txt deleted file mode 100644 index 8d2a0ca..0000000 --- a/samples-android/TbmpSkeletonNative/CMakeLists.txt +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (C) 2017 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -## - -cmake_minimum_required(VERSION 3.4.1) - -# Import the GPGS C++ SDK library -add_library(gpg_sdk STATIC IMPORTED) -set_target_properties(gpg_sdk PROPERTIES IMPORTED_LOCATION - ${GPG_SDK_PATH}/lib/c++/${ANDROID_ABI}/libgpg.a) - -# build native_app_glue as a static lib -add_library(native_app_glue STATIC - ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) - -# build cpufeatures as a static lib -add_library(cpufeatures STATIC - ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c) - -#include the native part of JUI Helper -add_subdirectory (${JUI_HELPER_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libjuihelper") - -#include the native part of NDKHelper -add_subdirectory (${NDK_HELPER_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libndkhelper") - -#JSON library -add_library(json STATIC - ${JSON_PATH}/src/lib_json/json_writer.cpp - ${JSON_PATH}/src/lib_json/json_reader.cpp - ${JSON_PATH}/src/lib_json/json_value.cpp - ) - target_include_directories(json PRIVATE - ${JSON_PATH}/include - ) - -# Export ANativeActivity_onCreate(), -# Refer to: https://github.com/android-ndk/ndk/issues/381. -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") - - -add_library(TBMPSkeletonNativeActivity - SHARED - src/main/cpp/TBMPSkeletonNativeActivity.cpp - src/main/cpp/TBMPSkeletonNativeActivity_Engine.cpp - ${TEAPOT_RENDERER_PATH}/TeapotRenderer.cpp - ) - -target_include_directories(TBMPSkeletonNativeActivity PRIVATE - ${ANDROID_NDK}/sources/android/native_app_glue - ${ANDROID_NDK}/sources/android/cpufeatures - ${GPG_SDK_PATH}/include - ${JSON_PATH}/include - ${juihelper_SOURCE_DIR}/src/main/cpp - ${ndkhelper_SOURCE_DIR}/src/main/cpp - ${TEAPOT_RENDERER_PATH} -) - -# Specifies libraries CMake should link to your target library. You -# can link multiple libraries, such as libraries you define in this -# build script, prebuilt third-party libraries, or system libraries. - -target_link_libraries(TBMPSkeletonNativeActivity - gpg_sdk - native_app_glue - cpufeatures - juihelper - ndkhelper - json - log - android - EGL - GLESv2 - z) diff --git a/samples-android/TbmpSkeletonNative/build.gradle b/samples-android/TbmpSkeletonNative/build.gradle deleted file mode 100644 index df1d311..0000000 --- a/samples-android/TbmpSkeletonNative/build.gradle +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 (C) Google LLC - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 27 - defaultConfig { - // - // REPLACE THE APPLICATION ID with your bundle ID - // - applicationId "com.google.example.games.ReplaceMe" - versionCode 1 - versionName "1.0" - - minSdkVersion 14 - targetSdkVersion 27 - - ndk.abiFilters 'x86', 'armeabi-v7a', 'arm64-v8a' - - externalNativeBuild { - cmake { - cppFlags "-std=c++11 -frtti -Wall -Werror" - arguments "-DJUI_HELPER_PATH=${project(':Common:JuiHelper').projectDir}" , - "-DNDK_HELPER_PATH=${project(':Common:NDKHelper').projectDir}" , - "-DGPG_SDK_PATH=${project(':Common:gpg-sdk').projectDir}/gpg-cpp-sdk/android", - "-DJSON_PATH=${project(':Common:gpg-sdk').projectDir}/../external/jsoncpp/", - "-DTEAPOT_RENDERER_PATH=${project(':Common').projectDir}/TeapotRenderer" , - "-DANDROID_STL=c++_static", - "-DANDROID_TOOLCHAIN=clang" - } - - } - } - - externalNativeBuild { - cmake.path "CMakeLists.txt" - } -} - -dependencies { - implementation project(":Common:JuiHelper") - implementation 'com.google.android.gms:play-services-games:11.8.0' - implementation 'com.google.android.gms:play-services-nearby:11.8.0' - implementation 'com.android.support:support-v4:27.0.2' -} diff --git a/samples-android/TbmpSkeletonNative/proguard-project.txt b/samples-android/TbmpSkeletonNative/proguard-project.txt deleted file mode 100644 index f2fe155..0000000 --- a/samples-android/TbmpSkeletonNative/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# 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 *; -#} diff --git a/samples-android/TbmpSkeletonNative/src/main/AndroidManifest.xml b/samples-android/TbmpSkeletonNative/src/main/AndroidManifest.xml deleted file mode 100644 index 59bd37d..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/AndroidManifest.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples-android/TbmpSkeletonNative/src/main/assets/Shaders/ShaderPlain.fsh b/samples-android/TbmpSkeletonNative/src/main/assets/Shaders/ShaderPlain.fsh deleted file mode 100644 index 75ae290..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/assets/Shaders/ShaderPlain.fsh +++ /dev/null @@ -1,42 +0,0 @@ - -// -// ShaderPlain.fsh -// - -#define USE_PHONG (1) - -uniform lowp vec3 vMaterialAmbient; -uniform mediump vec4 vMaterialSpecular; - -varying lowp vec4 colorDiffuse; - -#if USE_PHONG -uniform highp vec3 vLight0; -varying mediump vec3 position; -varying mediump vec3 normal; -#else -varying lowp vec4 colorSpecular; -#endif - -/* - Standard phong fragment shader implementation. - Take object space light position and normal, derive a specular intensity. -*/ -void main() { -#if USE_PHONG - mediump vec3 - halfVector = normalize(-vLight0 + position); - mediump - float NdotH = max(dot(normalize(normal), halfVector), 0.0); - mediump - float fPower = vMaterialSpecular.w; - mediump - float specular = pow(NdotH, fPower); - - lowp vec4 - colorSpecular = vec4(vMaterialSpecular.xyz * specular, 1); - gl_FragColor = colorDiffuse + colorSpecular; -#else - gl_FragColor = colorDiffuse + colorSpecular; -#endif -} \ No newline at end of file diff --git a/samples-android/TbmpSkeletonNative/src/main/assets/Shaders/VS_ShaderPlain.vsh b/samples-android/TbmpSkeletonNative/src/main/assets/Shaders/VS_ShaderPlain.vsh deleted file mode 100644 index a62e4a8..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/assets/Shaders/VS_ShaderPlain.vsh +++ /dev/null @@ -1,64 +0,0 @@ -// -// ShaderPlain.vsh -// - -#define USE_PHONG (1) - -attribute highp vec3 myVertex; -attribute highp vec3 myNormal; -attribute mediump vec2 myUV; -attribute mediump vec4 myBone; - -varying mediump vec2 texCoord; -varying lowp vec4 colorDiffuse; - -#if USE_PHONG -varying mediump vec3 position; -varying mediump vec3 normal; -#else -varying lowp vec4 colorSpecular; -#endif - -uniform highp mat4 uMVMatrix; -uniform highp mat4 uPMatrix; - -uniform highp vec3 vLight0; - -uniform lowp vec4 vMaterialDiffuse; -uniform lowp vec3 vMaterialAmbient; -uniform lowp vec4 vMaterialSpecular; - -/* - Standard vertex shader shader implementation. - Transform vertices and normal, - derive diffuse value and pass object space light source - position to fragment shader for phong shading. -*/ -void main(void) { - highp vec4 - p = vec4(myVertex, 1); - gl_Position = uPMatrix * p; - - texCoord = myUV; - - highp vec3 - worldNormal = vec3( - mat3(uMVMatrix[0].xyz, uMVMatrix[1].xyz, uMVMatrix[2].xyz) * myNormal); - highp vec3 - ecPosition = p.xyz; - - colorDiffuse = dot(worldNormal, normalize(-vLight0 + ecPosition)) - * vMaterialDiffuse + vec4(vMaterialAmbient, 1); - -#if USE_PHONG - normal = worldNormal; - position = ecPosition; -#else - highp vec3 halfVector = normalize(ecPosition - vLight0); - - highp float NdotH = max(-dot(worldNormal, halfVector), 0.0); - float fPower = vMaterialSpecular.w; - highp float specular = min( pow(NdotH, fPower), 1.0); - colorSpecular = vec4(vMaterialSpecular.xyz * specular, 1); -#endif -} diff --git a/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.cpp b/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.cpp deleted file mode 100644 index 481ce82..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.cpp +++ /dev/null @@ -1,746 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Include files - */ -#include "TBMPSkeletonNativeActivity.h" - -/* - * Key for JSON object - */ -const char *TurnCounterKey = "turnCounter"; -const char *StringToPassKey = "data"; - -/* - * Initialize GooglePlayGameServices - */ -void Engine::InitGooglePlayGameServices() { - gpg::AndroidInitialization::android_main(app_); - - if (service_ == nullptr) { - // Game Services have not been initialized, create a new Game Services. - gpg::AndroidPlatformConfiguration platform_configuration; - platform_configuration.SetActivity(app_->activity->clazz); - - gpg::GameServices::Builder builder; - service_ = builder.SetOnAuthActionStarted([this](gpg::AuthOperation op) { - OnAuthActionStarted(op); - }) - .SetOnAuthActionFinished( - [this](gpg::AuthOperation op, gpg::AuthStatus status) { - OnAuthActionFinished(op, status); - }) - .SetOnTurnBasedMatchEvent( - [this](gpg::MultiplayerEvent event, std::string match_id, - gpg::TurnBasedMatch match) { - LOGI("TurnBasedMultiplayerEvent callback"); - //Show default inbox - ShowMatchInbox(); - }) - .SetOnMultiplayerInvitationEvent( - [this](gpg::MultiplayerEvent event, std::string match_id, - gpg::MultiplayerInvitation invitation) { - LOGI("MultiplayerInvitationEvent callback"); - //Show default inbox - ShowMatchInbox(); - }).Create(platform_configuration); - } -} - -/* - * Callback: Authentication started - */ -void Engine::OnAuthActionStarted(gpg::AuthOperation op) { - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, op]() { - EnableUI(false); - authorizing_ = true; - if (status_text_) { - if (op == gpg::AuthOperation::SIGN_IN) { - LOGI("Signing in to GPG"); - status_text_->SetAttribute("Text", "Signing In..."); - } else { - LOGI("Signing out from GPG"); - status_text_->SetAttribute("Text", "Signing Out..."); - } - } - }); -} - -/* - * Callback: Authentication finishes - */ -void Engine::OnAuthActionFinished(gpg::AuthOperation op, - gpg::AuthStatus status) { - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, status]() { - EnableUI(true); - authorizing_ = false; - if (button_sign_in_) { - button_sign_in_->SetAttribute( - "Text", gpg::IsSuccess(status) ? "Sign Out" : "Sign In"); - } - - if (status_text_) { - status_text_->SetAttribute( - "Text", - gpg::IsSuccess(status) ? "Signed In" : "Signed Out"); - } - }); -} - -/* - * Show match inbox - */ -void Engine::ShowMatchInbox() { - service_->TurnBasedMultiplayer().ShowMatchInboxUI([this]( - gpg::TurnBasedMultiplayerManager::MatchInboxUIResponse const & response) { - if (gpg::IsSuccess(response.status)) { - //Show game based on the user's selection - switch (response.match.Status()) { - case gpg::MatchStatus::THEIR_TURN: - //Manage match with dismiss, leave and cancel options - ManageGame(response.match, true, true, false); - break; - case gpg::MatchStatus::MY_TURN: - //Play selected game - PlayGame(response.match); - break; - case gpg::MatchStatus::COMPLETED: - //Manage match with dismiss, rematch options - ManageGame(response.match, false, false, true); - break; - case gpg::MatchStatus::EXPIRED: - default: - //Manage match with dismiss option - ManageGame(response.match, false, false, false); - break; - } - } else { - LOGI("Invalid response status"); - } - }); -} - -/* - * Quick match - * - Create a match with minimal setting and play the game - */ -void Engine::QuickMatch() { - gpg::TurnBasedMultiplayerManager &manager = service_->TurnBasedMultiplayer(); - gpg::TurnBasedMatchConfig config = - gpg::TurnBasedMatchConfig::Builder().SetMinimumAutomatchingPlayers(1) - .SetMaximumAutomatchingPlayers(2).Create(); - - EnableUI(false); - manager.CreateTurnBasedMatch( - config, - [this](gpg::TurnBasedMultiplayerManager::TurnBasedMatchResponse const & - matchResponse) { - EnableUI(true); - if (gpg::IsSuccess(matchResponse.status)) { - PlayGame(matchResponse.match); - } - }); -} - -/* - * Invite friends - * - Show Player Select UI via ShowPlayerSelectUI, - * - When the UI is finished, create match and show game UI - */ -void Engine::InviteFriend() { - service_->TurnBasedMultiplayer().ShowPlayerSelectUI( - MIN_PLAYERS, MAX_PLAYERS, true, - [this](gpg::TurnBasedMultiplayerManager::PlayerSelectUIResponse const & - response) { - LOGI("selected match %d", response.status); - if (gpg::IsSuccess(response.status)) { - // Create new match with the config - gpg::TurnBasedMatchConfig config = gpg::TurnBasedMatchConfig::Builder() - .SetMinimumAutomatchingPlayers(response.minimum_automatching_players) - .SetMaximumAutomatchingPlayers(response.maximum_automatching_players) - .AddAllPlayersToInvite(response.player_ids).Create(); - EnableUI(false); - service_->TurnBasedMultiplayer().CreateTurnBasedMatch( - config, - [this]( - gpg::TurnBasedMultiplayerManager::TurnBasedMatchResponse const & - matchResponse) { - EnableUI(true); - if (gpg::IsSuccess(matchResponse.status)) { - PlayGame(matchResponse.match); - } - }); - } - }); -} - -/* - * Cancel current match - */ -void Engine::CancelMatch() { - EnableUI(false); - gpg::TurnBasedMultiplayerManager &manager = service_->TurnBasedMultiplayer(); - manager.CancelMatch(current_match_, [this](gpg::MultiplayerStatus status) { - EnableUI(true); - LOGI("Canceled the game"); - }); -} - -/* - * Dismiss current match - */ -void Engine::DismissMatch() { - gpg::TurnBasedMultiplayerManager &manager = service_->TurnBasedMultiplayer(); - manager.DismissMatch(current_match_); -} - -/* - * Rematch selected match - */ -void Engine::Rematch() { - EnableUI(false); - gpg::TurnBasedMultiplayerManager &manager = service_->TurnBasedMultiplayer(); - manager.Rematch( - current_match_, - [this](gpg::TurnBasedMultiplayerManager::TurnBasedMatchResponse - matchResponse) { - LOGI("Remathing the game"); - EnableUI(true); - if (gpg::IsSuccess(matchResponse.status)) { - PlayGame(matchResponse.match); - } - }); -} - -/* - * Leave current match - * Invoking different APIs based on match state - */ -void Engine::LeaveMatch() { - gpg::TurnBasedMultiplayerManager &manager = service_->TurnBasedMultiplayer(); - if (current_match_.Status() == gpg::MatchStatus::MY_TURN) { - //Leave a game - manager.LeaveMatchDuringMyTurn(current_match_, - current_match_.SuggestedNextParticipant(), - [](gpg::MultiplayerStatus status) { - LOGI("Left the game"); - }); - } else { - manager.LeaveMatchDuringTheirTurn(current_match_, - [](gpg::MultiplayerStatus status) { - LOGI("Left the game"); - }); - } - return; -} - -/* - * Take turn based on playGame UI parameters - */ -void Engine::TakeTurn(const bool winning, const bool losing) { - gpg::TurnBasedMultiplayerManager &manager = service_->TurnBasedMultiplayer(); - - // When it is MY_TURN, localParticipant is always PendingParticipant(). - gpg::MultiplayerParticipant localParticipant = - current_match_.PendingParticipant(); - - LOGI("Taking my turn. local participant id:%s", - localParticipant.Id().c_str()); - - gpg::MultiplayerParticipant nextParticipant = - current_match_.SuggestedNextParticipant(); - - if (!nextParticipant.Valid()) { - //Error case - manager.DismissMatch(current_match_); - return; - } - - turn_counter_++; - std::vector match_data = SetupMatchData(); - - //By default, passing through existing ParticipantResults - gpg::ParticipantResults results = current_match_.ParticipantResults(); - - if (winning) { - //Create winning participants result - results = current_match_.ParticipantResults() - .WithResult(localParticipant.Id(), // local player ID - 0, // placing - gpg::MatchResult::WIN // status - ); - } else if (losing) { - //Create losing participants result - results = current_match_.ParticipantResults() - .WithResult(localParticipant.Id(), // local player ID - 0, // placing - gpg::MatchResult::LOSS // status - ); - } - - //Take normal turn - manager.TakeMyTurn( - current_match_, match_data, results, nextParticipant, - [](gpg::TurnBasedMultiplayerManager::TurnBasedMatchResponse const & - response) { - LOGI("Took turn"); - }); -} - -/* - * Play games UI that is in your turn - */ -void Engine::PlayGame(gpg::TurnBasedMatch const &match) { - current_match_ = match; - ParseMatchData(); - - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this]() { - LOGI("Playing match"); - if (dialog_) - delete dialog_; - - dialog_ = new jui_helper::JUIDialog(app_->activity); - - // Setting up CheckBox - checkBoxWinning_ = new jui_helper::JUICheckBox("Win the game"); - checkBoxLosing_ = new jui_helper::JUICheckBox("I have lost"); - checkBoxQuit_ = new jui_helper::JUICheckBox("Leave"); - - // Setting up linear layout - jui_helper::JUILinearLayout *layout = new jui_helper::JUILinearLayout(); - layout->SetLayoutParams(jui_helper::ATTRIBUTE_SIZE_MATCH_PARENT, - jui_helper::ATTRIBUTE_SIZE_WRAP_CONTENT); - layout->SetMargins(0, 50, 0, 50); - layout->SetAttribute("Orientation", - jui_helper::LAYOUT_ORIENTATION_VERTICAL); - layout->AddView(checkBoxWinning_); - layout->AddView(checkBoxLosing_); - layout->AddView(checkBoxQuit_); - - // Take Turn Button - jui_helper::JUIButton *button = new jui_helper::JUIButton("Take Turn"); - button->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - if (checkBoxQuit_->IsChecked()) - LeaveMatch(); - else - TakeTurn(checkBoxWinning_->IsChecked(), checkBoxLosing_->IsChecked()); - dialog_->Close(); - } - }); - button->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, layout); - - jui_helper::JUIButton *buttonCancel = new jui_helper::JUIButton("Cancel"); - buttonCancel->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - CancelMatch(); - dialog_->Close(); - } - }); - buttonCancel->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - buttonCancel->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, button); - - dialog_->SetCallback( - jui_helper::JUICALLBACK_DIALOG_CANCELLED, - [this](jui_helper::JUIDialog * dialog, const int32_t message) { - LOGI("Dialog cancelled"); - CancelMatch(); - dialog_->Close(); - }); - dialog_->SetCallback( - jui_helper::JUICALLBACK_DIALOG_DISMISSED, - [this](jui_helper::JUIDialog * dialog, const int32_t message) { - LOGI("Dialog dismissed"); - dialog_->Close(); - }); - - size_t size = 64; - char str[size]; - snprintf(str, size, "Turn %d", turn_counter_); - dialog_->SetAttribute("Title", (const char *)str); - dialog_->AddView(layout); - dialog_->AddView(button); - dialog_->Show(); - LOGI("Showing dialog"); - }); -} - -/* - * Manage games UI when finished/their turn game is selected in match inbox UI - */ -void Engine::ManageGame(gpg::TurnBasedMatch const &match, const bool leave, - const bool cancel, const bool rematch) { - current_match_ = match; - ParseMatchData(); - - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread( - [this, leave, cancel, rematch]() { - LOGI("Managing match"); - if (dialog_) - delete dialog_; - dialog_ = new jui_helper::JUIDialog(app_->activity); - - jui_helper::JUIButton *currentButton; - // Take Turn Button - jui_helper::JUIButton *button = new jui_helper::JUIButton("Dismiss"); - button->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - DismissMatch(); - dialog_->Close(); - } - }); - button->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_TOP, - jui_helper::LAYOUT_PARAMETER_TRUE); - button->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - dialog_->AddView(button); - currentButton = button; - - if (leave) { - LOGI("Adding leave button"); - // Leave Button - jui_helper::JUIButton *leaveButton = new jui_helper::JUIButton("Leave"); - leaveButton->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - LeaveMatch(); - dialog_->Close(); - } - }); - leaveButton->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - leaveButton->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, currentButton); - dialog_->AddView(leaveButton); - currentButton = leaveButton; - } - - if (cancel) { - LOGI("Adding cancel button"); - // Cancel Button - jui_helper::JUIButton *cancelButton = new jui_helper::JUIButton("Cancel"); - cancelButton->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - CancelMatch(); - dialog_->Close(); - } - }); - cancelButton->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - cancelButton->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, currentButton); - dialog_->AddView(cancelButton); - currentButton = cancelButton; - } - - if (rematch) { - LOGI("Adding rematch button"); - // Leave Button - jui_helper::JUIButton *rematchButton = new jui_helper::JUIButton("Rematch"); - rematchButton->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - Rematch(); - dialog_->Close(); - } - }); - rematchButton->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - rematchButton->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, currentButton); - dialog_->AddView(rematchButton); - } - - dialog_->SetCallback( - jui_helper::JUICALLBACK_DIALOG_CANCELLED, - [this](jui_helper::JUIDialog * dialog, const int32_t message) { - LOGI("Dialog cancelled"); - dialog_->Close(); - }); - dialog_->SetCallback( - jui_helper::JUICALLBACK_DIALOG_DISMISSED, - [this](jui_helper::JUIDialog * dialog, const int32_t message) { - LOGI("Dialog dismissed"); - dialog_->Close(); - }); - - size_t size = 64; - char str[size]; - snprintf(str, size, "Turn %d", turn_counter_); - dialog_->SetAttribute("Title", (const char *)str); - dialog_->Show(); - LOGI("Showing dialog"); - }); -} - -/* - * Parse JSON match data - */ -void Engine::ParseMatchData() { - LOGI("Parsing match data %d", static_cast(current_match_.Data().size())); - turn_counter_ = 1; - if (!current_match_.HasData() || current_match_.Data().size() == 0) { - LOGI("Game data not found"); - return; - } - - //UTF16 to UTF8 conversion - //Removing BOM for first 2 bytes - std::u16string source; - auto it = current_match_.Data().begin(); - auto end = current_match_.Data().end(); - //Skip BOM - it++; - it++; - while (it != end) { - uint16_t i = *it++; - i |= (*it++) << 8; - source.push_back(i); - } - - std::wstring_convert, char16_t> convert; - std::string str = convert.to_bytes(source); - - Json::Value root; - Json::Reader reader; - bool parsingSuccessful = reader.parse(str, root); - if (!parsingSuccessful) { - LOGI("Data parse error"); - return; - } - - turn_counter_ = root[TurnCounterKey].asInt(); - LOGI("Got turn counter %d", turn_counter_); -} - -/* - * Create JSON data to store - */ -std::vector Engine::SetupMatchData() { - Json::StyledWriter writer; - Json::Value root; - root[TurnCounterKey] = turn_counter_; - root[StringToPassKey] = "test"; - std::string source = writer.write(root); - - //Converting back from UTF8 to UTF16 - std::wstring_convert, char16_t> convert; - std::u16string dest = convert.from_bytes(source); - - std::vector v; - v.push_back(0xff); //Adding BOM - v.push_back(0xfe); //Adding BOM - auto it = dest.begin(); - auto end = dest.end(); - while (it != end) { - uint16_t i = *it++; - v.push_back( (unsigned char)(i & 0x0ff)); - v.push_back((unsigned char) i >> 8); - } - - LOGI("Created Game Data: size: %d", static_cast(v.size())); - return v; -} - -/* - * Initialize main sample UI, - * invoking jui_helper functions to create java UIs - */ -void Engine::InitUI() { - const int32_t buttonWidth = 600; - // Show toast with app label - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([]() { - jui_helper::JUIToast toast( - ndk_helper::JNIHelper::GetInstance()->GetAppLabel()); - toast.Show(); - }); - - // The window initialization - jui_helper::JUIWindow::Init(app_->activity, JUIHELPER_CLASS_NAME); - - // - // Buttons - // - // Sign in button. - button_sign_in_ = new jui_helper::JUIButton( - service_->IsAuthorized() ? "Sign Out" : "Sign In"); - button_sign_in_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button_sign_in_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button_sign_in_->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - LOGI("button_sign_in_ click: %d", message); - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - if (service_->IsAuthorized()) { - service_->SignOut(); - } else { - service_->StartAuthorizationUI(); - } - } - }); - button_sign_in_->SetAttribute("MinimumWidth", buttonWidth); - button_sign_in_->SetMargins(0, 0, 0, 50); - - jui_helper::JUIWindow::GetInstance()->AddView(button_sign_in_); - - button_quick_match_ = new jui_helper::JUIButton("Quick match"); - button_quick_match_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, - button_sign_in_); - button_quick_match_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button_quick_match_->SetAttribute("MinimumWidth", buttonWidth); - button_quick_match_->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - QuickMatch(); - } - }); - jui_helper::JUIWindow::GetInstance()->AddView(button_quick_match_); - - button_invite_ = new jui_helper::JUIButton("Invite Friends!"); - button_invite_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, - button_quick_match_); - button_invite_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button_invite_->SetAttribute("MinimumWidth", buttonWidth); - button_invite_->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - InviteFriend(); - } - }); - jui_helper::JUIWindow::GetInstance()->AddView(button_invite_); - - button_matches_ = new jui_helper::JUIButton("All my matches"); - button_matches_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, button_invite_); - button_matches_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - button_matches_->SetAttribute("MinimumWidth", buttonWidth); - button_matches_->SetCallback( - [this](jui_helper::JUIView * view, const int32_t message) { - if (message == jui_helper::JUICALLBACK_BUTTON_UP) { - ShowMatchInbox(); - } - }); - jui_helper::JUIWindow::GetInstance()->AddView(button_matches_); - - status_text_ = new jui_helper::JUITextView( - service_->IsAuthorized() ? "Signed In." : "Signed Out."); - - status_text_->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_BOTTOM, - jui_helper::LAYOUT_PARAMETER_TRUE); - status_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT, - jui_helper::LAYOUT_PARAMETER_TRUE); - jui_helper::JUIWindow::GetInstance()->AddView(status_text_); - - textViewFPS_ = new jui_helper::JUITextView("0.0FPS"); - textViewFPS_->SetAttribute("Gravity", jui_helper::ATTRIBUTE_GRAVITY_LEFT); - textViewFPS_->SetAttribute("TextColor", 0xffffffff); - textViewFPS_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, 18.f); - textViewFPS_->SetAttribute("Padding", 10, 10, 10, 10); - jui_helper::JUIWindow::GetInstance()->AddView(textViewFPS_); - - if (authorizing_) - EnableUI(false); - return; -} - -void Engine::EnableUI(bool enable) { - LOGI("Updating UI:%d", enable); - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, enable]() { - button_sign_in_->SetAttribute("Enabled", enable); - - bool b = enable; - if (service_->IsAuthorized() == false) - b = false; - button_quick_match_->SetAttribute("Enabled", b); - button_invite_->SetAttribute("Enabled", b); - button_matches_->SetAttribute("Enabled", b); - }); -} - -/* - * JNI functions those manage activity lifecycle - */ -extern "C" { -/* - * These callbacks are necessary to work Google Play Game Services UIs properly - * - * For apps which target Android 2.3 or 3.x devices (API Version prior to 14), - * Play Game Services has no way to automatically receive Activity lifecycle - * callbacks. In these cases, Play Game Services relies on the owning Activity - * to notify it of lifecycle events. Any Activity which owns a GameServices - * object should call the AndroidSupport::* functions from within their own - * lifecycle callback functions. The arguments in these functions match those - * provided by Android, so no additional processing is necessary. - */ -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivityResult( - JNIEnv *env, jobject thiz, jobject activity, jint requestCode, - jint resultCode, jobject data) { - gpg::AndroidSupport::OnActivityResult(env, activity, requestCode, resultCode, - data); -} - -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivityCreated( - JNIEnv *env, jobject thiz, jobject activity, jobject saved_instance_state) { - gpg::AndroidSupport::OnActivityCreated(env, activity, saved_instance_state); -} - -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivityDestroyed( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityDestroyed(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivityPaused( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityPaused(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivityResumed( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityResumed(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivitySaveInstanceState( - JNIEnv *env, jobject thiz, jobject activity, jobject out_state) { - gpg::AndroidSupport::OnActivitySaveInstanceState(env, activity, out_state); -} - -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivityStarted( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityStarted(env, activity); -} - -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_nativeOnActivityStopped( - JNIEnv *env, jobject thiz, jobject activity) { - gpg::AndroidSupport::OnActivityStopped(env, activity); -} - -} diff --git a/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.h b/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.h deleted file mode 100644 index 6c10779..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TBMPSLEKETONNATIVIACTIVITY_H_ -#define TBMPSLEKETONNATIVIACTIVITY_H_ - -/* - * Include files - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -// For GPGS -#include "gpg/gpg.h" - -// For JSON parser -#include "json/json.h" - -#include "TeapotRenderer.h" -#include "NDKHelper.h" -#include "JavaUI.h" - -/* - * Preprocessors - */ - -// Class name of helper function -#define HELPER_CLASS_NAME "com.sample.helper.NDKHelper" -// Class name of JUIhelper function -#define JUIHELPER_CLASS_NAME "com.sample.helper.JUIHelper" -// Share object name of helper function library -#define HELPER_CLASS_SONAME "TBMPSkeletonNativeActivity" - -// -// Also set "com.google.android.gms.games.APP_ID" in AndrdoiManifest.xml -// - -const int32_t MIN_PLAYERS = 1; -const int32_t MAX_PLAYERS = 3; - -/* - * Engine class of the sample - */ -struct android_app; -class Engine { -public: - // GPG related methods - void InitGooglePlayGameServices(); - void InviteFriend(); - void ShowMatchInbox(); - void QuickMatch(); - void PlayGame(gpg::TurnBasedMatch const &match); - void TakeTurn(const bool winning, const bool losing); - void LeaveMatch(); - void CancelMatch(); - void DismissMatch(); - void Rematch(); - - // Event handling - static void HandleCmd(struct android_app *app, int32_t cmd); - static int32_t HandleInput(android_app *app, AInputEvent *event); - - // Engine life cycles - Engine(); - ~Engine(); - void SetState(android_app *state); - int InitDisplay(const int32_t cmd); - void LoadResources(); - void UnloadResources(); - void DrawFrame(); - void TermDisplay(const int32_t cmd); - void TrimMemory(); - bool IsReady(); - - // Sensor managers - void ProcessSensors(const int32_t id) { sensroManager_.Process(id); } - void ResumeSensors() { sensroManager_.Resume(); } - void SuspendSensors() { sensroManager_.Suspend(); } - -private: - // Callbacks from GPG. - void OnAuthActionStarted(gpg::AuthOperation op); - void OnAuthActionFinished(gpg::AuthOperation op, gpg::AuthStatus status); - - void ParseMatchData(); - std::vector SetupMatchData(); - void ManageGame(gpg::TurnBasedMatch const &match, const bool leave, - const bool cancel, const bool rematch); - - std::unique_ptr service_; - gpg::TurnBasedMatch current_match_; - int32_t turn_counter_; - - bool authorizing_; - // - void EnableUI(bool enable); - void InitUI(); - void TransformPosition(ndk_helper::Vec2 &vec); - void UpdateFPS(float fFPS); - - // Renderer of a teapot - TeapotRenderer renderer_; - - // GLContext instance - ndk_helper::GLContext *gl_context_; - - // Sensor manager - ndk_helper::SensorManager sensroManager_; - - bool initialized_resources_; - bool has_focus_; - - // Helpers for touch control - ndk_helper::DoubletapDetector doubletap_detector_; - ndk_helper::PinchDetector pinch_detector_; - ndk_helper::DragDetector drag_detector_; - ndk_helper::PerfMonitor monitor_; - ndk_helper::TapCamera tap_camera_; - - // JUI text view to show FPS - jui_helper::JUITextView *textViewFPS_; - - // Native activity app instance - android_app *app_; - - // JUI dialog - jui_helper::JUIDialog *dialog_; - - jui_helper::JUIButton *button_sign_in_; - jui_helper::JUIButton *button_quick_match_; - jui_helper::JUIButton *button_invite_; - jui_helper::JUIButton *button_matches_; - - jui_helper::JUITextView *status_text_; - - jui_helper::JUICheckBox *checkBoxWinning_; - jui_helper::JUICheckBox *checkBoxLosing_; - jui_helper::JUICheckBox *checkBoxQuit_; -}; - -#endif //TBMPSLEKETONNATIVIACTIVITY_H_ diff --git a/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity_Engine.cpp b/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity_Engine.cpp deleted file mode 100644 index 0ca9a3a..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/cpp/TBMPSkeletonNativeActivity_Engine.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Include files - */ -#include "TBMPSkeletonNativeActivity.h" - -/* - * Ctor - */ -Engine::Engine() - : authorizing_(false), initialized_resources_(false), has_focus_(false), - textViewFPS_(nullptr), app_(nullptr), dialog_(nullptr), - button_sign_in_(nullptr), button_invite_(nullptr), status_text_(nullptr) { - gl_context_ = ndk_helper::GLContext::GetInstance(); -} - -/* - * Dtor - */ -Engine::~Engine() { - jui_helper::JUIWindow::GetInstance()->Close(); - delete dialog_; -} - -/** - * Load resources - */ -void Engine::LoadResources() { - renderer_.Init(); - renderer_.Bind(&tap_camera_); -} - -/** - * Unload resources - */ -void Engine::UnloadResources() { renderer_.Unload(); } - -/** - * Initialize an EGL context for the current display. - */ -int Engine::InitDisplay(const int32_t cmd) { - if (!initialized_resources_) { - gl_context_->Init(app_->window); - InitUI(); - LoadResources(); - initialized_resources_ = true; - } else { - // initialize OpenGL ES and EGL - if (EGL_SUCCESS != gl_context_->Resume(app_->window)) { - UnloadResources(); - LoadResources(); - } - - jui_helper::JUIWindow::GetInstance()->Resume(app_->activity, cmd); - } - - // Enable culling OpenGL state - glEnable(GL_CULL_FACE); - - // Enabled depth test OpenGL state - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - - // Note that screen size might have been changed - glViewport(0, 0, gl_context_->GetScreenWidth(), - gl_context_->GetScreenHeight()); - renderer_.UpdateViewport(); - - tap_camera_.SetFlip(1.f, -1.f, -1.f); - tap_camera_.SetPinchTransformFactor(2.f, 2.f, 8.f); - - return 0; -} - -/** - * Just the current frame in the display. - */ -void Engine::DrawFrame() { - float fFPS; - if (monitor_.Update(fFPS)) { - UpdateFPS(fFPS); - } - renderer_.Update(monitor_.GetCurrentTime()); - - // Just fill the screen with a color. - glClearColor(0.5f, 0.5f, 0.5f, 1.f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - renderer_.Render(); - - // Swap - if (EGL_SUCCESS != gl_context_->Swap()) { - UnloadResources(); - LoadResources(); - } -} - -/** - * Tear down the EGL context currently associated with the display. - */ -void Engine::TermDisplay(const int32_t cmd) { - gl_context_->Suspend(); - jui_helper::JUIWindow::GetInstance()->Suspend(cmd); -} - -void Engine::TrimMemory() { - LOGI("Trimming memory"); - gl_context_->Invalidate(); -} - -/** - * Process the next input event. - * In the handler, it passes touch events to gesture detector helper class - * Based on gesture detection, the app moves camera, model view accordingly - */ -int32_t Engine::HandleInput(android_app *app, AInputEvent *event) { - Engine *eng = (Engine *)app->userData; - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { - ndk_helper::GESTURE_STATE doubleTapState = - eng->doubletap_detector_.Detect(event); - ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect(event); - ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect(event); - - // Double tap detector has a priority over other detectors - if (doubleTapState == ndk_helper::GESTURE_STATE_ACTION) { - // Detect double tap - eng->tap_camera_.Reset(true); - } else { - // Handle drag state - if (dragState & ndk_helper::GESTURE_STATE_START) { - // Otherwise, start dragging - ndk_helper::Vec2 v; - eng->drag_detector_.GetPointer(v); - eng->TransformPosition(v); - eng->tap_camera_.BeginDrag(v); - } else if (dragState & ndk_helper::GESTURE_STATE_MOVE) { - ndk_helper::Vec2 v; - eng->drag_detector_.GetPointer(v); - eng->TransformPosition(v); - eng->tap_camera_.Drag(v); - } else if (dragState & ndk_helper::GESTURE_STATE_END) { - eng->tap_camera_.EndDrag(); - } - - // Handle pinch state - if (pinchState & ndk_helper::GESTURE_STATE_START) { - // Start new pinch - ndk_helper::Vec2 v1; - ndk_helper::Vec2 v2; - eng->pinch_detector_.GetPointers(v1, v2); - eng->TransformPosition(v1); - eng->TransformPosition(v2); - eng->tap_camera_.BeginPinch(v1, v2); - } else if (pinchState & ndk_helper::GESTURE_STATE_MOVE) { - // Multi touch - // Start new pinch - ndk_helper::Vec2 v1; - ndk_helper::Vec2 v2; - eng->pinch_detector_.GetPointers(v1, v2); - eng->TransformPosition(v1); - eng->TransformPosition(v2); - eng->tap_camera_.Pinch(v1, v2); - } - } - return 1; - } - return 0; -} - -/** - * Process the next main command. - */ -void Engine::HandleCmd(struct android_app *app, int32_t cmd) { - Engine *eng = (Engine *)app->userData; - switch (cmd) { - case APP_CMD_SAVE_STATE: - break; - case APP_CMD_INIT_WINDOW: - if (app->window != NULL) { - eng->InitDisplay(APP_CMD_INIT_WINDOW); - eng->DrawFrame(); - } - - break; - case APP_CMD_TERM_WINDOW: - // Note that JUI helper needs to know if a window has been terminated - eng->TermDisplay(APP_CMD_TERM_WINDOW); - - eng->has_focus_ = false; - break; - case APP_CMD_START: - break; - case APP_CMD_STOP: - break; - case APP_CMD_RESUME: - jui_helper::JUIWindow::GetInstance()->Resume(app->activity, APP_CMD_RESUME); - break; - case APP_CMD_GAINED_FOCUS: - // Start animation - eng->ResumeSensors(); - eng->has_focus_ = true; - jui_helper::JUIWindow::GetInstance()->Resume(app->activity, - APP_CMD_GAINED_FOCUS); - break; - case APP_CMD_LOST_FOCUS: - // Also stop animating. - eng->SuspendSensors(); - eng->has_focus_ = false; - eng->DrawFrame(); - break; - case APP_CMD_LOW_MEMORY: - // Free up GL resources - eng->TrimMemory(); - break; - case APP_CMD_CONFIG_CHANGED: - // Configuration changes - eng->TermDisplay(APP_CMD_CONFIG_CHANGED); - eng->InitDisplay(APP_CMD_CONFIG_CHANGED); - break; - case APP_CMD_DESTROY: - ndk_helper::JNIHelper::GetInstance()->DetachCurrentThread(); - break; - } -} - -/* - * Misc - */ -void Engine::SetState(android_app *state) { - app_ = state; - doubletap_detector_.SetConfiguration(app_->config); - drag_detector_.SetConfiguration(app_->config); - pinch_detector_.SetConfiguration(app_->config); - sensroManager_.Init(state); -} - -bool Engine::IsReady() { - return has_focus_; - -} - -void Engine::TransformPosition(ndk_helper::Vec2 &vec) { - vec = ndk_helper::Vec2(2.0f, 2.0f) * vec / - ndk_helper::Vec2(gl_context_->GetScreenWidth(), - gl_context_->GetScreenHeight()) - - ndk_helper::Vec2(1.f, 1.f); -} - -void Engine::UpdateFPS(float fFPS) { - ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([fFPS, this]() { - const int32_t count = 64; - char str[count]; - snprintf(str, count, "%2.2f FPS", fFPS); - textViewFPS_->SetAttribute("Text", (const char *)str); - }); - - return; -} - -Engine g_engine; - -/** - * This is the main entry point of a native application that is using - * android_native_app_glue. It runs in its own thread, with its own - * event loop for receiving input events and doing other things. - */ -void android_main(android_app *state) { - g_engine.SetState(state); - - // Init helper functions - ndk_helper::JNIHelper::Init(state->activity, HELPER_CLASS_NAME, - HELPER_CLASS_SONAME); - - // Init play game services - g_engine.InitGooglePlayGameServices(); - - state->userData = &g_engine; - state->onAppCmd = Engine::HandleCmd; - state->onInputEvent = Engine::HandleInput; - - // loop waiting for stuff to do. - while (1) { - // Read all pending events. - int id; - int events; - android_poll_source *source; - - // If not animating, we will block forever waiting for events. - // If animating, we loop until all events are read, then continue - // to draw the next frame of animation. - while ((id = ALooper_pollAll(g_engine.IsReady() ? 0 : -1, NULL, &events, - (void **)&source)) >= 0) { - // Process this event. - if (source != NULL) - source->process(state, source); - - g_engine.ProcessSensors(id); - - // Check if we are exiting. - if (state->destroyRequested != 0) { - g_engine.TermDisplay(APP_CMD_TERM_WINDOW); - return; - } - } - - if (g_engine.IsReady()) { - // Drawing is throttled to the screen update rate, so there - // is no need to do timing here. - g_engine.DrawFrame(); - } - } -} - -extern "C" { -JNIEXPORT void -Java_com_google_example_games_tbmpskel_TBMPSkeletonNativeActivity_OnPauseHandler( - JNIEnv *env) { - // This call is to suppress 'E/WindowManager(): android.view.WindowLeaked...' - // errors. - // Since orientation change events in NativeActivity comes later than - // expected, we can not dismiss - // popupWindow gracefully from NativeActivity. - // So we are releasing popupWindows explicitly triggered from Java callback - // through JNI call. - jui_helper::JUIWindow::GetInstance()->Suspend(APP_CMD_PAUSE); -} -} diff --git a/samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonApplication.java b/samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonApplication.java deleted file mode 100644 index a64881b..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonApplication.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.example.games.tbmpskel; - -import android.app.Application; - -public class TBMPSkeletonApplication extends Application { - public void onCreate() { - super.onCreate(); - } -} diff --git a/samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonNativeActivity.java b/samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonNativeActivity.java deleted file mode 100644 index e4a9eca..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/java/com/google/example/games/tbmpskel/TBMPSkeletonNativeActivity.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.example.games.tbmpskel; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.NativeActivity; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; - -public class TBMPSkeletonNativeActivity extends NativeActivity { - // Load SO - static { - System.loadLibrary("TBMPSkeletonNativeActivity"); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // Hide toolbar - int SDK_INT = android.os.Build.VERSION.SDK_INT; - if (SDK_INT >= 19) { - setImmersiveSticky(); - - View decorView = getWindow().getDecorView(); - decorView - .setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { - @Override - public void onSystemUiVisibilityChange(int visibility) { - setImmersiveSticky(); - } - }); - } - - nativeOnActivityCreated(this, savedInstanceState); - } - - @SuppressLint("InlinedApi") - @SuppressWarnings("deprecation") - protected void onResume() { - super.onResume(); - - // Hide toolbar - int SDK_INT = android.os.Build.VERSION.SDK_INT; - if (SDK_INT >= 11 && SDK_INT < 14) { - getWindow().getDecorView().setSystemUiVisibility( - View.STATUS_BAR_HIDDEN); - } else if (SDK_INT >= 14 && SDK_INT < 19) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_LOW_PROFILE); - } else if (SDK_INT >= 19) { - setImmersiveSticky(); - } - - nativeOnActivityResumed(this); - } - - @SuppressLint("InlinedApi") - void setImmersiveSticky() { - View decorView = getWindow().getDecorView(); - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - } - - protected void onPause() { - super.onPause(); - // This call is to suppress 'E/WindowManager(): - // android.view.WindowLeaked...' errors. - // Since orientation change events in NativeActivity comes later than - // expected, we can not dismiss - // popupWindow gracefully from NativeActivity. - // So we are releasing popupWindows explicitly triggered from Java - // callback through JNI call. - OnPauseHandler(); - - nativeOnActivityPaused(this); - } - - protected void onDestroy() { - super.onDestroy(); - nativeOnActivityDestroyed(this); - } - - protected void onStart() { - super.onStart(); - nativeOnActivityStarted(this); - } - - protected void onStop() { - super.onStop(); - nativeOnActivityStopped(this); - } - - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - nativeOnActivitySaveInstanceState(this, outState); - } - - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - nativeOnActivityResult(this, requestCode,resultCode, data); - } - - // Implemented in C++. - public static native void nativeOnActivityResult(Activity activity, - int requestCode, int resultCode, Intent data); - - public static native void nativeOnActivityCreated(Activity activity, - Bundle savedInstanceState); - - private static native void nativeOnActivityDestroyed(Activity activity); - - private static native void nativeOnActivityPaused(Activity activity); - - private static native void nativeOnActivityResumed(Activity activity); - - private static native void nativeOnActivitySaveInstanceState( - Activity activity, Bundle outState); - - private static native void nativeOnActivityStarted(Activity activity); - - private static native void nativeOnActivityStopped(Activity activity); - - native public void OnPauseHandler(); -} diff --git a/samples-android/TbmpSkeletonNative/src/main/res/drawable-hdpi/ic_launcher.png b/samples-android/TbmpSkeletonNative/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index ea01cbf83706f5ddf4016b7a20d15bf5ff00fa72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8203 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 S+rjPt0000GmlA diff --git a/samples-android/TbmpSkeletonNative/src/main/res/drawable-mdpi/ic_launcher.png b/samples-android/TbmpSkeletonNative/src/main/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 8f808970c1991083869d91081f3fda8e33152203..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4064 zcmV<64noX1jR0Kg0R94w!5kYojk%n%XeUWBgWfu_H zL2*NI$GAp=i5gARXpC`-h!TXp|L?wMtk2|gsx(!R`ljYmr{3#+ue;Chp8q-b+-_cb z?Z4yy@i=kC|NZ^fofmd?TD|qMua!!rfBJl2wP@?t)@sc8m;jV)6RTDqHn~`L%-H5x zC!~=6sTaMkKQS?TWUfDZ&*^*aAGd#LbyilTwQ|e=m=DfuGw`kHGrmyWEB@sAb?I9p zD4sTiY{y5)N*P3E*#J^E+(UB4EhKte8%^S#x~F)jzhC;suJ3G1(whyox`pe!H>NU@ zmYI!HbQrX?p0a3rh>ZF{qNJL+z$hwM`vH+ShZQ(Y>~M708VBxM zkUh68Mk@{)9j)y#9Y3Z3mk~DpKGL?{M5OXI;&2nu7)CL*4S0k?3?dKLuof4v3TKEb zjt(uuzPnb~dEE@{7mP{mu{lS|b{S3{6Nvstfgk*Ugw`tvBG;(nm#_xk(c@J_Pz%Ks zgt2xBLHG)*@%e~5jtn|r?=5R=ySe~v=S@+6ayBX2t#?q-Civ$ogvq_F*#=!-TcPFb z0_^z84+D>Gpnbm#dD}$DXcm#^0&deQ0qDGL^;Y*K+lyPjT}ZkvnWF2`Tx_{ugU$<< z=>FCUJFZ$|=T#eev}K*{{$>evd=c8evPAQl1!y>Fikc&0RP3FP>`nvZbQvI{$p8`g zoV#A@rvI_Iz`S|$@kPSTXXX!W{z8nVeleQQ8q--dr8XCBU(xDv&YYSBb$B6tY=PE( zb2L%MH=UYCHE)XQJ|mRxpN*29S;*U}hxKi`NUqmGSPtj$iZB(cKU_WypDkNj-6wur z^O+$w9-oc+6LaXS8lmBoF?G@ejh~yM@zh*;K9@c}7xl+YspBSe){RklXbv{)H$=&9 z0~Bo6qY==d`qx5ywE#hxGstqkNzqSR4yOI+@9%%(cSnSI;oaf%3AYeG`{HPne&3DA z51Hp`_O4h~+GBjDbgv#N4jG{8@GLs3hNwO=n>sm%nh|P0Gh$^zLFsiPR34ne&U)G2 zU!32ehup0?$Z8iMwNVSwasfgzI0KGBs$re99l55IF8C4grDsPxYI|;2`P847_x?*e ze{nQYd}$=&^yN|MP~Tmr!M3xO`!YHV)!X}>$7j?o-m)f64DWS9tVz^BWUeOSD?~`H z)1?V%fULGz$muksGfb_+5Q;WKR_7`8`t&{>L>FiwC{=)!QCv9sYrx)1AIshJ`WCyW ztJWSix2B1I@9d)x(tFdEZ2H`2II~47!>v-~%@+pP-tReX^XY{G??J+@oxdB$iaj?{9D&{Yn1Vi&rpkq7%f?0nDPwyjeM1O<2 z_J>dxeF(1Z-@rHc2%2;L4&8aifa{2g!7Y3-`>F|C|yZBgovyfyoYawNm7Gg?x#65ayLw1XKXu%GV zvV6Y*YLCvLS~o`PSu=E;x5SpOt?jQYtkfn-TLhHq&iKqTCU!|z<$DqFAPw* z#qB*soAHsY}(u55>FnslN4D zjWbrO-YhDWsCp#F}I-S>iP@J0MlPCUujL zWqmcN-{Z4?C20lq?5F(*gh{ysiaS2y#Pb{|xNUGCNX0~b}yGw+;ZXM)r z*L`B!o>(q*w04gZPF-rj(et$^-(kx-%{Y|VDq@{arBO|&)uahb-#br8CBz|%S5C_I zPg1W*ZV(b?_Sd)SAfZM`R>i5GwDJ1aUtbuY<&f{YSuMr~lj`{-sZIzvEpw3t8gL1l zx?NUk5h$PJ<+F6wojW5Ru1&bI6n((qfBMvMQc3ZL=m#Fd79aB}NvlheB}i`vVw$ zJjgo!+d&Ayqo04m7vF3{^>$Ax4O;)JgorH@U~LW`zH&9P*h3}7KatZ+Wy3~{wixst z0r8foA6@9C^5Q$UvhAMZ6s@Mc3AH>Tui-;l!N=NME^!W3?U5BPSQ%Hr8%^0H1TAkY zL|(^S)U_(`U~qtS^xqBU+TDA%kzFEzykZt)RR5AvIypI-2uRYx;*~1NzVT|!iBz(T zd}xygS%m;zF*6T|9cTRIg#p@51-+waHr>ep#8>kXSI$Q`1y~WPdO%ue<{4Yg8%?ek zfRRyJGaqNZX-D6wEs$g^NB8bp3_kod2y=e_{#`V-7hr9IEsmVng>455At}>9OfdtX zO#yVU$ejYnXSB&{1juO7LE2^!nGDjt=O1`RV0p$ZBRJmugzg|UmIj5nH4D*z*-ltw;IT-f}-vrYf*-oK8z z&P2p#c_1dm1q~gUxHoX~S5ay2$)|b6MN|QmXSNRf<=R;6t`h6V08|Os*=LeHmL|DZ z41f2_12oTXlT~u*4Dgsm3?W(S#5qLuv&i)npoBLfr_y3%l)OpfR{&4PsB#o-Jz3a& zq#899lvd0A2XWX%4um#QMr+ZkJW^+1XF7rvRlwct;DjiN)jJe(`nS zTemkAP4nyH$~h!<0~2O0f-+_j$KWZSty9c(i7DoeFw#n@6CkPn`2d6{sZ5G+`17Xc zBsM`qR~o_;99kSSuvVdm)iNO#xlW1mk{?SWp{F0VJhd zW)=PgVDb}yA_`EcfkY~!#5x|1Uub-8#p&!R!l6^m{{|rS2|$hpA{6@Yl?q_(p&IQg zQ`^f_y_5!Rw$!dGW&ku>R8(G~nd|NCeLbt$tR%LKL+ETHGM|Hhv>C)9aMIC;^>ZDg zi@A@P+(@7$Hen+V$IdtYA1B!eumDZZy~Y59W^pJ450Q#F@RezjMebA9J_TUplTfQg z6I~Nt(V_+phx5iu0j%F-ridx!kmw>N%p9yqnL+FVRQe=23+!q2cpO)*0VWR;Hu6!? z<%{pGUt%45HUb^b0bugs(&aB9S#OK5EG{)3q7=pmNH8J`ohFC+#Br)5r9zUlQHR7= zX_Kg29i?xKQPN9oY?zy4b4|>HQZ?=brV5B}Jcqc1stsAWt3>2AcrS`CGh_kct7#N! z`83Hn@Rw<0V`~O(-?_m$tfVj!dmRYv{Q za>kXk15VlgSS3`K35juC4;}Km0I(8MZ01pO#LPJ|FR!a;( z>pTKeIZXS^0)%E#fGiV)$SsufmrwFwCeK%D@UFDbW=c$+<>b)Jmsr4_7YVUg{5oS# z_p14u{H7;WSUqIc25AM4jRLb;GdU5r;TMohb%ZC+M{1Efva42ORg@9yIHUInrs~kd z4bSGWk3%RxM5Za$#4Z>%v;Rc=hkv!gd_at1QDJbV#%NRlk1lf5@$#oe+l~!<`WzqY z3i$}m;LxaWsgrcunI}MWp^%njrvHD^frhQ;)2*d8LX*siYp`awmGl*TI~S2^^H)Ea zR;h2i`aoEwK4S94h$%FrM$e0MAt`1|kD5MWOAP68HsVUfh$}IHq{sy8@{AF_-iY=; z`ec<<^l)Vqztt&d`afUkm%D!X;}wKbhfS|76`oQYmNLnEW~;&3Cl0^H&{N;5Y$h z4xhlvbqZ{~X2Q{5pbX0}D~iptDv}i~E%le0-f|5U!8uS9%YC_6`kUTW>}6oVfvcjbPagNa>%mP zYM00V(*80Iu9E0hd&W#x`X|w@HjVCoQ`l|d&-VV>i^dedta Sl%PTY0000gb)(kA!u-y7T4nL1PRHU^ZcLp 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 diff --git a/samples-android/TbmpSkeletonNative/src/main/res/layout/widgets.xml b/samples-android/TbmpSkeletonNative/src/main/res/layout/widgets.xml deleted file mode 100644 index c689a52..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/res/layout/widgets.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/samples-android/TbmpSkeletonNative/src/main/res/values/ids.xml b/samples-android/TbmpSkeletonNative/src/main/res/values/ids.xml deleted file mode 100644 index b2fc703..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/res/values/ids.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - REPLACE_ME - - diff --git a/samples-android/TbmpSkeletonNative/src/main/res/values/strings.xml b/samples-android/TbmpSkeletonNative/src/main/res/values/strings.xml deleted file mode 100644 index 6a7c191..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - TBMPSkeleton - - diff --git a/samples-android/TbmpSkeletonNative/src/main/res/values/styles.xml b/samples-android/TbmpSkeletonNative/src/main/res/values/styles.xml deleted file mode 100644 index 4a10ca4..0000000 --- a/samples-android/TbmpSkeletonNative/src/main/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/samples-android/build.gradle b/samples-android/build.gradle index f788858..9a7e39d 100644 --- a/samples-android/build.gradle +++ b/samples-android/build.gradle @@ -21,7 +21,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.5.1' } } diff --git a/samples-android/gradle/wrapper/gradle-wrapper.properties b/samples-android/gradle/wrapper/gradle-wrapper.properties index 1e53d6d..6e515af 100644 --- a/samples-android/gradle/wrapper/gradle-wrapper.properties +++ b/samples-android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Nov 14 14:20:53 PST 2017 +#Mon Aug 03 01:36:35 BST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/samples-android/settings.gradle b/samples-android/settings.gradle index 361c872..36c7dd2 100644 --- a/samples-android/settings.gradle +++ b/samples-android/settings.gradle @@ -1,9 +1,7 @@ include ':Common:gpg-sdk', ':Common:JuiHelper', ':Common:NDKHelper', - ':ButtonClicker', ':CollectAllTheStarsNative', ':Minimalist', - ':TbmpSkeletonNative', ':Teapot'