20 |
21 | #include "paddleboat.h"
22 |
23 | namespace paddleboat {
24 | const Paddleboat_Controller_Mapping_Data *GetInternalControllerData();
25 |
26 | int32_t GetInternalControllerDataCount();
27 | } // namespace paddleboat
--------------------------------------------------------------------------------
/GameController/src/main/java/com/google/android/games/paddleboat/GuardedBy.java:
--------------------------------------------------------------------------------
1 | package com.google.android.games.paddleboat;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Denotes that the annotated method or field can only be accessed when holding the referenced lock.
10 | *
11 | * Example:
12 | *
13 | * final Object objectLock = new Object();
14 | *
15 | * {@literal @}GuardedBy("objectLock")
16 | * volatile Object object;
17 | *
18 | * Object getObject() {
19 | * synchronized (objectLock) {
20 | * if (object == null) {
21 | * object = new Object();
22 | * }
23 | * }
24 | * return object;
25 | * }
26 | */
27 | @Target({ElementType.FIELD, ElementType.METHOD})
28 | @Retention(RetentionPolicy.CLASS)
29 | public @interface GuardedBy {
30 | String value();
31 | }
32 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/Settings.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.google.androidgamesdk.gametextinput;
17 |
18 | import android.view.inputmethod.EditorInfo;
19 |
20 | // Settings for InputConnection
21 | public final class Settings {
22 | EditorInfo mEditorInfo;
23 | boolean mForwardKeyEvents;
24 |
25 | public Settings(EditorInfo editorInfo, boolean forwardKeyEvents) {
26 | mEditorInfo = editorInfo;
27 | mForwardKeyEvents = forwardKeyEvents;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/GameActivity/prefab-src/modules/game-activity/include/game-activity/system_utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include "string"
20 |
21 | namespace gamesdk {
22 |
23 | // Get the value of the given system property
24 | std::string GetSystemProp(const char* key, const char* default_value = "");
25 |
26 | // Get the value of the given system property as an integer
27 | int GetSystemPropAsInt(const char* key, int default_value = 0);
28 |
29 | // Get the value of the given system property as a bool
30 | bool GetSystemPropAsBool(const char* key, bool default_value = false);
31 |
32 | } // namespace gamesdk
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerInternalConstants.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | namespace paddleboat {
22 | inline constexpr size_t MAX_AXIS_COUNT = 48;
23 | // Must match GameControllerManager.DEVICEINFO_ARRAY_SIZE
24 | inline constexpr size_t DEVICEINFO_ARRAY_SIZE = 7;
25 | inline constexpr size_t DEVICEINFO_ARRAY_BYTESIZE =
26 | sizeof(int32_t) * DEVICEINFO_ARRAY_SIZE;
27 | inline constexpr size_t DEVICEINFO_MAX_NAME_LENGTH = 128;
28 |
29 | inline constexpr int32_t IGNORED_EVENT = 0;
30 | inline constexpr int32_t HANDLED_EVENT = 1;
31 | } // namespace paddleboat
32 |
--------------------------------------------------------------------------------
/src/extras/build.gradle:
--------------------------------------------------------------------------------
1 | project('aar') {
2 | apply plugin: 'com.android.library'
3 | }
4 |
5 | project('apk') {
6 | apply plugin: 'com.android.application'
7 | }
8 |
9 | subprojects {
10 |
11 | android {
12 | compileSdkVersion 28
13 | defaultConfig {
14 | minSdkVersion 16
15 | targetSdkVersion 28
16 | versionCode 1
17 | versionName "1.0"
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | externalNativeBuild {
20 | cmake {
21 | cppFlags ""
22 | }
23 | }
24 | consumerProguardFiles 'lib-proguard-rules.txt'
25 | sourceSets {
26 | main {
27 | manifest.srcFile '../src/main/AndroidManifest.xml'
28 | java.srcDirs = ['../src/main/java']
29 | }
30 | }
31 | }
32 | buildTypes {
33 | release {
34 | minifyEnabled false
35 | }
36 | }
37 | lintOptions {
38 | abortOnError false
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation fileTree(dir: 'libs', include: ['*.jar'])
44 | testImplementation 'junit:junit:4.12'
45 | }
46 |
47 | }
48 |
49 | task assembleRelease {
50 | dependsOn 'apk:assembleRelease'
51 | }
52 |
--------------------------------------------------------------------------------
/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/State.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.google.androidgamesdk.gametextinput;
17 |
18 | // The state of an editable text region.
19 | public final class State {
20 | public State(String text_in, int selectionStart_in, int selectionEnd_in,
21 | int composingRegionStart_in, int composingRegionEnd_in) {
22 | text = text_in;
23 | selectionStart = selectionStart_in;
24 | selectionEnd = selectionEnd_in;
25 | composingRegionStart = composingRegionStart_in;
26 | composingRegionEnd = composingRegionEnd_in;
27 | }
28 |
29 | public String text;
30 | public int selectionStart;
31 | public int selectionEnd;
32 | public int composingRegionStart;
33 | public int composingRegionEnd;
34 | }
35 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerDeviceInfo.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | #include "GameControllerDeviceInfo.h"
17 |
18 | #include
19 |
20 | namespace paddleboat {
21 | GameControllerDeviceInfo::GameControllerDeviceInfo() {
22 | mName[0] = '\0';
23 | mInfo.mDeviceId = -1;
24 | mInfo.mProductId = -1;
25 | mInfo.mVendorId = -1;
26 | mInfo.mAxisBitsLow = 0;
27 | mInfo.mAxisBitsHigh = 0;
28 | mInfo.mControllerNumber = -1;
29 | mInfo.mControllerFlags = 0;
30 |
31 | for (size_t i = 0; i < paddleboat::MAX_AXIS_COUNT; ++i) {
32 | mAxisMinArray[i] = 0.0f;
33 | mAxisMaxArray[i] = 0.0f;
34 | mAxisFlatArray[i] = 0.0f;
35 | mAxisFuzzArray[i] = 0.0f;
36 | }
37 | }
38 |
39 | void GameControllerDeviceInfo::setName(const char *name) {
40 | strncpy(mName, name, DEVICEINFO_MAX_NAME_LENGTH);
41 | }
42 | } // namespace paddleboat
--------------------------------------------------------------------------------
/GameController/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | id 'com.android.library'
19 | }
20 |
21 | buildDir="../../out_paddleboat"
22 |
23 | android {
24 |
25 | defaultConfig {
26 | minSdkVersion 16
27 | compileSdkVersion 31
28 | targetSdkVersion 31
29 | versionCode 1
30 | versionName "1.1"
31 |
32 | consumerProguardFiles "consumer-rules.pro"
33 | }
34 |
35 | buildTypes {
36 | release {
37 | minifyEnabled false
38 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
39 | }
40 | }
41 |
42 | compileOptions {
43 | sourceCompatibility JavaVersion.VERSION_1_8
44 | targetCompatibility JavaVersion.VERSION_1_8
45 | }
46 | }
47 |
48 | dependencies {
49 | implementation "androidx.annotation:annotation:1.3.0"
50 | }
51 | repositories {
52 | mavenCentral()
53 | }
54 |
--------------------------------------------------------------------------------
/GameTextInput/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | }
4 |
5 | buildDir="../../out_game_text_input"
6 |
7 | android {
8 | compileSdkVersion 28
9 |
10 | defaultConfig {
11 | minSdkVersion 14
12 | targetSdkVersion 28
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | consumerProguardFiles "consumer-rules.pro"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | // TODO(florianrival@) Enabling this will trigger "Cannot query the value of this property because it has no value available." error.
31 | // buildFeatures {
32 | // prefabPublishing true
33 | // }
34 | // prefab {
35 | // gametextinput {
36 | // headers "src/main/cpp/include"
37 | // libraryName "game-text-input"
38 | // }
39 | // }
40 | }
41 |
42 | dependencies {
43 | implementation 'androidx.appcompat:appcompat:1.2.0'
44 | implementation 'androidx.core:core:1.5.0'
45 | implementation 'com.google.android.material:material:1.1.0'
46 | testImplementation 'junit:junit:4.+'
47 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
49 | }
50 |
--------------------------------------------------------------------------------
/GameActivity/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | }
4 |
5 | buildDir = "../../out_game_activity"
6 |
7 | android {
8 | compileSdkVersion 28
9 |
10 | defaultConfig {
11 | minSdkVersion 16
12 | targetSdkVersion 28
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | consumerProguardFiles "consumer-rules.pro"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 |
31 | // TODO(florianrival@) Enabling this will trigger "Cannot query the value of this property because it has no value available." error.
32 | // buildFeatures {
33 | // prefabPublishing true
34 | // }
35 | // prefab {
36 | // gameactivity {
37 | // headers "src/main/cpp/include"
38 | // }
39 | // }
40 | }
41 |
42 | dependencies {
43 | implementation 'androidx.appcompat:appcompat:1.2.0'
44 | implementation 'androidx.core:core:1.5.0'
45 | implementation 'com.google.android.material:material:1.2.1'
46 | testImplementation 'junit:junit:4.+'
47 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
49 | }
50 | repositories {
51 | mavenCentral()
52 | }
53 |
--------------------------------------------------------------------------------
/GameController/src/androidTest/java/com/google/android/games/paddleboat/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2021 The Android Open Source Project
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | package com.google.android.games.paddleboat;
15 |
16 | import android.content.Context;
17 | import android.support.test.InstrumentationRegistry;
18 | import android.support.test.runner.AndroidJUnit4;
19 |
20 | import org.junit.Test;
21 | import org.junit.runner.RunWith;
22 |
23 | import static org.junit.Assert.*;
24 |
25 | /**
26 | * Instrumented test, which will execute on an Android device.
27 | *
28 | * @see Testing documentation
29 | */
30 | @RunWith(AndroidJUnit4.class)
31 | public class ExampleInstrumentedTest {
32 | @Test
33 | public void useAppContext() {
34 | // Context of the app under test.
35 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
36 | assertEquals("com.google.android.games.paddleboat.test", appContext.getPackageName());
37 | }
38 | }
--------------------------------------------------------------------------------
/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/Listener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.google.androidgamesdk.gametextinput;
17 |
18 | import androidx.core.graphics.Insets;
19 |
20 | /**
21 | * Listener interface for text, selection and composing region changes.
22 | * Also a listener for window insets changes.
23 | */
24 | public interface Listener {
25 |
26 | /*
27 | * Called when the IME text, selection or composing region has changed.
28 | *
29 | * @param newState The updated state
30 | * @param dismmissed Whether the IME has been dismissed by the user
31 | */
32 | void stateChanged(State newState, boolean dismissed);
33 |
34 | /*
35 | * Called when the IME window insets change, i.e. the IME moves into or out of view.
36 | *
37 | * @param insets The new window insets, i.e. the offsets of top, bottom, left and right
38 | * relative to the window
39 | */
40 | void onImeInsetsChanged(Insets insets);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gamecommon.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * @defgroup game_common Game Common
19 | * Common structures and functions used within AGDK
20 | * @{
21 | */
22 |
23 | #pragma once
24 |
25 | /**
26 | * The type of a component for which to retrieve insets. See
27 | * https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat.Type
28 | */
29 | typedef enum GameCommonInsetsType {
30 | GAMECOMMON_INSETS_TYPE_CAPTION_BAR = 0,
31 | GAMECOMMON_INSETS_TYPE_DISPLAY_CUTOUT,
32 | GAMECOMMON_INSETS_TYPE_IME,
33 | GAMECOMMON_INSETS_TYPE_MANDATORY_SYSTEM_GESTURES,
34 | GAMECOMMON_INSETS_TYPE_NAVIGATION_BARS,
35 | GAMECOMMON_INSETS_TYPE_STATUS_BARS,
36 | GAMECOMMON_INSETS_TYPE_SYSTEM_BARS,
37 | GAMECOMMON_INSETS_TYPE_SYSTEM_GESTURES,
38 | GAMECOMMON_INSETS_TYPE_TAPABLE_ELEMENT,
39 | GAMECOMMON_INSETS_TYPE_WATERFALL,
40 | GAMECOMMON_INSETS_TYPE_COUNT
41 | } GameCommonInsetsType;
42 |
--------------------------------------------------------------------------------
/GameActivity/README.md:
--------------------------------------------------------------------------------
1 | # GameActivity
2 |
3 | GameActivity is an Android `Activity` modeled on the API of `NativeActivity`, but with a few changes to make it a better starting point for game developers.
4 |
5 | * It inherits from `androidx.appcompat.app.AppCompatActivity` - allowing you to use Jetpack components architecture (and still use some of the newer platform features on older Android devices).
6 | * It renders into a `SurfaceView` that allows you to interface with any other Android UI element.
7 | * It handles *events* like a Java activity would do, allowing any Android UI element (like a text edit, a webview, an ad or a form) to work as usual, but still exposes the events to your game using a C interface that makes them easy to consume in your game loop.
8 | * It offers a C API similar to `NativeActivity`, and to the *android_native_app_glue* library.
9 |
10 | ## Build the GameActivity AAR from sources
11 |
12 | From the `gamesdk` directory:
13 |
14 | * `./gradlew packageLocalZip -Plibraries=game_activity`
15 |
16 | The AAR is output in the `package` directory. It contains GameActivity, both the Java class and its C++ implementation, and the "android_native_app_glue" library.
17 |
18 | ## Integrate GameActivity AAR to your build
19 |
20 | Refer to the integration guide for now. Link to it and other documentation will be added here later.
21 |
22 | Note that the library has a dependency on androidx.core version 1.5.0 or later. You should add the following to your app's build.gradle file:
23 | `
24 | dependencies {
25 | ...
26 | implementation "androidx.core:core:1.5.0"
27 | ...
28 | `
29 | Not adding this will cause a JNI crash at startup.
30 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/ThreadUtil.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | // Enable thread safety attributes only with clang.
24 | // The attributes can be safely erased when compiling with other compilers.
25 | #if defined(__clang__) && (!defined(SWIG))
26 | #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
27 | #else
28 | #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
29 | #endif
30 |
31 | #if !defined GAMESDK_THREAD_CHECKS
32 | #define GAMESDK_THREAD_CHECKS 1
33 | #endif
34 |
35 | #if GAMESDK_THREAD_CHECKS
36 | #define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
37 |
38 | #define REQUIRES(...) \
39 | THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
40 |
41 | #define NO_THREAD_SAFETY_ANALYSIS \
42 | THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
43 | #else
44 | #define GUARDED_BY(x)
45 | #define REQUIRES(...)
46 | #define NO_THREAD_SAFETY_ANALYSIS
47 | #endif
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Source Code
2 |
3 | This project use google game sdk source code. Current dependency info:
4 |
5 | - You can download from [github](https://android.googlesource.com/platform/frameworks/opt/gamesdk).
6 | - **Branch**: android-games-sdk-release
7 | - **CommitID**: 551ee6b30de541fabceb8424abfb3818b5037283
8 |
9 | ## How to use cloned code
10 |
11 | - Copy 'GameActivity', 'GameController', 'GameTextInput' and 'src/extras' directories from cloned directories.
12 | - First, modify cpp files and CMakeLists.txt, and then copy these files to the directory 'sources/Android-gamesdk' in the GIT [repository](https://github.com/cocos/cocos-engine-external.git)
13 | - Modify onCreate function of GameActivity
14 |
15 | ``` Java
16 | String libname = "main";
17 | // following code is needed to be added
18 | if (null != getIntent().getStringExtra(META_DATA_LIB_NAME)) {
19 | libname = getIntent().getStringExtra(META_DATA_LIB_NAME);
20 | }
21 | ```
22 |
23 | ## How to Build
24 |
25 | - Use gradle command. gradlew :lib-game-sdk::assembleRelease
26 |
27 | ## How to use classes.jar
28 |
29 | - Copy ***classes.jar*** from the 'lib-game-sdk/build/intermediates/aar_main_jar/release' directory to the 'native\cocos\platform\android\java\libs' directory of the git [repository](https://github.com/cocos/cocos-engine.git)
30 | - rename classes.jar to ***game-sdk.jar***
31 |
32 | ## Environment requirements
33 |
34 | - ***JAVA_HOME*** configuration to jdk11 can not be higher than jdk14,
35 | start a new command line window and make sure that the Java version number is the set JDK version. You may need to put ***$JAVA_HOME/bin*** to the system variable path.
36 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/Log.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | #include
22 |
23 | #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
24 | #define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
25 | #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
26 | #define ALOGW_ONCE_IF(cond, ...) \
27 | do { \
28 | static bool alogw_once##__FILE__##__LINE__##__ = true; \
29 | if (cond && alogw_once##__FILE__##__LINE__##__) { \
30 | alogw_once##__FILE__##__LINE__##__ = false; \
31 | ALOGW(__VA_ARGS__); \
32 | } \
33 | } while (0)
34 |
35 | #ifndef NDEBUG
36 | #define ALOGV(...) \
37 | __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
38 | #else
39 | #define ALOGV(...)
40 | #endif
41 |
--------------------------------------------------------------------------------
/src/extras/src/main/java/com/google/androidgamesdk/ChoreographerCallback.java:
--------------------------------------------------------------------------------
1 | package com.google.androidgamesdk;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 | import android.view.Choreographer;
6 | import android.util.Log;
7 |
8 |
9 | public class ChoreographerCallback implements Choreographer.FrameCallback {
10 | private static final String LOG_TAG = "ChoreographerCallback";
11 | private long mCookie;
12 | private LooperThread mLooper;
13 |
14 | private class LooperThread extends Thread {
15 | public Handler mHandler;
16 |
17 | public void run() {
18 | Log.i(LOG_TAG, "Starting looper thread");
19 | Looper.prepare();
20 | mHandler = new Handler();
21 | Looper.loop();
22 | Log.i(LOG_TAG, "Terminating looper thread");
23 | }
24 | }
25 |
26 | public ChoreographerCallback(long cookie) {
27 | mCookie = cookie;
28 | mLooper = new LooperThread();
29 | mLooper.start();
30 | }
31 |
32 | public void postFrameCallback() {
33 | mLooper.mHandler.post(new Runnable() {
34 | @Override
35 | public void run() {
36 | Choreographer.getInstance().postFrameCallback(ChoreographerCallback.this);
37 | }
38 | });
39 | }
40 |
41 | public void postFrameCallbackDelayed(long delayMillis) {
42 | Choreographer.getInstance().postFrameCallbackDelayed(this, delayMillis);
43 | }
44 |
45 | public void terminate() {
46 | mLooper.mHandler.getLooper().quit();
47 | }
48 |
49 | @Override
50 | public void doFrame(long frameTimeNanos) {
51 | nOnChoreographer(mCookie, frameTimeNanos);
52 | }
53 |
54 | public native void nOnChoreographer(long cookie, long frameTimeNanos);
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2021 The Android Open Source Project
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # https://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 | project(paddleboat C CXX)
17 |
18 | include_directories(${CMAKE_CURRENT_LIST_DIR}/paddleboat/include)
19 |
20 | set( PADDLEBOAT_SRCS
21 | ${CMAKE_CURRENT_LIST_DIR}/InternalControllerTable.cpp
22 | ${CMAKE_CURRENT_LIST_DIR}/GameController.cpp
23 | ${CMAKE_CURRENT_LIST_DIR}/GameControllerDeviceInfo.cpp
24 | ${CMAKE_CURRENT_LIST_DIR}/GameControllerLog.cpp
25 | ${CMAKE_CURRENT_LIST_DIR}/GameControllerManager.cpp
26 | ${CMAKE_CURRENT_LIST_DIR}/GameControllerMappingUtils.cpp
27 | ${CMAKE_CURRENT_LIST_DIR}/paddleboat_c.cpp)
28 |
29 | # set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Os")
30 | # set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
31 | # set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g0")
32 |
33 | add_library(paddleboat_static STATIC ${PADDLEBOAT_SRCS})
34 |
35 | set_target_properties( paddleboat_static PROPERTIES
36 | LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build )
37 |
38 | add_library(paddleboat SHARED ${CMAKE_CURRENT_LIST_DIR}/paddleboat_c.cpp)
39 |
40 | target_link_libraries(paddleboat
41 | paddleboat_static
42 | android
43 | atomic
44 | log)
45 |
--------------------------------------------------------------------------------
/lib-game-sdk/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | id 'com.android.library'
19 | }
20 |
21 |
22 | android {
23 |
24 | defaultConfig {
25 | minSdkVersion 18
26 | compileSdkVersion 31
27 | targetSdkVersion 31
28 | versionCode 1
29 | versionName "1.1"
30 |
31 | consumerProguardFiles "consumer-rules.pro"
32 | sourceSets {
33 | main {
34 | manifest.srcFile 'AndroidManifest.xml'
35 | java.srcDirs = ['../src/extras/src/main/java', '../GameActivity/src/main/java', '../GameController/src/main/java']
36 | }
37 | }
38 | }
39 |
40 | buildTypes {
41 | release {
42 | minifyEnabled false
43 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
44 | }
45 | }
46 |
47 | compileOptions {
48 | sourceCompatibility JavaVersion.VERSION_1_8
49 | targetCompatibility JavaVersion.VERSION_1_8
50 | }
51 | }
52 |
53 | dependencies {
54 | // implementation "androidx.annotation:annotation:1.3.0"
55 | // implementation 'androidx.appcompat:appcompat:1.2.0'
56 | // implementation 'androidx.core:core:1.5.0'
57 | // implementation 'com.google.android.material:material:1.2.1'
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/src/extras/src/main/java/com/google/androidgamesdk/GameSdkDeviceInfoJni.java:
--------------------------------------------------------------------------------
1 | package com.google.androidgamesdk;
2 |
3 | /** JNI api for getting device information */
4 | public class GameSdkDeviceInfoJni {
5 | private static Throwable initializationExceptionOrError;
6 |
7 | static {
8 | try {
9 | System.loadLibrary("game_sdk_device_info_jni");
10 | } catch(Exception exception) {
11 | // Catch SecurityException, NullPointerException (or any potential unchecked exception)
12 | // as we don't want to crash the app if the library failed to load.
13 | initializationExceptionOrError = exception;
14 | } catch(Error error) {
15 | // Catch UnsatisfiedLinkError (or any potential unchecked error)
16 | // as we don't want to crash the app if the library failed to load.
17 | initializationExceptionOrError = error;
18 | }
19 | }
20 |
21 | /**
22 | * Returns a byte array, which is a serialized proto containing device information, or
23 | * null if the native library "game_sdk_device_info_jni" could not be loaded.
24 | *
25 | * @return Optional with the serialized byte array, representing game sdk device info with errors,
26 | * or null.
27 | */
28 | public static byte[] tryGetProtoSerialized() {
29 | if (initializationExceptionOrError != null) {
30 | return null;
31 | }
32 |
33 | return getProtoSerialized();
34 | }
35 |
36 |
37 | /**
38 | * Returns the exception or error that was caught when trying to load the library, if any.
39 | * Otherwise, returns null.
40 | *
41 | * @return The caught Throwable or null.
42 | */
43 | public static Throwable getInitializationExceptionOrError() {
44 | return initializationExceptionOrError;
45 | }
46 |
47 | /**
48 | * Returns a byte array, which is a serialized proto.
49 | *
50 | * @return serialized byte array, representing game sdk device info with errors.
51 | */
52 | private static native byte[] getProtoSerialized();
53 |
54 | private GameSdkDeviceInfoJni() {}
55 | }
56 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerMappingUtils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | #ifndef PADDLEBOAT_H
22 | #include "paddleboat.h"
23 | #endif
24 |
25 | namespace paddleboat {
26 | class MappingTableSearch {
27 | public:
28 | MappingTableSearch();
29 |
30 | MappingTableSearch(Paddleboat_Controller_Mapping_Data *mapRoot,
31 | int32_t entryCount);
32 |
33 | void initSearchParameters(const int32_t newVendorId,
34 | const int32_t newProductId,
35 | const int32_t newMinApi, const int32_t newMaxApi);
36 |
37 | Paddleboat_Controller_Mapping_Data *mappingRoot;
38 | int32_t vendorId;
39 | int32_t productId;
40 | int32_t minApi;
41 | int32_t maxApi;
42 | int32_t tableIndex;
43 | int32_t mapEntryCount;
44 | int32_t tableEntryCount;
45 | int32_t tableMaxEntryCount;
46 | };
47 |
48 | class GameControllerMappingUtils {
49 | public:
50 | static bool findMatchingMapEntry(MappingTableSearch *searchEntry);
51 |
52 | static bool insertMapEntry(
53 | const Paddleboat_Controller_Mapping_Data *mappingData,
54 | MappingTableSearch *searchEntry);
55 |
56 | static const Paddleboat_Controller_Mapping_Data *validateMapTable(
57 | const Paddleboat_Controller_Mapping_Data *mappingRoot,
58 | const int32_t tableEntryCount);
59 | };
60 | } // namespace paddleboat
61 |
--------------------------------------------------------------------------------
/GameController/src/test/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2021 The Android Open Source Project
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # https://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | set(TEST_ABI_LIST ${TEST_ABIS})
18 |
19 | if (${ANDROID_ABI} IN_LIST TEST_ABI_LIST)
20 | if (${RUN_CPP_TESTS} MATCHES "true")
21 | set(TEST_LIB_NAME paddleboat-tests-lib)
22 | set(GTEST_DIR ${ANDROID_NDK}/sources/third_party/googletest)
23 | add_library(gtest STATIC ${GTEST_DIR}/src/gtest_main.cc ${GTEST_DIR}/src/gtest-all.cc)
24 | target_include_directories(gtest PRIVATE ${GTEST_DIR})
25 | target_include_directories(gtest PUBLIC ${GTEST_DIR}/include)
26 |
27 | add_executable(${TEST_LIB_NAME}
28 | ${TEST_SRC_DIR}/paddleboat_tests.cpp)
29 |
30 | target_link_libraries(${TEST_LIB_NAME} paddleboat gtest)
31 |
32 | set(TARGET_TEST_DIR /data/local/tmp/${TEST_LIB_NAME})
33 | set(TARGET_TEST_LIB_DIR ${TARGET_TEST_DIR}/${ANDROID_ABI})
34 | find_program(ADB NAMES adb PATHS ${ANDROID_SDK_ROOT}/platform-tools)
35 |
36 | message(STATUS "Running Paddleboat tests for ABI ${ANDROID_ABI} SDK ${ANDROID_SDK_ROOT} RUN_CPP_TESTS ${RUN_CPP_TESTS}")
37 |
38 | add_custom_command(TARGET ${TEST_LIB_NAME} POST_BUILD
39 | COMMAND ${ADB} shell mkdir -p ${TARGET_TEST_LIB_DIR}
40 | COMMAND ${ADB} push $ ${TARGET_TEST_LIB_DIR}/
41 | COMMAND ${ADB} push $ ${TARGET_TEST_LIB_DIR}/
42 | COMMAND ${ADB} shell \"export LD_LIBRARY_PATH=${TARGET_TEST_LIB_DIR}\; ${TARGET_TEST_LIB_DIR}/${TEST_LIB_NAME}\")
43 | endif () # RUN_CPP_TESTS
44 | else()
45 | message(STATUS "Skipping Paddleboat tests for ABI ${ANDROID_ABI} SDK ${ANDROID_SDK_ROOT} RUN_CPP_TESTS ${RUN_CPP_TESTS}")
46 | endif () # ANDROID_ABI in TEST_ABIS
47 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerDeviceInfo.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include "GameControllerInternalConstants.h"
20 |
21 | namespace paddleboat {
22 |
23 | class GameControllerDeviceInfo {
24 | public:
25 | // These are copied directly from the int[] mGameControllerDeviceInfoArray,
26 | // the layout should match the field definitions in
27 | // Paddleboat_Controller_Info.java
28 | struct InfoFields {
29 | int32_t mDeviceId;
30 | int32_t mVendorId;
31 | int32_t mProductId;
32 | int32_t mAxisBitsLow;
33 | int32_t mAxisBitsHigh;
34 | int32_t mControllerNumber;
35 | int32_t mControllerFlags;
36 | };
37 |
38 | GameControllerDeviceInfo();
39 |
40 | void setName(const char *name);
41 |
42 | const char *getName() const { return mName; }
43 |
44 | const InfoFields &getInfo() const { return mInfo; }
45 |
46 | const float *getMinArray() const { return mAxisMinArray; }
47 |
48 | const float *getMaxArray() const { return mAxisMaxArray; }
49 |
50 | const float *getFlatArray() const { return mAxisFlatArray; }
51 |
52 | const float *getFuzzArray() const { return mAxisFuzzArray; }
53 |
54 | InfoFields *getInfo() { return &mInfo; }
55 |
56 | float *getMinArray() { return mAxisMinArray; }
57 |
58 | float *getMaxArray() { return mAxisMaxArray; }
59 |
60 | float *getFlatArray() { return mAxisFlatArray; }
61 |
62 | float *getFuzzArray() { return mAxisFuzzArray; }
63 |
64 | private:
65 | char mName[paddleboat::DEVICEINFO_MAX_NAME_LENGTH];
66 | InfoFields mInfo;
67 | float mAxisMinArray[paddleboat::MAX_AXIS_COUNT];
68 | float mAxisMaxArray[paddleboat::MAX_AXIS_COUNT];
69 | float mAxisFlatArray[paddleboat::MAX_AXIS_COUNT];
70 | float mAxisFuzzArray[paddleboat::MAX_AXIS_COUNT];
71 | };
72 | } // namespace paddleboat
73 |
--------------------------------------------------------------------------------
/GameActivity/prefab-src/modules/game-activity/include/game-activity/system_utils.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "system_utils.h"
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | namespace gamesdk {
24 |
25 | #if __ANDROID_API__ >= 26
26 | std::string getSystemPropViaCallback(const char* key,
27 | const char* default_value = "") {
28 | const prop_info* prop = __system_property_find(key);
29 | if (prop == nullptr) {
30 | return default_value;
31 | }
32 | std::string return_value;
33 | auto thunk = [](void* cookie, const char* /*name*/, const char* value,
34 | uint32_t /*serial*/) {
35 | if (value != nullptr) {
36 | std::string* r = static_cast(cookie);
37 | *r = value;
38 | }
39 | };
40 | __system_property_read_callback(prop, thunk, &return_value);
41 | return return_value;
42 | }
43 | #else
44 | std::string getSystemPropViaGet(const char* key,
45 | const char* default_value = "") {
46 | char buffer[PROP_VALUE_MAX + 1] = ""; // +1 for terminator
47 | int bufferLen = __system_property_get(key, buffer);
48 | if (bufferLen > 0)
49 | return buffer;
50 | else
51 | return "";
52 | }
53 | #endif
54 |
55 | std::string GetSystemProp(const char* key, const char* default_value) {
56 | #if __ANDROID_API__ >= 26
57 | return getSystemPropViaCallback(key, default_value);
58 | #else
59 | return getSystemPropViaGet(key, default_value);
60 | #endif
61 | }
62 |
63 | int GetSystemPropAsInt(const char* key, int default_value) {
64 | std::string prop = GetSystemProp(key);
65 | return prop == "" ? default_value : strtoll(prop.c_str(), nullptr, 10);
66 | }
67 |
68 | bool GetSystemPropAsBool(const char* key, bool default_value) {
69 | return GetSystemPropAsInt(key, default_value) != 0;
70 | }
71 |
72 | } // namespace gamesdk
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerGameActivityMirror.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | namespace paddleboat {
22 | // GameActivity needs to use its own event data structures for key and motion
23 | // events. We do not want to have a dependency on GameActivity. Unfortunately,
24 | // this means we need to internally mirror the relevant structures.
25 | // Paddleboat_processGameActivityInputEvent includes a struct size parameter,
26 | // and we know that the GameActivity structures will only ever add fields, so we
27 | // can determine if we are being passed a later version of the struct than the
28 | // mirrored internal version.
29 |
30 | // The following should mirror GameActivity.h
31 | #define PADDLEBOAT_GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT 48
32 | #define PADDLEBOAT_MAX_NUM_POINTERS_IN_MOTION_EVENT 8
33 |
34 | typedef struct Paddleboat_GameActivityPointerInfo {
35 | int32_t id;
36 | int32_t toolType; // added in newer version
37 | float axisValues[PADDLEBOAT_GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
38 | float rawX;
39 | float rawY;
40 | } Paddleboat_GameActivityPointerInfo;
41 |
42 | typedef struct Paddleboat_GameActivityMotionEvent {
43 | int32_t deviceId;
44 | int32_t source;
45 | int32_t action;
46 | int64_t eventTime;
47 | int64_t downTime;
48 | int32_t flags;
49 | int32_t metaState;
50 | int32_t actionButton;
51 | int32_t buttonState;
52 | int32_t classification;
53 | int32_t edgeFlags;
54 | uint32_t pointerCount;
55 | Paddleboat_GameActivityPointerInfo
56 | pointers[PADDLEBOAT_MAX_NUM_POINTERS_IN_MOTION_EVENT];
57 | float precisionX;
58 | float precisionY;
59 | } Paddleboat_GameActivityMotionEvent;
60 |
61 | typedef struct Paddleboat_GameActivityKeyEvent {
62 | int32_t deviceId;
63 | int32_t source;
64 | int32_t action;
65 | int64_t eventTime;
66 | int64_t downTime;
67 | int32_t flags;
68 | int32_t metaState;
69 | int32_t modifiers;
70 | int32_t repeatCount;
71 | int32_t keyCode;
72 | } Paddleboat_GameActivityKeyEvent;
73 | } // namespace paddleboat
74 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerThread.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2021 The Android Open Source Project
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | package com.google.android.games.paddleboat;
15 |
16 | import android.hardware.input.InputManager;
17 | import android.os.Handler;
18 | import android.os.Looper;
19 | import android.util.Log;
20 |
21 | import java.lang.Thread;
22 |
23 | public class GameControllerThread extends Thread implements InputManager.InputDeviceListener {
24 | private static final String TAG = "GameControllerThread";
25 | private boolean activeInputDeviceListener = false;
26 | private GameControllerManager mGameControllerManager;
27 | private Handler mHandler;
28 |
29 | public void setGameControllerManager(GameControllerManager gcManager) {
30 | mGameControllerManager = gcManager;
31 | }
32 |
33 | @Override
34 | public void run () {
35 | Looper.prepare();
36 | mHandler = new Handler(Looper.myLooper());
37 | onStart();
38 | Looper.loop();
39 | }
40 |
41 | public void onStop() {
42 | if (activeInputDeviceListener) {
43 | Log.d(TAG, "unregisterInputDeviceListener");
44 | mGameControllerManager.getAppInputManager().unregisterInputDeviceListener(this);
45 | activeInputDeviceListener = false;
46 | }
47 | }
48 |
49 | public void onStart() {
50 | if (!activeInputDeviceListener) {
51 | Log.d(TAG, "registerInputDeviceListener");
52 | mGameControllerManager.getAppInputManager().registerInputDeviceListener(this, mHandler);
53 | activeInputDeviceListener = true;
54 | }
55 | }
56 |
57 | public void terminate() {
58 | if (mHandler != null) {
59 | Log.d(TAG, "terminate");
60 | mHandler.getLooper().quit();
61 | mHandler = null;
62 | }
63 | }
64 |
65 | @Override
66 | public void onInputDeviceAdded(int deviceId) {
67 | Log.d(TAG, "onInputDeviceAdded id: " + deviceId);
68 | mGameControllerManager.onInputDeviceAdded(deviceId);
69 | }
70 |
71 | @Override
72 | public void onInputDeviceRemoved(int deviceId) {
73 | Log.d(TAG, "onInputDeviceRemoved id: " + deviceId);
74 | mGameControllerManager.onInputDeviceRemoved(deviceId);
75 | }
76 |
77 | @Override
78 | public void onInputDeviceChanged(int deviceId) {
79 | Log.d(TAG, "onInputDeviceChanged id: " + deviceId);
80 | mGameControllerManager.onInputDeviceChanged(deviceId);
81 | }
82 | }
--------------------------------------------------------------------------------
/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/GameTextInput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.google.androidgamesdk.gametextinput;
17 |
18 | import android.text.Editable;
19 | import android.text.Spanned;
20 | import android.view.inputmethod.EditorInfo;
21 |
22 | /*
23 | * Singleton GameTextInput class with helper methods.
24 | */
25 | public final class GameTextInput {
26 | private static final GameTextInput composingRegionKey;
27 | private static final Class selectionKey;
28 |
29 | public final static void copyEditorInfo(EditorInfo from, EditorInfo to) {
30 | if (from == null || to == null)
31 | return;
32 | if (from.hintText != null) {
33 | to.hintText = from.hintText;
34 | }
35 |
36 | to.inputType = from.inputType;
37 | to.imeOptions = from.imeOptions;
38 | to.label = from.label;
39 | to.initialCapsMode = from.initialCapsMode;
40 | to.privateImeOptions = from.privateImeOptions;
41 | if (from.packageName != null) {
42 | to.packageName = from.packageName;
43 | }
44 |
45 | to.fieldId = from.fieldId;
46 | if (from.fieldName != null) {
47 | to.fieldName = from.fieldName;
48 | }
49 | }
50 |
51 | public static final class Pair {
52 | int first, second;
53 |
54 | Pair(int f, int s) {
55 | first = f;
56 | second = s;
57 | }
58 | }
59 |
60 | public final static Pair getSelection(Editable editable) {
61 | return new Pair(editable.getSpanStart(selectionKey), editable.getSpanEnd(selectionKey));
62 | }
63 |
64 | public final static Pair getComposingRegion(Editable editable) {
65 | return new Pair(
66 | editable.getSpanStart(composingRegionKey), editable.getSpanEnd(composingRegionKey));
67 | }
68 |
69 | public final static void setSelection(Editable editable, int start, int end) {
70 | if (start > editable.length())
71 | start = editable.length();
72 | if (end > editable.length())
73 | end = editable.length();
74 |
75 | // Note that selections can be in the opposite order
76 | if (start > end)
77 | editable.setSpan(selectionKey, end, start, 0);
78 | else
79 | editable.setSpan(selectionKey, start, end, 0);
80 | }
81 |
82 | public final static void setComposingRegion(Editable editable, int start, int end) {
83 | if (start > editable.length())
84 | start = editable.length();
85 | if (end > editable.length())
86 | end = editable.length();
87 |
88 | // Note that selections can be in the opposite order
89 | if (start > end)
90 | editable.setSpan(composingRegionKey, end, start, Spanned.SPAN_COMPOSING);
91 | else
92 | editable.setSpan(composingRegionKey, start, end, Spanned.SPAN_COMPOSING);
93 | }
94 |
95 | public final static void removeComposingRegion(Editable editable) {
96 | editable.removeSpan(composingRegionKey);
97 | }
98 |
99 | public final static GameTextInput getComposingRegionKey() {
100 | return composingRegionKey;
101 | }
102 |
103 | public final static Class getSelectionKey() {
104 | return selectionKey;
105 | }
106 |
107 | private GameTextInput() {}
108 |
109 | static {
110 | composingRegionKey = new GameTextInput();
111 | selectionKey = composingRegionKey.getClass();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerLog.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "GameControllerLog.h"
18 |
19 | #include
20 |
21 | #include "GameControllerLogStrings.h"
22 | #include "Log.h"
23 |
24 | #define LOG_TAG "GameControllerManager"
25 | // Filter input event logging to qualifying 'gamecontroller' event sources
26 | #define LOG_FILTER_PADDLEBOAT_SOURCES
27 |
28 | #define ELEMENTS_OF(x) (sizeof(x) / sizeof(x[0]))
29 |
30 | namespace paddleboat {
31 | const char *LogGetInputSourceString(const int32_t eventSource) {
32 | const char *inputSourceString = "AINPUT_SOURCE_UNKNOWN";
33 |
34 | switch (eventSource) {
35 | case AINPUT_SOURCE_KEYBOARD:
36 | inputSourceString = "AINPUT_SOURCE_KEYBOARD";
37 | break;
38 | case AINPUT_SOURCE_DPAD:
39 | inputSourceString = "AINPUT_SOURCE_DPAD";
40 | break;
41 | case AINPUT_SOURCE_GAMEPAD:
42 | inputSourceString = "AINPUT_SOURCE_GAMEPAD";
43 | break;
44 | case AINPUT_SOURCE_TOUCHSCREEN:
45 | inputSourceString = "AINPUT_SOURCE_TOUCHSCREEN";
46 | break;
47 | case AINPUT_SOURCE_MOUSE:
48 | inputSourceString = "AINPUT_SOURCE_MOUSE";
49 | break;
50 | case AINPUT_SOURCE_STYLUS:
51 | inputSourceString = "AINPUT_SOURCE_STYLUS";
52 | break;
53 | case AINPUT_SOURCE_BLUETOOTH_STYLUS:
54 | inputSourceString = "AINPUT_SOURCE_BLUETOOTH_STYLUS";
55 | break;
56 | case AINPUT_SOURCE_MOUSE_RELATIVE:
57 | inputSourceString = "AINPUT_SOURCE_MOUSE_RELATIVE";
58 | break;
59 | case AINPUT_SOURCE_TOUCHPAD:
60 | inputSourceString = "AINPUT_SOURCE_TOUCHPAD";
61 | break;
62 | case AINPUT_SOURCE_TOUCH_NAVIGATION:
63 | inputSourceString = "AINPUT_SOURCE_TOUCH_NAVIGATION";
64 | break;
65 | case AINPUT_SOURCE_JOYSTICK:
66 | inputSourceString = "AINPUT_SOURCE_JOYSTICK";
67 | break;
68 | case AINPUT_SOURCE_ROTARY_ENCODER:
69 | inputSourceString = "AINPUT_SOURCE_ROTARY_ENCODER";
70 | break;
71 | default:
72 | break;
73 | }
74 | return inputSourceString;
75 | }
76 |
77 | void LogInputEvent(const AInputEvent *event) {
78 | const int32_t eventSource = AInputEvent_getSource(event);
79 | #if defined LOG_FILTER_PADDLEBOAT_SOURCES
80 | if (!(eventSource == AINPUT_SOURCE_DPAD ||
81 | eventSource == AINPUT_SOURCE_GAMEPAD ||
82 | eventSource == AINPUT_SOURCE_JOYSTICK)) {
83 | return;
84 | }
85 | #endif
86 | const int32_t eventDeviceId = AInputEvent_getDeviceId(event);
87 | const int32_t eventType = AInputEvent_getType(event);
88 | const char *inputSourceString = LogGetInputSourceString(eventSource);
89 |
90 | if (eventType == AINPUT_EVENT_TYPE_KEY) {
91 | const int32_t eventAction = AKeyEvent_getAction(event);
92 | const int32_t eventFlags = AKeyEvent_getFlags(event);
93 | const int32_t eventKeycode = AKeyEvent_getKeyCode(event);
94 | const char *actionString =
95 | (eventAction < ELEMENTS_OF(AKEY_ACTION_STRINGS) && eventAction >= 0)
96 | ? AKEY_ACTION_STRINGS[eventAction]
97 | : "AKEY_ACTION out of range";
98 | const char *keycodeString =
99 | (eventKeycode < ELEMENTS_OF(AKEYCODE_STRINGS) && eventKeycode >= 0)
100 | ? AKEYCODE_STRINGS[eventKeycode]
101 | : "AKEYCODE out of range";
102 | ALOGI(
103 | "LogInputEvent\nAINPUT_EVENT_TYPE_KEY deviceId %d source %s\n%s %s "
104 | "%08x",
105 | eventDeviceId, inputSourceString, actionString, keycodeString,
106 | eventFlags);
107 | } else if (eventType == AINPUT_EVENT_TYPE_MOTION) {
108 | const int32_t eventAction =
109 | AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;
110 | const int32_t eventFlags = AMotionEvent_getFlags(event);
111 | const char *actionString =
112 | (eventAction < ELEMENTS_OF(AMOTION_ACTION_STRINGS) &&
113 | eventAction >= 0)
114 | ? AMOTION_ACTION_STRINGS[eventAction]
115 | : "AMOTION_ACTION out of range";
116 | ALOGI(
117 | "LogInputEvent\nAINPUT_EVENT_TYPE_MOTION deviceId %d source %s\n%s "
118 | "%08x",
119 | eventDeviceId, inputSourceString, actionString, eventFlags);
120 | }
121 | }
122 | } // namespace paddleboat
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameController.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | #include "GameControllerDeviceInfo.h"
22 | #include "GameControllerGameActivityMirror.h"
23 | #include "paddleboat.h"
24 |
25 | namespace paddleboat {
26 |
27 | // set if axisMultiplier/axisAdjustment should be applied (raw device axis isn't
28 | // -1.0 to 1.0)
29 | inline constexpr uint32_t GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS =
30 | (1U << 0);
31 | // set if trigger is being faked as an analog axis (device just has a on/off
32 | // button flag)
33 | inline constexpr uint32_t GAMECONTROLLER_AXIS_FLAG_DIGITAL_TRIGGER = (1U << 1);
34 |
35 | class GameController {
36 | public:
37 | enum GameControllerAxis {
38 | GAMECONTROLLER_AXIS_LSTICK_X = 0,
39 | GAMECONTROLLER_AXIS_LSTICK_Y,
40 | GAMECONTROLLER_AXIS_RSTICK_X,
41 | GAMECONTROLLER_AXIS_RSTICK_Y,
42 | GAMECONTROLLER_AXIS_L1,
43 | GAMECONTROLLER_AXIS_L2,
44 | GAMECONTROLLER_AXIS_R1,
45 | GAMECONTROLLER_AXIS_R2,
46 | GAMECONTROLLER_AXIS_HAT_X,
47 | GAMECONTROLLER_AXIS_HAT_Y,
48 | GAMECONTROLLER_AXIS_COUNT
49 | };
50 |
51 | struct GameControllerAxisInfo {
52 | // Index into the device axis array, -1 is unmapped
53 | int32_t axisIndex = -1;
54 | // See GAMECONTROLLER_AXIS_FLAG constants
55 | uint32_t axisFlags = 0;
56 | // Button mask flag, if backed/shadowed by digital button when axis >
57 | // 0.0
58 | uint32_t axisButtonMask = 0;
59 | // Button mask flag, if backed/shadowed by digital button when axis <
60 | // 0.0
61 | uint32_t axisButtonNegativeMask = 0;
62 | // Multiplier to normalize to a 0.0 center -> 1.0 edge
63 | float axisMultiplier = 1.0f;
64 | // Adjustment to bring center to 0.0 if necessary
65 | float axisAdjust = 0.0f;
66 |
67 | void resetInfo() {
68 | axisIndex = -1;
69 | axisFlags = 0;
70 | axisButtonMask = 0;
71 | axisButtonNegativeMask = 0;
72 | axisMultiplier = 1.0f;
73 | axisAdjust = 0.0f;
74 | }
75 | };
76 |
77 | GameController();
78 |
79 | void setupController(const Paddleboat_Controller_Mapping_Data *mappingData);
80 |
81 | void initializeDefaultAxisMapping();
82 |
83 | int32_t processGameActivityKeyEvent(
84 | const Paddleboat_GameActivityKeyEvent *event, const size_t eventSize);
85 |
86 | int32_t processGameActivityMotionEvent(
87 | const Paddleboat_GameActivityMotionEvent *event,
88 | const size_t eventSize);
89 |
90 | int32_t processKeyEvent(const AInputEvent *event);
91 |
92 | int32_t processMotionEvent(const AInputEvent *event);
93 |
94 | Paddleboat_ControllerStatus getControllerStatus() const {
95 | return mControllerStatus;
96 | }
97 |
98 | void setControllerStatus(
99 | const Paddleboat_ControllerStatus controllerStatus) {
100 | mControllerStatus = controllerStatus;
101 | }
102 |
103 | int32_t getConnectionIndex() const { return mConnectionIndex; }
104 |
105 | void setConnectionIndex(const int32_t connectionIndex) {
106 | mConnectionIndex = connectionIndex;
107 | }
108 |
109 | uint64_t getControllerAxisMask() const { return mControllerAxisMask; }
110 |
111 | Paddleboat_Controller_Data &getControllerData() { return mControllerData; }
112 |
113 | const Paddleboat_Controller_Data &getControllerData() const {
114 | return mControllerData;
115 | }
116 |
117 | Paddleboat_Controller_Info &getControllerInfo() { return mControllerInfo; }
118 |
119 | const Paddleboat_Controller_Info &getControllerInfo() const {
120 | return mControllerInfo;
121 | }
122 |
123 | GameControllerDeviceInfo &getDeviceInfo() { return mDeviceInfo; }
124 |
125 | const GameControllerDeviceInfo &getDeviceInfo() const {
126 | return mDeviceInfo;
127 | }
128 |
129 | GameControllerAxisInfo *getAxisInfo() { return mAxisInfo; }
130 |
131 | const GameControllerAxisInfo *getAxisInfo() const { return mAxisInfo; }
132 |
133 | bool getControllerDataDirty() const { return mControllerDataDirty; }
134 |
135 | void setControllerDataDirty(const bool dirty);
136 |
137 | void resetControllerData();
138 |
139 | private:
140 | int32_t processKeyEventInternal(const int32_t eventKeyCode,
141 | const int32_t eventKeyAction);
142 |
143 | int32_t processMotionEventInternal(const float *axisArray,
144 | const AInputEvent *event);
145 |
146 | void setupAxis(const GameControllerAxis gcAxis,
147 | const int32_t preferredNativeAxisId,
148 | const int32_t secondaryNativeAxisId,
149 | const int32_t buttonMask, const int32_t buttonNegativeMask);
150 |
151 | void adjustAxisConstants();
152 |
153 | uint64_t mControllerAxisMask = 0;
154 | Paddleboat_ControllerStatus mControllerStatus =
155 | PADDLEBOAT_CONTROLLER_INACTIVE;
156 | int32_t mConnectionIndex = -1;
157 | Paddleboat_Controller_Data mControllerData;
158 | Paddleboat_Controller_Info mControllerInfo;
159 | int32_t mButtonKeycodes[PADDLEBOAT_BUTTON_COUNT];
160 | GameControllerAxisInfo mAxisInfo[GAMECONTROLLER_AXIS_COUNT];
161 | GameControllerDeviceInfo mDeviceInfo;
162 | // Controller data has been updated since the last time it was read
163 | bool mControllerDataDirty;
164 | };
165 | } // namespace paddleboat
166 |
--------------------------------------------------------------------------------
/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerInfo.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2021 The Android Open Source Project
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | package com.google.android.games.paddleboat;
15 |
16 | import android.os.Build;
17 | import android.view.InputDevice;
18 |
19 | import java.util.List;
20 |
21 | public class GameControllerInfo {
22 | private static final int MAX_AXIS_COUNT = 48;
23 | // Axis bits won't fit in 32-bits, split them across low/high
24 | private static final int AXIS_COUNT_LOW = 31;
25 |
26 | // Indices into the mGameControllerDeviceInfoArray that
27 | // pass device specific information to the native client
28 | private static final int DEVICEINFO_INDEX_DEVICEID = 0;
29 | private static final int DEVICEINFO_INDEX_VENDORID = 1;
30 | private static final int DEVICEINFO_INDEX_PRODUCTID = 2;
31 | private static final int DEVICEINFO_INDEX_AXISBITS_LOW = 3;
32 | private static final int DEVICEINFO_INDEX_AXISBITS_HIGH = 4;
33 | private static final int DEVICEINFO_INDEX_CONTROLLERNUMBER = 5;
34 | private static final int DEVICEINFO_INDEX_DEVICEFLAGS = 6;
35 | private static final int DEVICEINFO_ARRAY_SIZE = 7;
36 |
37 | private static final int DEVICEFLAG_VIBRATION = 0x8000000;
38 | private static final int DEVICEFLAG_VIBRATION_DUAL_MOTOR = 0x10000000;
39 | private static final int DEVICEFLAG_VIRTUAL_MOUSE = 0x40000000;
40 |
41 | private final int[] mGameControllerDeviceInfoArray;
42 | private final float[] mGameControllerAxisMinArray;
43 | private final float[] mGameControllerAxisMaxArray;
44 | private final float[] mGameControllerAxisFlatArray;
45 | private final float[] mGameControllerAxisFuzzArray;
46 | private final String mGameControllerNameString;
47 |
48 | private GameControllerListener mListener = null;
49 |
50 | GameControllerInfo(InputDevice inputDevice) {
51 | mGameControllerDeviceInfoArray = new int[DEVICEINFO_ARRAY_SIZE];
52 | mGameControllerAxisMinArray = new float[MAX_AXIS_COUNT];
53 | mGameControllerAxisMaxArray = new float[MAX_AXIS_COUNT];
54 | mGameControllerAxisFlatArray = new float[MAX_AXIS_COUNT];
55 | mGameControllerAxisFuzzArray = new float[MAX_AXIS_COUNT];
56 |
57 | for (int index = 0; index < DEVICEINFO_ARRAY_SIZE; ++index) {
58 | mGameControllerDeviceInfoArray[index] = 0;
59 | }
60 |
61 | for (int index = 0; index < MAX_AXIS_COUNT; ++index) {
62 | mGameControllerAxisMinArray[index] = 0.0f;
63 | mGameControllerAxisMaxArray[index] = 0.0f;
64 | mGameControllerAxisFlatArray[index] = 0.0f;
65 | mGameControllerAxisFuzzArray[index] = 0.0f;
66 | }
67 |
68 | mGameControllerNameString = inputDevice.getName();
69 | EnumerateAxis(inputDevice);
70 | EnumerateInfoArray(inputDevice);
71 | }
72 |
73 | public GameControllerListener GetListener() {
74 | return mListener;
75 | }
76 |
77 | public void SetListener(GameControllerListener listener) {
78 | mListener = listener;
79 | }
80 |
81 | public int GetGameControllerDeviceId() {
82 | return mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_DEVICEID];
83 | }
84 |
85 | public int GetGameControllerFlags() {
86 | return mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_DEVICEFLAGS];
87 | }
88 |
89 | public int[] GetGameControllerDeviceInfoArray() {
90 | return mGameControllerDeviceInfoArray;
91 | }
92 |
93 | public float[] GetGameControllerAxisMinArray() {
94 | return mGameControllerAxisMinArray;
95 | }
96 |
97 | public float[] GetGameControllerAxisMaxArray() {
98 | return mGameControllerAxisMaxArray;
99 | }
100 |
101 | public float[] GetGameControllerAxisFlatArray() {
102 | return mGameControllerAxisFlatArray;
103 | }
104 |
105 | public float[] GetGameControllerAxisFuzzArray() {
106 | return mGameControllerAxisFuzzArray;
107 | }
108 |
109 | public String GetGameControllerNameString() {
110 | return mGameControllerNameString;
111 | }
112 |
113 | private void EnumerateAxis(InputDevice inputDevice) {
114 | List motionRanges = inputDevice.getMotionRanges();
115 | for (InputDevice.MotionRange motionRange : motionRanges) {
116 | int axisIndex = motionRange.getAxis();
117 | if (axisIndex >= 0 && axisIndex < MAX_AXIS_COUNT) {
118 | int axisSource = motionRange.getSource();
119 | if (axisSource == InputDevice.SOURCE_JOYSTICK ||
120 | axisSource == InputDevice.SOURCE_GAMEPAD) {
121 | if (axisIndex <= AXIS_COUNT_LOW) {
122 | mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_AXISBITS_LOW] |=
123 | (1 << axisIndex);
124 | } else {
125 | mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_AXISBITS_HIGH] |=
126 | (1 << (axisIndex - (AXIS_COUNT_LOW + 1)));
127 | }
128 | mGameControllerAxisMinArray[axisIndex] = motionRange.getMin();
129 | mGameControllerAxisMaxArray[axisIndex] = motionRange.getMax();
130 | mGameControllerAxisFlatArray[axisIndex] = motionRange.getFlat();
131 | mGameControllerAxisFuzzArray[axisIndex] = motionRange.getFuzz();
132 | }
133 | }
134 | }
135 | }
136 |
137 | private void EnumerateInfoArray(InputDevice inputDevice) {
138 | mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_DEVICEID] = inputDevice.getId();
139 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
140 | mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_VENDORID] = inputDevice.getVendorId();
141 | mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_PRODUCTID] = inputDevice.getProductId();
142 | mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_CONTROLLERNUMBER] =
143 | inputDevice.getControllerNumber();
144 | }
145 | mGameControllerDeviceInfoArray[DEVICEINFO_INDEX_DEVICEFLAGS] =
146 | GameControllerManager.getControllerFlagsForDevice(inputDevice);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/paddleboat_c.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "GameControllerManager.h"
18 | #include "paddleboat.h"
19 |
20 | using namespace paddleboat;
21 |
22 | extern "C" {
23 |
24 | // Internal macros to track Paddleboat version, do not use directly.
25 | #define PADDLEBOAT_MAJOR_VERSION 1
26 | #define PADDLEBOAT_MINOR_VERSION 1
27 | #define PADDLEBOAT_BUGFIX_VERSION 0
28 |
29 | #define PADDLEBOAT_PACKED_VERSION \
30 | ((PADDLEBOAT_MAJOR_VERSION << 24) | (PADDLEBOAT_MINOR_VERSION << 16) | \
31 | (PADDLEBOAT_BUGFIX_VERSION))
32 |
33 | #define PADDLEBOAT_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX) \
34 | PREFIX##_##MAJOR##_##MINOR##_##BUGFIX
35 | #define PADDLEBOAT_VERSION_CONCAT(PREFIX, MAJOR, MINOR, BUGFIX) \
36 | PADDLEBOAT_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX)
37 | #define PADDLEBOAT_VERSION_SYMBOL \
38 | PADDLEBOAT_VERSION_CONCAT(PADDLEBOAT_version, PADDLEBOAT_MAJOR_VERSION, \
39 | PADDLEBOAT_MINOR_VERSION, \
40 | PADDLEBOAT_BUGFIX_VERSION)
41 |
42 | void PADDLEBOAT_VERSION_SYMBOL();
43 |
44 | Paddleboat_ErrorCode Paddleboat_init(JNIEnv *env, jobject jcontext) {
45 | PADDLEBOAT_VERSION_SYMBOL();
46 | Paddleboat_ErrorCode errorCode = GameControllerManager::init(env, jcontext);
47 | if (errorCode == PADDLEBOAT_NO_ERROR) {
48 | GameControllerManager::update(env);
49 | }
50 | return errorCode;
51 | }
52 |
53 | bool Paddleboat_isInitialized() {
54 | return GameControllerManager::isInitialized();
55 | }
56 |
57 | void Paddleboat_destroy(JNIEnv *env) {
58 | GameControllerManager::destroyInstance(env);
59 | }
60 |
61 | void Paddleboat_onStop(JNIEnv *env) { GameControllerManager::onStop(env); }
62 |
63 | void Paddleboat_onStart(JNIEnv *env) { GameControllerManager::onStart(env); }
64 |
65 | int32_t Paddleboat_processInputEvent(const AInputEvent *event) {
66 | return GameControllerManager::processInputEvent(event);
67 | }
68 |
69 | int32_t Paddleboat_processGameActivityKeyInputEvent(const void *event,
70 | const size_t eventSize) {
71 | return GameControllerManager::processGameActivityKeyInputEvent(event,
72 | eventSize);
73 | }
74 |
75 | int32_t Paddleboat_processGameActivityMotionInputEvent(const void *event,
76 | const size_t eventSize) {
77 | return GameControllerManager::processGameActivityMotionInputEvent(
78 | event, eventSize);
79 | }
80 |
81 | uint64_t Paddleboat_getActiveAxisMask() {
82 | return GameControllerManager::getActiveAxisMask();
83 | }
84 |
85 | bool Paddleboat_getBackButtonConsumed() {
86 | return GameControllerManager::getBackButtonConsumed();
87 | }
88 |
89 | void Paddleboat_setBackButtonConsumed(bool consumeBackButton) {
90 | GameControllerManager::setBackButtonConsumed(consumeBackButton);
91 | }
92 |
93 | void Paddleboat_setControllerStatusCallback(
94 | Paddleboat_ControllerStatusCallback statusCallback, void *userData) {
95 | GameControllerManager::setControllerStatusCallback(statusCallback,
96 | userData);
97 | }
98 |
99 | void Paddleboat_setMotionDataCallback(
100 | Paddleboat_MotionDataCallback motionDataCallback, void *userData) {
101 | GameControllerManager::setMotionDataCallback(motionDataCallback, userData);
102 | }
103 |
104 | void Paddleboat_setMouseStatusCallback(
105 | Paddleboat_MouseStatusCallback statusCallback, void *userData) {
106 | GameControllerManager::setMouseStatusCallback(statusCallback, userData);
107 | }
108 |
109 | Paddleboat_ErrorCode Paddleboat_getControllerData(
110 | const int32_t controllerIndex, Paddleboat_Controller_Data *controllerData) {
111 | return GameControllerManager::getControllerData(controllerIndex,
112 | controllerData);
113 | }
114 |
115 | Paddleboat_ErrorCode Paddleboat_getControllerInfo(
116 | const int32_t controllerIndex, Paddleboat_Controller_Info *controllerInfo) {
117 | return GameControllerManager::getControllerInfo(controllerIndex,
118 | controllerInfo);
119 | }
120 |
121 | Paddleboat_ErrorCode Paddleboat_getControllerName(const int32_t controllerIndex,
122 | const size_t bufferSize,
123 | char *controllerName) {
124 | return GameControllerManager::getControllerName(controllerIndex, bufferSize,
125 | controllerName);
126 | }
127 |
128 | Paddleboat_ControllerStatus Paddleboat_getControllerStatus(
129 | const int32_t controllerIndex) {
130 | return GameControllerManager::getControllerStatus(controllerIndex);
131 | }
132 |
133 | Paddleboat_ErrorCode Paddleboat_setControllerLight(
134 | const int32_t controllerIndex, const Paddleboat_LightType lightType,
135 | const uint32_t lightData, JNIEnv *env) {
136 | return GameControllerManager::setControllerLight(controllerIndex, lightType,
137 | lightData, env);
138 | }
139 |
140 | Paddleboat_ErrorCode Paddleboat_setControllerVibrationData(
141 | const int32_t controllerIndex,
142 | const Paddleboat_Vibration_Data *vibrationData, JNIEnv *env) {
143 | return GameControllerManager::setControllerVibrationData(
144 | controllerIndex, vibrationData, env);
145 | }
146 |
147 | Paddleboat_ErrorCode Paddleboat_getMouseData(Paddleboat_Mouse_Data *mouseData) {
148 | return GameControllerManager::getMouseData(mouseData);
149 | }
150 |
151 | Paddleboat_MouseStatus Paddleboat_getMouseStatus() {
152 | return GameControllerManager::getMouseStatus();
153 | }
154 |
155 | void Paddleboat_addControllerRemapData(
156 | const Paddleboat_Remap_Addition_Mode addMode,
157 | const int32_t remapTableEntryCount,
158 | const Paddleboat_Controller_Mapping_Data *mappingData) {
159 | GameControllerManager::addControllerRemapData(addMode, remapTableEntryCount,
160 | mappingData);
161 | }
162 |
163 | int32_t Paddleboat_getControllerRemapTableData(
164 | const int32_t destRemapTableEntryCount,
165 | Paddleboat_Controller_Mapping_Data *mappingData) {
166 | return GameControllerManager::getControllerRemapTableData(
167 | destRemapTableEntryCount, mappingData);
168 | }
169 |
170 | void Paddleboat_update(JNIEnv *env) { GameControllerManager::update(env); }
171 |
172 | int32_t Paddleboat_getLastKeycode() {
173 | return GameControllerManager::getLastKeycode();
174 | }
175 |
176 | void PADDLEBOAT_VERSION_SYMBOL() {
177 | // Intentionally empty: this function is used to ensure that the proper
178 | // version of the library is linked against the proper headers.
179 | // In case of mismatch, a linker error will be triggered because of an
180 | // undefined symbol, as the name of the function depends on the version.
181 | }
182 | } // extern "C" {
--------------------------------------------------------------------------------
/src/extras/src/main/java/com/google/androidgamesdk/SwappyDisplayManager.java:
--------------------------------------------------------------------------------
1 | package com.google.androidgamesdk;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.content.ComponentName;
6 | import android.content.pm.ActivityInfo;
7 | import android.content.pm.PackageManager;
8 | import android.hardware.display.DisplayManager;
9 | import android.os.Build;
10 | import android.os.Handler;
11 | import android.os.Looper;
12 | import android.util.Log;
13 | import android.view.Display;
14 | import android.view.Window;
15 | import android.view.WindowManager;
16 |
17 | import java.util.concurrent.locks.Condition;
18 | import java.util.concurrent.locks.Lock;
19 | import java.util.concurrent.locks.ReentrantLock;
20 |
21 | import static android.app.NativeActivity.META_DATA_LIB_NAME;
22 |
23 | public class SwappyDisplayManager implements DisplayManager.DisplayListener {
24 | final private String LOG_TAG = "SwappyDisplayManager";
25 | final private boolean DEBUG = false;
26 | final private long ONE_MS_IN_NS = 1000000;
27 | final private long ONE_S_IN_NS = ONE_MS_IN_NS * 1000;
28 |
29 | private long mCookie;
30 | private Activity mActivity;
31 | private WindowManager mWindowManager;
32 | private Display.Mode mCurrentMode;
33 |
34 | private LooperThread mLooper;
35 |
36 | private class LooperThread extends Thread {
37 | public Handler mHandler;
38 | private Lock mLock = new ReentrantLock();
39 | private Condition mCondition = mLock.newCondition();
40 |
41 | @Override
42 | public void start() {
43 | mLock.lock();
44 | super.start();
45 | try {
46 | mCondition.await();
47 | } catch (InterruptedException e) {
48 | e.printStackTrace();
49 | }
50 | mLock.unlock();
51 |
52 | }
53 |
54 | public void run() {
55 | Log.i(LOG_TAG, "Starting looper thread");
56 |
57 | mLock.lock();
58 | Looper.prepare();
59 | mHandler = new Handler();
60 | mCondition.signal();
61 | mLock.unlock();
62 |
63 | Looper.loop();
64 |
65 | Log.i(LOG_TAG, "Terminating looper thread");
66 | }
67 | }
68 |
69 | @TargetApi(Build.VERSION_CODES.M)
70 | private boolean modeMatchesCurrentResolution(Display.Mode mode) {
71 | return mode.getPhysicalHeight() == mCurrentMode.getPhysicalHeight() &&
72 | mode.getPhysicalWidth() == mCurrentMode.getPhysicalWidth();
73 |
74 | }
75 |
76 | public SwappyDisplayManager(long cookie, Activity activity) {
77 | // Load the native library for cases where an NDK application is running
78 | // without a java componenet
79 | try {
80 | ActivityInfo ai = activity.getPackageManager().getActivityInfo(
81 | activity.getIntent().getComponent(), PackageManager.GET_META_DATA);
82 | if (ai.metaData != null) {
83 | String nativeLibName = ai.metaData.getString(META_DATA_LIB_NAME);
84 | if (nativeLibName != null) {
85 | System.loadLibrary(nativeLibName);
86 | }
87 | }
88 | } catch (java.lang.Throwable e) {
89 | Log.e(LOG_TAG, e.getMessage());
90 | }
91 |
92 | mCookie = cookie;
93 | mActivity = activity;
94 |
95 | mWindowManager = mActivity.getSystemService(WindowManager.class);
96 | Display display = mWindowManager.getDefaultDisplay();
97 | mCurrentMode = display.getMode();
98 | updateSupportedRefreshRates(display);
99 |
100 | // Register display listener callbacks
101 | DisplayManager dm = mActivity.getSystemService(DisplayManager.class);
102 |
103 | synchronized(this) {
104 | mLooper = new LooperThread();
105 | mLooper.start();
106 | dm.registerDisplayListener(this, mLooper.mHandler);
107 | }
108 | }
109 |
110 | private void updateSupportedRefreshRates(Display display) {
111 | Display.Mode[] supportedModes = display.getSupportedModes();
112 | int totalModes = 0;
113 | for (int i = 0; i < supportedModes.length; i++) {
114 | if (!modeMatchesCurrentResolution(supportedModes[i])) {
115 | continue;
116 | }
117 | totalModes++;
118 | }
119 |
120 | long[] supportedRefreshPeriods = new long[totalModes];
121 | int[] supportedDisplayModeIds = new int[totalModes];
122 | totalModes = 0;
123 | for (int i = 0; i < supportedModes.length; i++) {
124 | if (!modeMatchesCurrentResolution(supportedModes[i])) {
125 | continue;
126 | }
127 | supportedRefreshPeriods[totalModes] =
128 | (long) (ONE_S_IN_NS / supportedModes[i].getRefreshRate());
129 | supportedDisplayModeIds[totalModes] = supportedModes[i].getModeId();
130 | totalModes++;
131 |
132 | }
133 | // Call down to native to set the supported refresh rates
134 | nSetSupportedRefreshPeriods(mCookie, supportedRefreshPeriods, supportedDisplayModeIds);
135 | }
136 |
137 | public void setPreferredDisplayModeId(final int modeId) {
138 | mActivity.runOnUiThread(new Runnable() {
139 | @Override
140 | public void run() {
141 | Window w = mActivity.getWindow();
142 | WindowManager.LayoutParams l = w.getAttributes();
143 | if (DEBUG) {
144 | Log.v(LOG_TAG, "set preferredDisplayModeId to " + modeId);
145 | }
146 | l.preferredDisplayModeId = modeId;
147 |
148 |
149 | w.setAttributes(l);
150 | }
151 | });
152 | }
153 |
154 | public void terminate() {
155 | mLooper.mHandler.getLooper().quit();
156 | }
157 |
158 | @Override
159 | public void onDisplayAdded(int displayId) {
160 |
161 | }
162 |
163 | @Override
164 | public void onDisplayRemoved(int displayId) {
165 |
166 | }
167 |
168 | @Override
169 | public void onDisplayChanged(int displayId) {
170 | synchronized(this) {
171 | Display display = mWindowManager.getDefaultDisplay();
172 | float newRefreshRate = display.getRefreshRate();
173 | Display.Mode newMode = display.getMode();
174 | boolean resolutionChanged =
175 | (newMode.getPhysicalWidth() != mCurrentMode.getPhysicalWidth()) |
176 | (newMode.getPhysicalHeight() != mCurrentMode.getPhysicalHeight());
177 | boolean refreshRateChanged = (newRefreshRate != mCurrentMode.getRefreshRate());
178 | mCurrentMode = newMode;
179 |
180 | if (resolutionChanged) {
181 | updateSupportedRefreshRates(display);
182 | }
183 |
184 | if (refreshRateChanged) {
185 | final long appVsyncOffsetNanos = display.getAppVsyncOffsetNanos();
186 | final long vsyncPresentationDeadlineNanos =
187 | mWindowManager.getDefaultDisplay().getPresentationDeadlineNanos();
188 |
189 | final long vsyncPeriodNanos = (long)(ONE_S_IN_NS / newRefreshRate);
190 | final long sfVsyncOffsetNanos =
191 | vsyncPeriodNanos - (vsyncPresentationDeadlineNanos - ONE_MS_IN_NS);
192 |
193 | nOnRefreshPeriodChanged(mCookie,
194 | vsyncPeriodNanos,
195 | appVsyncOffsetNanos,
196 | sfVsyncOffsetNanos);
197 | }
198 | }
199 | }
200 |
201 | private native void nSetSupportedRefreshPeriods(long cookie,
202 | long[] refreshPeriods,
203 | int[] modeIds);
204 | private native void nOnRefreshPeriodChanged(long cookie,
205 | long refreshPeriod,
206 | long appOffset,
207 | long sfOffset);
208 | }
209 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerMappingUtils.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "GameControllerMappingUtils.h"
18 |
19 | #include "GameControllerManager.h"
20 |
21 | namespace paddleboat {
22 | MappingTableSearch::MappingTableSearch()
23 | : mappingRoot(nullptr),
24 | vendorId(0),
25 | productId(0),
26 | minApi(0),
27 | maxApi(0),
28 | tableIndex(0),
29 | mapEntryCount(0),
30 | tableEntryCount(0),
31 | tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
32 |
33 | MappingTableSearch::MappingTableSearch(
34 | Paddleboat_Controller_Mapping_Data *mapRoot, int32_t entryCount)
35 | : mappingRoot(mapRoot),
36 | vendorId(0),
37 | productId(0),
38 | minApi(0),
39 | maxApi(0),
40 | tableIndex(0),
41 | mapEntryCount(0),
42 | tableEntryCount(entryCount),
43 | tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
44 |
45 | void MappingTableSearch::initSearchParameters(const int32_t newVendorId,
46 | const int32_t newProductId,
47 | const int32_t newMinApi,
48 | const int32_t newMaxApi) {
49 | vendorId = newVendorId;
50 | productId = newProductId;
51 | minApi = newMinApi;
52 | maxApi = newMaxApi;
53 | tableIndex = 0;
54 | }
55 |
56 | bool GameControllerMappingUtils::findMatchingMapEntry(
57 | MappingTableSearch *searchEntry) {
58 | int32_t currentIndex = 0;
59 |
60 | // Starting out with a linear search. Updating the map table is something
61 | // that should only ever be done once at startup, if it actually takes an
62 | // appreciable time to execute when working with a big remap dictionary,
63 | // this is low-hanging fruit to optimize.
64 | const Paddleboat_Controller_Mapping_Data *mapRoot =
65 | searchEntry->mappingRoot;
66 | while (currentIndex < searchEntry->tableEntryCount) {
67 | const Paddleboat_Controller_Mapping_Data &mapEntry =
68 | mapRoot[currentIndex];
69 | if (mapEntry.vendorId > searchEntry->vendorId) {
70 | // Passed by the search vendorId value, so we don't already exist in
71 | // the table, set the current index as the insert point and bail
72 | searchEntry->tableIndex = currentIndex;
73 | return false;
74 | } else if (searchEntry->vendorId == mapEntry.vendorId) {
75 | if (mapEntry.productId > searchEntry->productId) {
76 | // Passed by the search productId value, so we don't already
77 | // exist in the table, set the current index as the insert point
78 | // and bail
79 | searchEntry->tableIndex = currentIndex;
80 | return false;
81 | } else if (searchEntry->productId == mapEntry.productId) {
82 | // Any overlap of the min/max API range is treated as matching
83 | // an existing entry
84 | if ((searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
85 | searchEntry->minApi <=
86 | mapEntry.maximumEffectiveApiLevel) ||
87 | (searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
88 | mapEntry.maximumEffectiveApiLevel == 0)) {
89 | searchEntry->tableIndex = currentIndex;
90 | return true;
91 | }
92 | }
93 | }
94 | ++currentIndex;
95 | }
96 | searchEntry->tableIndex = currentIndex;
97 | return false;
98 | }
99 |
100 | bool GameControllerMappingUtils::insertMapEntry(
101 | const Paddleboat_Controller_Mapping_Data *mappingData,
102 | MappingTableSearch *searchEntry) {
103 | bool insertSuccess = false;
104 | // Verify there is room in the table for another entry
105 | if (searchEntry->tableEntryCount < searchEntry->tableMaxEntryCount &&
106 | searchEntry->tableIndex < searchEntry->tableMaxEntryCount) {
107 | // Empty table, or inserting at the end, no relocation of existing data
108 | // required, otherwise shift existing data down starting at the insert
109 | // index.
110 | if (!(searchEntry->tableEntryCount == 0 ||
111 | searchEntry->tableIndex == searchEntry->tableEntryCount)) {
112 | const size_t copySize =
113 | (searchEntry->tableEntryCount - searchEntry->tableIndex) *
114 | sizeof(Paddleboat_Controller_Mapping_Data);
115 | memmove(&searchEntry->mappingRoot[searchEntry->tableIndex + 1],
116 | &searchEntry->mappingRoot[searchEntry->tableIndex],
117 | copySize);
118 | }
119 | // Insert the new data
120 | memcpy(&searchEntry->mappingRoot[searchEntry->tableIndex], mappingData,
121 | sizeof(Paddleboat_Controller_Mapping_Data));
122 | insertSuccess = true;
123 | }
124 | return insertSuccess;
125 | }
126 |
127 | const Paddleboat_Controller_Mapping_Data *
128 | GameControllerMappingUtils::validateMapTable(
129 | const Paddleboat_Controller_Mapping_Data *mappingRoot,
130 | const int32_t tableEntryCount) {
131 | // The map table is always assumed to be sorted by increasing vendorId. Each
132 | // sequence of entries with the same vendorId are sorted by increasing
133 | // productId. Multiple entries with the same vendorId/productId range are
134 | // sorted by increasing min/max API ranges. vendorId
135 | // productId
136 | // minApi
137 | int32_t currentIndex = 0;
138 | int32_t previousVendorId = -1;
139 | while (currentIndex < tableEntryCount) {
140 | if (mappingRoot[currentIndex].vendorId < previousVendorId) {
141 | // failure in vendorId order, return the offending entry
142 | return &mappingRoot[currentIndex];
143 | }
144 |
145 | int32_t previousProductId = mappingRoot[currentIndex].productId;
146 | int32_t previousMinApi =
147 | mappingRoot[currentIndex].minimumEffectiveApiLevel;
148 | int32_t previousMaxApi =
149 | mappingRoot[currentIndex].maximumEffectiveApiLevel;
150 | previousVendorId = mappingRoot[currentIndex++].vendorId;
151 |
152 | while (currentIndex < tableEntryCount &&
153 | mappingRoot[currentIndex].vendorId == previousVendorId) {
154 | while (currentIndex < tableEntryCount &&
155 | mappingRoot[currentIndex].productId == previousProductId) {
156 | if (mappingRoot[currentIndex].minimumEffectiveApiLevel <
157 | previousMinApi ||
158 | mappingRoot[currentIndex].minimumEffectiveApiLevel <
159 | previousMaxApi) {
160 | // failure in API order, return the offending entry
161 | return &mappingRoot[currentIndex];
162 | }
163 | previousMinApi =
164 | mappingRoot[currentIndex].minimumEffectiveApiLevel;
165 | previousMaxApi =
166 | mappingRoot[currentIndex++].maximumEffectiveApiLevel;
167 | }
168 | if (mappingRoot[currentIndex].productId < previousProductId) {
169 | // failure in productId order, return the offending entry
170 | return &mappingRoot[currentIndex];
171 | }
172 | previousProductId = mappingRoot[currentIndex++].productId;
173 | }
174 | }
175 |
176 | // Validation success, return nullptr (no offending entries to return)
177 | return nullptr;
178 | }
179 | } // namespace paddleboat
180 |
--------------------------------------------------------------------------------
/GameController/src/main/cpp/GameControllerManager.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 | #include
21 |
22 | #include
23 |
24 | #include "GameController.h"
25 | #include "ThreadUtil.h"
26 |
27 | namespace paddleboat {
28 |
29 | class GameControllerManager {
30 | private:
31 | // Allows construction with std::unique_ptr from a static method, but
32 | // disallows construction outside of the class since no one else can
33 | // construct a ConstructorTag
34 | struct ConstructorTag {};
35 |
36 | static constexpr int32_t MAX_MOUSE_DEVICES = 2;
37 | static constexpr int32_t INVALID_MOUSE_ID = -1;
38 | static constexpr int32_t MAX_REMAP_TABLE_SIZE = 256;
39 |
40 | // Assuming update is getting called at 60Hz, wait one minute in between
41 | // checking battery status
42 | static constexpr int32_t BATTERY_REFRESH_WAIT = 60 * 60;
43 |
44 | public:
45 | GameControllerManager(JNIEnv *env, jobject jcontext, ConstructorTag);
46 |
47 | ~GameControllerManager();
48 |
49 | static inline int32_t getRemapTableSize() { return MAX_REMAP_TABLE_SIZE; }
50 |
51 | static Paddleboat_ErrorCode init(JNIEnv *env, jobject jcontext);
52 |
53 | static void destroyInstance(JNIEnv *env);
54 |
55 | static bool isInitialized();
56 |
57 | // Get/Set whether AKEYCODE_BACK is 'eaten' or allowed to pass through to
58 | // the system This can be used to block the OS backing out of the game, or
59 | // allowing it if the game is in an appropriate state (i.e. the title
60 | // screen)
61 | static bool getBackButtonConsumed();
62 |
63 | static void setBackButtonConsumed(bool consumed);
64 |
65 | static Paddleboat_ErrorCode setControllerLight(
66 | const int32_t controllerIndex, const Paddleboat_LightType lightType,
67 | const uint32_t lightData, JNIEnv *env);
68 |
69 | static void setControllerStatusCallback(
70 | Paddleboat_ControllerStatusCallback statusCallback, void *userData);
71 |
72 | static void setMotionDataCallback(
73 | Paddleboat_MotionDataCallback motionDataCallback, void *userData);
74 |
75 | static void setMouseStatusCallback(
76 | Paddleboat_MouseStatusCallback statusCallback, void *userData);
77 |
78 | static void onStop(JNIEnv *env);
79 |
80 | static void onStart(JNIEnv *env);
81 |
82 | static void terminate(JNIEnv *env);
83 |
84 | static void update(JNIEnv *env);
85 |
86 | static Paddleboat_ErrorCode getControllerData(
87 | const int32_t controllerIndex,
88 | Paddleboat_Controller_Data *controllerData);
89 |
90 | static Paddleboat_ErrorCode getControllerInfo(
91 | const int32_t controllerIndex, Paddleboat_Controller_Info *deviceInfo);
92 |
93 | static Paddleboat_ErrorCode getControllerName(const int32_t controllerIndex,
94 | const size_t bufferSize,
95 | char *controllerName);
96 |
97 | static Paddleboat_ControllerStatus getControllerStatus(
98 | const int32_t controllerIndex);
99 |
100 | static Paddleboat_ErrorCode setControllerVibrationData(
101 | const int32_t controllerIndex,
102 | const Paddleboat_Vibration_Data *vibrationData, JNIEnv *env);
103 |
104 | static Paddleboat_ErrorCode getMouseData(Paddleboat_Mouse_Data *mouseData);
105 |
106 | static Paddleboat_MouseStatus getMouseStatus();
107 |
108 | static int32_t processInputEvent(const AInputEvent *event);
109 |
110 | static int32_t processGameActivityKeyInputEvent(const void *event,
111 | const size_t eventSize);
112 |
113 | static int32_t processGameActivityMotionInputEvent(const void *event,
114 | const size_t eventSize);
115 |
116 | static uint64_t getActiveAxisMask();
117 |
118 | static void addControllerRemapData(
119 | const Paddleboat_Remap_Addition_Mode addMode,
120 | const int32_t remapTableEntryCount,
121 | const Paddleboat_Controller_Mapping_Data *mappingData);
122 |
123 | static int32_t getControllerRemapTableData(
124 | const int32_t destRemapTableEntryCount,
125 | Paddleboat_Controller_Mapping_Data *mappingData);
126 |
127 | // Called from the JNI bridge functions
128 | static GameControllerDeviceInfo *onConnection();
129 |
130 | static void onDisconnection(const int32_t deviceId);
131 |
132 | static void onMotionData(const int32_t deviceId, const int32_t motionType,
133 | const uint64_t timestamp, const float dataX,
134 | const float dataY, const float dataZ);
135 |
136 | static void onMouseConnection(const int32_t deviceId);
137 |
138 | static void onMouseDisconnection(const int32_t deviceId);
139 |
140 | static jclass getGameControllerClass();
141 |
142 | static jobject getGameControllerObject();
143 |
144 | // device debug helper function
145 | static int32_t getLastKeycode();
146 |
147 | private:
148 | static GameControllerManager *getInstance();
149 |
150 | Paddleboat_ErrorCode initMethods(JNIEnv *env);
151 |
152 | bool isLightTypeSupported(const Paddleboat_Controller_Info &controllerInfo,
153 | const Paddleboat_LightType lightType);
154 |
155 | int32_t processControllerKeyEvent(const AInputEvent *event,
156 | GameController &gameController);
157 |
158 | int32_t processControllerGameActivityKeyEvent(
159 | const Paddleboat_GameActivityKeyEvent *event, const size_t eventSize,
160 | GameController &gameController);
161 |
162 | int32_t processMouseEvent(const AInputEvent *event);
163 |
164 | int32_t processGameActivityMouseEvent(const void *event,
165 | const size_t eventSize,
166 | const int32_t eventDeviceId);
167 |
168 | void rescanVirtualMouseControllers();
169 |
170 | void updateBattery(JNIEnv *env);
171 |
172 | void updateMouseDataTimestamp();
173 |
174 | void releaseGlobals(JNIEnv *env);
175 |
176 | const Paddleboat_Controller_Mapping_Data *getMapForController(
177 | const GameController &gameController);
178 |
179 | bool mInitialized = false;
180 | bool mGCMClassInitialized = false;
181 | bool mBackButtonConsumed = true;
182 | bool mMotionEventReporting = false;
183 |
184 | int32_t mApiLevel = 16;
185 | int32_t mBatteryWait = BATTERY_REFRESH_WAIT;
186 | jobject mContext = NULL;
187 | jclass mGameControllerClass = NULL;
188 | jobject mGameControllerObject = NULL;
189 | jmethodID mInitMethodId = NULL;
190 | jmethodID mGetApiLevelMethodId = NULL;
191 | jmethodID mGetBatteryLevelMethodId = NULL;
192 | jmethodID mGetBatteryStatusMethodId = NULL;
193 | jmethodID mSetLightMethodId = NULL;
194 | jmethodID mSetNativeReadyMethodId = NULL;
195 | jmethodID mSetReportMotionEventsMethodId = NULL;
196 | jmethodID mSetVibrationMethodId = NULL;
197 |
198 | uint64_t mActiveAxisMask = 0;
199 |
200 | int32_t mRemapEntryCount = 0;
201 | Paddleboat_Controller_Mapping_Data mMappingTable[MAX_REMAP_TABLE_SIZE];
202 |
203 | Paddleboat_MotionDataCallback mMotionDataCallback = nullptr;
204 | void *mMotionDataCallbackUserData = nullptr;
205 |
206 | GameController mGameControllers[PADDLEBOAT_MAX_CONTROLLERS];
207 | Paddleboat_ControllerStatusCallback mStatusCallback = nullptr;
208 | void *mStatusCallbackUserData = nullptr;
209 | // device debug helper
210 | int32_t mLastKeyEventKeyCode = 0;
211 |
212 | Paddleboat_MouseStatus mMouseStatus = PADDLEBOAT_MOUSE_NONE;
213 | int32_t mMouseDeviceIds[MAX_MOUSE_DEVICES] = {INVALID_MOUSE_ID,
214 | INVALID_MOUSE_ID};
215 | int32_t mMouseControllerIndex = INVALID_MOUSE_ID;
216 | Paddleboat_Mouse_Data mMouseData;
217 | Paddleboat_MouseStatusCallback mMouseCallback = nullptr;
218 | void *mMouseCallbackUserData = nullptr;
219 |
220 | std::mutex mUpdateMutex;
221 | static std::mutex sInstanceMutex;
222 | static std::unique_ptr sInstance
223 | GUARDED_BY(sInstanceMutex);
224 | };
225 | } // namespace paddleboat
226 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * @defgroup game_text_input Game Text Input
19 | * The interface to use GameTextInput.
20 | * @{
21 | */
22 |
23 | #pragma once
24 |
25 | #include
26 | #include
27 | #include
28 |
29 | #include "gamecommon.h"
30 |
31 | #ifdef __cplusplus
32 | extern "C" {
33 | #endif
34 |
35 | /**
36 | * This struct holds a span within a region of text from start (inclusive) to
37 | * end (exclusive). An empty span or cursor position is specified with
38 | * start==end. An undefined span is specified with start = end = SPAN_UNDEFINED.
39 | */
40 | typedef struct GameTextInputSpan {
41 | /** The start of the region (inclusive). */
42 | int32_t start;
43 | /** The end of the region (exclusive). */
44 | int32_t end;
45 | } GameTextInputSpan;
46 |
47 | /**
48 | * Values with special meaning in a GameTextInputSpan.
49 | */
50 | enum GameTextInputSpanFlag { SPAN_UNDEFINED = -1 };
51 |
52 | /**
53 | * This struct holds the state of an editable section of text.
54 | * The text can have a selection and a composing region defined on it.
55 | * A composing region is used by IMEs that allow input using multiple steps to
56 | * compose a glyph or word. Use functions GameTextInput_getState and
57 | * GameTextInput_setState to read and modify the state that an IME is editing.
58 | */
59 | typedef struct GameTextInputState {
60 | /**
61 | * Text owned by the state, as a modified UTF-8 string. Null-terminated.
62 | * https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8
63 | */
64 | const char *text_UTF8;
65 | /**
66 | * Length in bytes of text_UTF8, *not* including the null at end.
67 | */
68 | int32_t text_length;
69 | /**
70 | * A selection defined on the text.
71 | */
72 | GameTextInputSpan selection;
73 | /**
74 | * A composing region defined on the text.
75 | */
76 | GameTextInputSpan composingRegion;
77 | } GameTextInputState;
78 |
79 | /**
80 | * A callback called by GameTextInput_getState.
81 | * @param context User-defined context.
82 | * @param state State, owned by the library, that will be valid for the duration
83 | * of the callback.
84 | */
85 | typedef void (*GameTextInputGetStateCallback)(
86 | void *context, const struct GameTextInputState *state);
87 |
88 | /**
89 | * Opaque handle to the GameTextInput API.
90 | */
91 | typedef struct GameTextInput GameTextInput;
92 |
93 | /**
94 | * Initialize the GameTextInput library.
95 | * If called twice without GameTextInput_destroy being called, the same pointer
96 | * will be returned and a warning will be issued.
97 | * @param env A JNI env valid on the calling thread.
98 | * @param max_string_size The maximum length of a string that can be edited. If
99 | * zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated
100 | * at initialization.
101 | * @return A handle to the library.
102 | */
103 | GameTextInput *GameTextInput_init(JNIEnv *env, uint32_t max_string_size);
104 |
105 | /**
106 | * When using GameTextInput, you need to create a gametextinput.InputConnection
107 | * on the Java side and pass it using this function to the library, unless using
108 | * GameActivity in which case this will be done for you. See the GameActivity
109 | * source code or GameTextInput samples for examples of usage.
110 | * @param input A valid GameTextInput library handle.
111 | * @param inputConnection A gametextinput.InputConnection object.
112 | */
113 | void GameTextInput_setInputConnection(GameTextInput *input,
114 | jobject inputConnection);
115 |
116 | /**
117 | * Unless using GameActivity, it is required to call this function from your
118 | * Java gametextinput.Listener.stateChanged method to convert eventState and
119 | * trigger any event callbacks. When using GameActivity, this does not need to
120 | * be called as event processing is handled by the Activity.
121 | * @param input A valid GameTextInput library handle.
122 | * @param eventState A Java gametextinput.State object.
123 | */
124 | void GameTextInput_processEvent(GameTextInput *input, jobject eventState);
125 |
126 | /**
127 | * Free any resources owned by the GameTextInput library.
128 | * Any subsequent calls to the library will fail until GameTextInput_init is
129 | * called again.
130 | * @param input A valid GameTextInput library handle.
131 | */
132 | void GameTextInput_destroy(GameTextInput *input);
133 |
134 | /**
135 | * Flags to be passed to GameTextInput_showIme.
136 | */
137 | enum ShowImeFlags {
138 | SHOW_IME_UNDEFINED = 0, // Default value.
139 | SHOW_IMPLICIT =
140 | 1, // Indicates that the user has forced the input method open so it
141 | // should not be closed until they explicitly do so.
142 | SHOW_FORCED = 2 // Indicates that this is an implicit request to show the
143 | // input window, not as the result of a direct request by
144 | // the user. The window may not be shown in this case.
145 | };
146 |
147 | /**
148 | * Show the IME. Calls InputMethodManager.showSoftInput().
149 | * @param input A valid GameTextInput library handle.
150 | * @param flags Defined in ShowImeFlags above. For more information see:
151 | * https://developer.android.com/reference/android/view/inputmethod/InputMethodManager
152 | */
153 | void GameTextInput_showIme(GameTextInput *input, uint32_t flags);
154 |
155 | /**
156 | * Flags to be passed to GameTextInput_hideIme.
157 | */
158 | enum HideImeFlags {
159 | HIDE_IME_UNDEFINED = 0, // Default value.
160 | HIDE_IMPLICIT_ONLY =
161 | 1, // Indicates that the soft input window should only be hidden if it
162 | // was not explicitly shown by the user.
163 | HIDE_NOT_ALWAYS =
164 | 2, // Indicates that the soft input window should normally be hidden,
165 | // unless it was originally shown with SHOW_FORCED.
166 | };
167 |
168 | /**
169 | * Show the IME. Calls InputMethodManager.hideSoftInputFromWindow().
170 | * @param input A valid GameTextInput library handle.
171 | * @param flags Defined in HideImeFlags above. For more information see:
172 | * https://developer.android.com/reference/android/view/inputmethod/InputMethodManager
173 | */
174 | void GameTextInput_hideIme(GameTextInput *input, uint32_t flags);
175 |
176 | /**
177 | * Call a callback with the current GameTextInput state, which may have been
178 | * modified by changes in the IME and calls to GameTextInput_setState. We use a
179 | * callback rather than returning the state in order to simplify ownership of
180 | * text_UTF8 strings. These strings are only valid during the calling of the
181 | * callback.
182 | * @param input A valid GameTextInput library handle.
183 | * @param callback A function that will be called with valid state.
184 | * @param context Context used by the callback.
185 | */
186 | void GameTextInput_getState(GameTextInput *input,
187 | GameTextInputGetStateCallback callback,
188 | void *context);
189 |
190 | /**
191 | * Set the current GameTextInput state. This state is reflected to any active
192 | * IME.
193 | * @param input A valid GameTextInput library handle.
194 | * @param state The state to set. Ownership is maintained by the caller and must
195 | * remain valid for the duration of the call.
196 | */
197 | void GameTextInput_setState(GameTextInput *input,
198 | const GameTextInputState *state);
199 |
200 | /**
201 | * Type of the callback needed by GameTextInput_setEventCallback that will be
202 | * called every time the IME state changes.
203 | * @param context User-defined context set in GameTextInput_setEventCallback.
204 | * @param current_state Current IME state, owned by the library and valid during
205 | * the callback.
206 | */
207 | typedef void (*GameTextInputEventCallback)(
208 | void *context, const GameTextInputState *current_state);
209 |
210 | /**
211 | * Optionally set a callback to be called whenever the IME state changes.
212 | * Not necessary if you are using GameActivity, which handles these callbacks
213 | * for you.
214 | * @param input A valid GameTextInput library handle.
215 | * @param callback Called by the library when the IME state changes.
216 | * @param context Context passed as first argument to the callback.
217 | */
218 | void GameTextInput_setEventCallback(GameTextInput *input,
219 | GameTextInputEventCallback callback,
220 | void *context);
221 |
222 | /**
223 | * Type of the callback needed by GameTextInput_setImeInsetsCallback that will
224 | * be called every time the IME window insets change.
225 | * @param context User-defined context set in
226 | * GameTextInput_setImeWIndowInsetsCallback.
227 | * @param current_insets Current IME insets, owned by the library and valid
228 | * during the callback.
229 | */
230 | typedef void (*GameTextInputImeInsetsCallback)(void *context,
231 | const ARect *current_insets);
232 |
233 | /**
234 | * Optionally set a callback to be called whenever the IME insets change.
235 | * Not necessary if you are using GameActivity, which handles these callbacks
236 | * for you.
237 | * @param input A valid GameTextInput library handle.
238 | * @param callback Called by the library when the IME insets change.
239 | * @param context Context passed as first argument to the callback.
240 | */
241 | void GameTextInput_setImeInsetsCallback(GameTextInput *input,
242 | GameTextInputImeInsetsCallback callback,
243 | void *context);
244 |
245 | /**
246 | * Get the current window insets for the IME.
247 | * @param input A valid GameTextInput library handle.
248 | * @param insets Filled with the current insets by this function.
249 | */
250 | void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets);
251 |
252 | /**
253 | * Unless using GameActivity, it is required to call this function from your
254 | * Java gametextinput.Listener.onImeInsetsChanged method to
255 | * trigger any event callbacks. When using GameActivity, this does not need to
256 | * be called as insets processing is handled by the Activity.
257 | * @param input A valid GameTextInput library handle.
258 | * @param eventState A Java gametextinput.State object.
259 | */
260 | void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets);
261 |
262 | /**
263 | * Convert a GameTextInputState struct to a Java gametextinput.State object.
264 | * Don't forget to delete the returned Java local ref when you're done.
265 | * @param input A valid GameTextInput library handle.
266 | * @param state Input state to convert.
267 | * @return A Java object of class gametextinput.State. The caller is required to
268 | * delete this local reference.
269 | */
270 | jobject GameTextInputState_toJava(const GameTextInput *input,
271 | const GameTextInputState *state);
272 |
273 | /**
274 | * Convert from a Java gametextinput.State object into a C GameTextInputState
275 | * struct.
276 | * @param input A valid GameTextInput library handle.
277 | * @param state A Java gametextinput.State object.
278 | * @param callback A function called with the C struct, valid for the duration
279 | * of the call.
280 | * @param context Context passed to the callback.
281 | */
282 | void GameTextInputState_fromJava(const GameTextInput *input, jobject state,
283 | GameTextInputGetStateCallback callback,
284 | void *context);
285 |
286 | #ifdef __cplusplus
287 | }
288 | #endif
289 |
290 | /** @} */
291 |
--------------------------------------------------------------------------------
/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerListener.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2021 The Android Open Source Project
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.android.games.paddleboat;
16 |
17 | import android.hardware.Sensor;
18 | import android.hardware.SensorDirectChannel;
19 | import android.hardware.SensorEvent;
20 | import android.hardware.SensorManager;
21 | import android.hardware.lights.Light;
22 | import android.hardware.lights.LightState;
23 | import android.hardware.lights.LightsManager;
24 | import android.hardware.lights.LightsRequest;
25 | import android.os.Build;
26 | import android.util.Log;
27 | import android.view.InputDevice;
28 |
29 |
30 | public class GameControllerListener {
31 |
32 | private static final String TAG = "GameControllerListener";
33 | private boolean reportMotionEvents;
34 | private int inputDeviceFlags;
35 | private int inputDeviceId;
36 | private final GameControllerManager gameControllerManager;
37 | private GameControllerAccelerometerListener accelerometerListener;
38 | private GameControllerGyroscopeListener gyroscopeListener;
39 | private InputDevice inputDevice;
40 | private final Object mLightLock = new Object();
41 | @GuardedBy("mLightLock")
42 | private LightsManager lightsManager;
43 | private LightsManager.LightsSession lightsSession;
44 | private Sensor accelerometer;
45 | private Sensor gyroscope;
46 | private final Object mSensorLock = new Object();
47 | @GuardedBy("mSensorLock")
48 | private SensorManager sensorManager;
49 | public GameControllerListener(GameControllerManager gcManager, InputDevice newDevice,
50 | int newFlags, boolean motionEvents) {
51 | gameControllerManager = gcManager;
52 | inputDevice = newDevice;
53 | inputDeviceFlags = newFlags;
54 | inputDeviceId = inputDevice.getId();
55 | reportMotionEvents = motionEvents;
56 | lightsManager = null;
57 | lightsSession = null;
58 | accelerometer = null;
59 | accelerometerListener = null;
60 | gyroscope = null;
61 | gyroscopeListener = null;
62 | sensorManager = null;
63 |
64 | configureMotion();
65 | }
66 |
67 | public void resetListener(InputDevice newDevice, int newFlags) {
68 | shutdownListener();
69 | inputDevice = newDevice;
70 | inputDeviceFlags = newFlags;
71 | inputDeviceId = newDevice.getId();
72 | configureMotion();
73 | }
74 |
75 | public void shutdownListener() {
76 | // Called when the device sends a disconnected or changed event
77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
78 | Log.d(TAG, "shutdownListener");
79 | synchronized (mLightLock) {
80 | if (lightsSession != null) {
81 | lightsSession.close();
82 | }
83 | lightsSession = null;
84 | lightsManager = null;
85 | }
86 |
87 | synchronized (mSensorLock) {
88 | if (sensorManager != null) {
89 | if (accelerometerListener != null) {
90 | sensorManager.unregisterListener(accelerometerListener);
91 | accelerometerListener = null;
92 | }
93 | if (gyroscopeListener != null) {
94 | sensorManager.unregisterListener((gyroscopeListener));
95 | gyroscopeListener = null;
96 | }
97 | }
98 | accelerometer = null;
99 | gyroscope = null;
100 | sensorManager = null;
101 | }
102 | }
103 | inputDeviceFlags = 0;
104 | inputDevice = null;
105 | }
106 |
107 | public void setReportMotionEvents() {
108 | reportMotionEvents = true;
109 | configureMotion();
110 | }
111 |
112 | public void setLight(int lightType, int lightValue) {
113 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
114 | synchronized (mLightLock) {
115 | // Avoid starting a light session until we actually make changes
116 | // to a light
117 | if (lightsManager == null) {
118 | configureLights();
119 | }
120 | if (lightsManager != null) {
121 | for (Light currentLight : lightsManager.getLights()) {
122 | if (lightType == GameControllerManager.LIGHT_TYPE_PLAYER &&
123 | currentLight.getType() == Light.LIGHT_TYPE_PLAYER_ID) {
124 | LightState.Builder stateBuilder = new LightState.Builder();
125 | stateBuilder.setPlayerId(lightValue);
126 | LightsRequest.Builder requestBuilder = new LightsRequest.Builder();
127 | requestBuilder.addLight(currentLight, stateBuilder.build());
128 | lightsSession.requestLights(requestBuilder.build());
129 | break;
130 | } else if (lightType == GameControllerManager.LIGHT_TYPE_RGB &&
131 | currentLight.hasRgbControl()) {
132 | LightState.Builder stateBuilder = new LightState.Builder();
133 | stateBuilder.setColor(lightValue);
134 | LightsRequest.Builder requestBuilder = new LightsRequest.Builder();
135 | requestBuilder.addLight(currentLight, stateBuilder.build());
136 | lightsSession.requestLights(requestBuilder.build());
137 | break;
138 | }
139 | }
140 | }
141 | }
142 | }
143 |
144 | }
145 |
146 | private void configureLights() {
147 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
148 | if ((inputDeviceFlags & (GameControllerManager.DEVICEFLAG_LIGHT_PLAYER |
149 | GameControllerManager.DEVICEFLAG_LIGHT_RGB)) != 0) {
150 | synchronized (mLightLock) {
151 | Log.d(TAG, "configureLights");
152 | lightsManager = inputDevice.getLightsManager();
153 | lightsSession = lightsManager.openSession();
154 | }
155 | }
156 | }
157 | }
158 |
159 | private void configureMotion() {
160 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && reportMotionEvents) {
161 | synchronized (mSensorLock) {
162 | sensorManager = inputDevice.getSensorManager();
163 | if ((inputDeviceFlags & (GameControllerManager.DEVICEFLAG_ACCELEROMETER |
164 | GameControllerManager.DEVICEFLAG_GYROSCOPE)) != 0) {
165 | accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
166 | if (accelerometer != null) {
167 | if (gameControllerManager.getPrintControllerInfo()) {
168 | printSensorInformation(accelerometer, "accelerometer");
169 | }
170 | accelerometerListener =
171 | new GameControllerAccelerometerListener(accelerometer);
172 | Log.d(TAG, "registering listener for accelerometer");
173 | sensorManager.registerListener(accelerometerListener, accelerometer,
174 | SensorManager.SENSOR_DELAY_GAME);
175 | }
176 | gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
177 | if (gyroscope != null) {
178 | if (gameControllerManager.getPrintControllerInfo()) {
179 | printSensorInformation(gyroscope, "gyroscope");
180 | }
181 | gyroscopeListener = new GameControllerGyroscopeListener(gyroscope);
182 | Log.d(TAG, "registering listener for gyroscope");
183 | sensorManager.registerListener(gyroscopeListener, gyroscope,
184 | SensorManager.SENSOR_DELAY_GAME);
185 | }
186 | }
187 | }
188 | }
189 | }
190 |
191 | private void printSensorInformation(Sensor sensor, String sensorName) {
192 | Log.d(TAG, "Registering listener for " + sensorName);
193 | Log.d(TAG, "Begin sensor information -----------------------------");
194 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
195 | Log.d(TAG, "getFifoMaxEventCount: " + sensor.getFifoMaxEventCount());
196 | Log.d(TAG, "getFifoReservedEventCount: " + sensor.getFifoReservedEventCount());
197 | }
198 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
199 | Log.d(TAG, "getHighestDirectReportRateLevel: " +
200 | sensor.getHighestDirectReportRateLevel());
201 | }
202 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
203 | Log.d(TAG, "getId: " + sensor.getId());
204 | }
205 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
206 | Log.d(TAG, "getMaxDelay: " + sensor.getMaxDelay());
207 | }
208 | Log.d(TAG, "getMaximumRange: " + sensor.getMaximumRange());
209 | Log.d(TAG, "getMinDelay: " + sensor.getMinDelay());
210 | Log.d(TAG, "getName: " + sensor.getName());
211 | Log.d(TAG, "getPower: " + sensor.getPower());
212 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
213 | Log.d(TAG, "getReportingMode: " + sensor.getReportingMode());
214 | }
215 | Log.d(TAG, "getVendor: " + sensor.getVendor());
216 | Log.d(TAG, "getVersion: " + sensor.getVersion());
217 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
218 | Log.d(TAG, "isAdditionalInfoSupported: " + sensor.isAdditionalInfoSupported());
219 | }
220 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
221 | boolean canMemoryFile =
222 | sensor.isDirectChannelTypeSupported(SensorDirectChannel.TYPE_MEMORY_FILE);
223 | boolean canHardwareBuffer =
224 | sensor.isDirectChannelTypeSupported(SensorDirectChannel.TYPE_HARDWARE_BUFFER);
225 | Log.d(TAG, "DirectChannel Memory File: " + canMemoryFile);
226 | Log.d(TAG, "DirectChannel Hardware Buffer: " + canHardwareBuffer);
227 | }
228 | Log.d(TAG, "End sensor information -------------------------------");
229 | }
230 |
231 | class GameControllerAccelerometerListener implements android.hardware.SensorEventListener {
232 | private final Sensor listenerAccelerometer;
233 |
234 | GameControllerAccelerometerListener(Sensor newAccelerometer) {
235 | listenerAccelerometer = newAccelerometer;
236 | }
237 |
238 | @Override
239 | public void onSensorChanged(SensorEvent event) {
240 | if (listenerAccelerometer != null) {
241 | synchronized (listenerAccelerometer) {
242 | if (event.sensor == listenerAccelerometer) {
243 | gameControllerManager.onMotionData(inputDeviceId,
244 | GameControllerManager.MOTION_ACCELEROMETER, event.timestamp,
245 | event.values[0], event.values[1], event.values[2]);
246 | }
247 | }
248 | }
249 | }
250 |
251 | @Override
252 | public void onAccuracyChanged(Sensor sensor, int accuracy) {
253 | }
254 | }
255 |
256 | class GameControllerGyroscopeListener implements android.hardware.SensorEventListener {
257 | private final Sensor listenerGyroscope;
258 |
259 | GameControllerGyroscopeListener(Sensor newGyroscope) {
260 | listenerGyroscope = newGyroscope;
261 | }
262 |
263 | @Override
264 | public void onSensorChanged(SensorEvent event) {
265 | if (listenerGyroscope != null) {
266 | synchronized (listenerGyroscope) {
267 | if (event.sensor == listenerGyroscope) {
268 | gameControllerManager.onMotionData(inputDeviceId,
269 | GameControllerManager.MOTION_GYROSCOPE, event.timestamp,
270 | event.values[0], event.values[1], event.values[2]);
271 | }
272 | }
273 | }
274 | }
275 |
276 | @Override
277 | public void onAccuracyChanged(Sensor sensor, int accuracy) {
278 |
279 | }
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * @addtogroup GameActivity Game Activity Events
19 | * The interface to use Game Activity Events.
20 | * @{
21 | */
22 |
23 | /**
24 | * @file GameActivityEvents.h
25 | */
26 | #ifndef ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H
27 | #define ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H
28 |
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #ifdef __cplusplus
36 | extern "C" {
37 | #endif
38 |
39 | /**
40 | * The maximum number of axes supported in an Android MotionEvent.
41 | * See https://developer.android.com/ndk/reference/group/input.
42 | */
43 | #define GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT 48
44 |
45 | /**
46 | * \brief Describe information about a pointer, found in a
47 | * GameActivityMotionEvent.
48 | *
49 | * You can read values directly from this structure, or use helper functions
50 | * (`GameActivityPointerAxes_getX`, `GameActivityPointerAxes_getY` and
51 | * `GameActivityPointerAxes_getAxisValue`).
52 | *
53 | * The X axis and Y axis are enabled by default but any other axis that you want
54 | * to read **must** be enabled first, using
55 | * `GameActivityPointerAxes_enableAxis`.
56 | *
57 | * \see GameActivityMotionEvent
58 | */
59 | typedef struct GameActivityPointerAxes {
60 | int32_t id;
61 | int32_t toolType;
62 | float axisValues[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
63 | float rawX;
64 | float rawY;
65 | } GameActivityPointerAxes;
66 |
67 | /** \brief Get the toolType of the pointer. */
68 | inline int32_t GameActivityPointerAxes_getToolType(
69 | const GameActivityPointerAxes* pointerInfo) {
70 | return pointerInfo->toolType;
71 | }
72 |
73 | /** \brief Get the current X coordinate of the pointer. */
74 | inline float GameActivityPointerAxes_getX(
75 | const GameActivityPointerAxes* pointerInfo) {
76 | return pointerInfo->axisValues[AMOTION_EVENT_AXIS_X];
77 | }
78 |
79 | /** \brief Get the current Y coordinate of the pointer. */
80 | inline float GameActivityPointerAxes_getY(
81 | const GameActivityPointerAxes* pointerInfo) {
82 | return pointerInfo->axisValues[AMOTION_EVENT_AXIS_Y];
83 | }
84 |
85 | /**
86 | * \brief Enable the specified axis, so that its value is reported in the
87 | * GameActivityPointerAxes structures stored in a motion event.
88 | *
89 | * You must enable any axis that you want to read, apart from
90 | * `AMOTION_EVENT_AXIS_X` and `AMOTION_EVENT_AXIS_Y` that are enabled by
91 | * default.
92 | *
93 | * If the axis index is out of range, nothing is done.
94 | */
95 | void GameActivityPointerAxes_enableAxis(int32_t axis);
96 |
97 | /**
98 | * \brief Disable the specified axis. Its value won't be reported in the
99 | * GameActivityPointerAxes structures stored in a motion event anymore.
100 | *
101 | * Apart from X and Y, any axis that you want to read **must** be enabled first,
102 | * using `GameActivityPointerAxes_enableAxis`.
103 | *
104 | * If the axis index is out of range, nothing is done.
105 | */
106 | void GameActivityPointerAxes_disableAxis(int32_t axis);
107 |
108 | /**
109 | * \brief Get the value of the requested axis.
110 | *
111 | * Apart from X and Y, any axis that you want to read **must** be enabled first,
112 | * using `GameActivityPointerAxes_enableAxis`.
113 | *
114 | * Find the valid enums for the axis (`AMOTION_EVENT_AXIS_X`,
115 | * `AMOTION_EVENT_AXIS_Y`, `AMOTION_EVENT_AXIS_PRESSURE`...)
116 | * in https://developer.android.com/ndk/reference/group/input.
117 | *
118 | * @param pointerInfo The structure containing information about the pointer,
119 | * obtained from GameActivityMotionEvent.
120 | * @param axis The axis to get the value from
121 | * @return The value of the axis, or 0 if the axis is invalid or was not
122 | * enabled.
123 | */
124 | float GameActivityPointerAxes_getAxisValue(
125 | const GameActivityPointerAxes* pointerInfo, int32_t axis);
126 |
127 | inline float GameActivityPointerAxes_getPressure(
128 | const GameActivityPointerAxes* pointerInfo) {
129 | return GameActivityPointerAxes_getAxisValue(pointerInfo,
130 | AMOTION_EVENT_AXIS_PRESSURE);
131 | }
132 |
133 | inline float GameActivityPointerAxes_getSize(
134 | const GameActivityPointerAxes* pointerInfo) {
135 | return GameActivityPointerAxes_getAxisValue(pointerInfo,
136 | AMOTION_EVENT_AXIS_SIZE);
137 | }
138 |
139 | inline float GameActivityPointerAxes_getTouchMajor(
140 | const GameActivityPointerAxes* pointerInfo) {
141 | return GameActivityPointerAxes_getAxisValue(pointerInfo,
142 | AMOTION_EVENT_AXIS_TOUCH_MAJOR);
143 | }
144 |
145 | inline float GameActivityPointerAxes_getTouchMinor(
146 | const GameActivityPointerAxes* pointerInfo) {
147 | return GameActivityPointerAxes_getAxisValue(pointerInfo,
148 | AMOTION_EVENT_AXIS_TOUCH_MINOR);
149 | }
150 |
151 | inline float GameActivityPointerAxes_getToolMajor(
152 | const GameActivityPointerAxes* pointerInfo) {
153 | return GameActivityPointerAxes_getAxisValue(pointerInfo,
154 | AMOTION_EVENT_AXIS_TOOL_MAJOR);
155 | }
156 |
157 | inline float GameActivityPointerAxes_getToolMinor(
158 | const GameActivityPointerAxes* pointerInfo) {
159 | return GameActivityPointerAxes_getAxisValue(pointerInfo,
160 | AMOTION_EVENT_AXIS_TOOL_MINOR);
161 | }
162 |
163 | inline float GameActivityPointerAxes_getOrientation(
164 | const GameActivityPointerAxes* pointerInfo) {
165 | return GameActivityPointerAxes_getAxisValue(pointerInfo,
166 | AMOTION_EVENT_AXIS_ORIENTATION);
167 | }
168 |
169 | /**
170 | * The maximum number of pointers returned inside a motion event.
171 | */
172 | #if (defined GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE)
173 | #define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT \
174 | GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE
175 | #else
176 | #define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT 8
177 | #endif
178 |
179 | /**
180 | * \brief Describe a motion event that happened on the GameActivity SurfaceView.
181 | *
182 | * This is 1:1 mapping to the information contained in a Java `MotionEvent`
183 | * (see https://developer.android.com/reference/android/view/MotionEvent).
184 | */
185 | typedef struct GameActivityMotionEvent {
186 | int32_t deviceId;
187 | int32_t source;
188 | int32_t action;
189 |
190 | int64_t eventTime;
191 | int64_t downTime;
192 |
193 | int32_t flags;
194 | int32_t metaState;
195 |
196 | int32_t actionButton;
197 | int32_t buttonState;
198 | int32_t classification;
199 | int32_t edgeFlags;
200 |
201 | uint32_t pointerCount;
202 | GameActivityPointerAxes
203 | pointers[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT];
204 |
205 | int historySize;
206 | long* historicalEventTimesMillis;
207 | long* historicalEventTimesNanos;
208 | float* historicalAxisValues;
209 |
210 | float precisionX;
211 | float precisionY;
212 | } GameActivityMotionEvent;
213 |
214 | float GameActivityMotionEvent_getHistoricalAxisValue(
215 | const GameActivityMotionEvent* event, int axis, int pointerIndex,
216 | int historyPos);
217 |
218 | inline int GameActivityMotionEvent_getHistorySize(
219 | const GameActivityMotionEvent* event) {
220 | return event->historySize;
221 | }
222 |
223 | inline float GameActivityMotionEvent_getHistoricalX(
224 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
225 | return GameActivityMotionEvent_getHistoricalAxisValue(
226 | event, AMOTION_EVENT_AXIS_X, pointerIndex, historyPos);
227 | }
228 |
229 | inline float GameActivityMotionEvent_getHistoricalY(
230 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
231 | return GameActivityMotionEvent_getHistoricalAxisValue(
232 | event, AMOTION_EVENT_AXIS_Y, pointerIndex, historyPos);
233 | }
234 |
235 | inline float GameActivityMotionEvent_getHistoricalPressure(
236 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
237 | return GameActivityMotionEvent_getHistoricalAxisValue(
238 | event, AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historyPos);
239 | }
240 |
241 | inline float GameActivityMotionEvent_getHistoricalSize(
242 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
243 | return GameActivityMotionEvent_getHistoricalAxisValue(
244 | event, AMOTION_EVENT_AXIS_SIZE, pointerIndex, historyPos);
245 | }
246 |
247 | inline float GameActivityMotionEvent_getHistoricalTouchMajor(
248 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
249 | return GameActivityMotionEvent_getHistoricalAxisValue(
250 | event, AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historyPos);
251 | }
252 |
253 | inline float GameActivityMotionEvent_getHistoricalTouchMinor(
254 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
255 | return GameActivityMotionEvent_getHistoricalAxisValue(
256 | event, AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historyPos);
257 | }
258 |
259 | inline float GameActivityMotionEvent_getHistoricalToolMajor(
260 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
261 | return GameActivityMotionEvent_getHistoricalAxisValue(
262 | event, AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historyPos);
263 | }
264 |
265 | inline float GameActivityMotionEvent_getHistoricalToolMinor(
266 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
267 | return GameActivityMotionEvent_getHistoricalAxisValue(
268 | event, AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historyPos);
269 | }
270 |
271 | inline float GameActivityMotionEvent_getHistoricalOrientation(
272 | const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
273 | return GameActivityMotionEvent_getHistoricalAxisValue(
274 | event, AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historyPos);
275 | }
276 |
277 | /** \brief Performs necessary initialization steps for GameActivityEvents.a
278 | *
279 | * User must call this function before calling any other functions of this unit.
280 | * If you use GameActivity it will call this function for you.
281 | */
282 | void GameActivityEventsInit(JNIEnv* env);
283 |
284 | /** \brief Handle the freeing of the GameActivityMotionEvent struct. */
285 | void GameActivityMotionEvent_destroy(GameActivityMotionEvent* c_event);
286 |
287 | /**
288 | * \brief Convert a Java `MotionEvent` to a `GameActivityMotionEvent`.
289 | *
290 | * This is done automatically by the GameActivity: see `onTouchEvent` to set
291 | * a callback to consume the received events.
292 | * This function can be used if you re-implement events handling in your own
293 | * activity.
294 | * Ownership of out_event is maintained by the caller.
295 | * Note that we pass as much information from Java Activity as possible
296 | * to avoid extra JNI calls.
297 | */
298 | void GameActivityMotionEvent_fromJava(
299 | JNIEnv* env, jobject motionEvent, GameActivityMotionEvent* out_event,
300 | int pointerCount, int historySize, int deviceId, int source, int action,
301 | int64_t eventTime, int64_t downTime, int flags, int metaState,
302 | int actionButton, int buttonState, int classification, int edgeFlags,
303 | float precisionX, float precisionY);
304 |
305 | /**
306 | * \brief Describe a key event that happened on the GameActivity SurfaceView.
307 | *
308 | * This is 1:1 mapping to the information contained in a Java `KeyEvent`
309 | * (see https://developer.android.com/reference/android/view/KeyEvent).
310 | * The only exception is the event times, which are reported as
311 | * nanoseconds in this struct.
312 | */
313 | typedef struct GameActivityKeyEvent {
314 | int32_t deviceId;
315 | int32_t source;
316 | int32_t action;
317 |
318 | int64_t eventTime;
319 | int64_t downTime;
320 |
321 | int32_t flags;
322 | int32_t metaState;
323 |
324 | int32_t modifiers;
325 | int32_t repeatCount;
326 | int32_t keyCode;
327 | int32_t scanCode;
328 | int32_t unicodeChar;
329 | } GameActivityKeyEvent;
330 |
331 | /**
332 | * \brief Convert a Java `KeyEvent` to a `GameActivityKeyEvent`.
333 | *
334 | * This is done automatically by the GameActivity: see `onKeyUp` and `onKeyDown`
335 | * to set a callback to consume the received events.
336 | * This function can be used if you re-implement events handling in your own
337 | * activity.
338 | * Ownership of out_event is maintained by the caller.
339 | */
340 | void GameActivityKeyEvent_fromJava(JNIEnv* env, jobject motionEvent,
341 | GameActivityKeyEvent* out_event);
342 |
343 | #ifdef __cplusplus
344 | }
345 | #endif
346 |
347 | /** @} */
348 |
349 | #endif // ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H
350 |
--------------------------------------------------------------------------------
/GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "GameActivityEvents.h"
18 |
19 | #include
20 |
21 | #include
22 |
23 | #include "GameActivityLog.h"
24 | #include "system_utils.h"
25 |
26 | static bool enabledAxes[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT] = {
27 | /* AMOTION_EVENT_AXIS_X */ true,
28 | /* AMOTION_EVENT_AXIS_Y */ true,
29 | // Disable all other axes by default (they can be enabled using
30 | // `GameActivityPointerAxes_enableAxis`).
31 | false};
32 |
33 | extern "C" void GameActivityPointerAxes_enableAxis(int32_t axis) {
34 | if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
35 | return;
36 | }
37 |
38 | enabledAxes[axis] = true;
39 | }
40 |
41 | float GameActivityPointerAxes_getAxisValue(
42 | const GameActivityPointerAxes *pointerInfo, int32_t axis) {
43 | if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
44 | return 0;
45 | }
46 |
47 | if (!enabledAxes[axis]) {
48 | ALOGW("Axis %d must be enabled before it can be accessed.", axis);
49 | return 0;
50 | }
51 |
52 | return pointerInfo->axisValues[axis];
53 | }
54 |
55 | extern "C" void GameActivityPointerAxes_disableAxis(int32_t axis) {
56 | if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
57 | return;
58 | }
59 |
60 | enabledAxes[axis] = false;
61 | }
62 |
63 | float GameActivityMotionEvent_getHistoricalAxisValue(
64 | const GameActivityMotionEvent *event, int axis, int pointerIndex,
65 | int historyPos) {
66 | if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
67 | return 0;
68 | }
69 |
70 | if (!enabledAxes[axis]) {
71 | ALOGW("Axis %d must be enabled before it can be accessed.", axis);
72 | return 0;
73 | }
74 |
75 | return event->historicalAxisValues[event->pointerCount * historyPos + axis];
76 | }
77 |
78 | static struct {
79 | jmethodID getDeviceId;
80 | jmethodID getSource;
81 | jmethodID getAction;
82 |
83 | jmethodID getEventTime;
84 | jmethodID getDownTime;
85 |
86 | jmethodID getFlags;
87 | jmethodID getMetaState;
88 |
89 | jmethodID getActionButton;
90 | jmethodID getButtonState;
91 | jmethodID getClassification;
92 | jmethodID getEdgeFlags;
93 |
94 | jmethodID getHistorySize;
95 | jmethodID getHistoricalEventTime;
96 |
97 | jmethodID getPointerCount;
98 | jmethodID getPointerId;
99 |
100 | jmethodID getToolType;
101 |
102 | jmethodID getRawX;
103 | jmethodID getRawY;
104 | jmethodID getXPrecision;
105 | jmethodID getYPrecision;
106 | jmethodID getAxisValue;
107 |
108 | jmethodID getHistoricalAxisValue;
109 | } gMotionEventClassInfo;
110 |
111 | extern "C" void GameActivityMotionEvent_destroy(
112 | GameActivityMotionEvent *c_event) {
113 | delete c_event->historicalAxisValues;
114 | delete c_event->historicalEventTimesMillis;
115 | delete c_event->historicalEventTimesNanos;
116 | }
117 |
118 | static void initMotionEvents(JNIEnv *env) {
119 | int sdkVersion = gamesdk::GetSystemPropAsInt("ro.build.version.sdk");
120 | gMotionEventClassInfo = {0};
121 | jclass motionEventClass = env->FindClass("android/view/MotionEvent");
122 | gMotionEventClassInfo.getDeviceId =
123 | env->GetMethodID(motionEventClass, "getDeviceId", "()I");
124 | gMotionEventClassInfo.getSource =
125 | env->GetMethodID(motionEventClass, "getSource", "()I");
126 | gMotionEventClassInfo.getAction =
127 | env->GetMethodID(motionEventClass, "getAction", "()I");
128 | gMotionEventClassInfo.getEventTime =
129 | env->GetMethodID(motionEventClass, "getEventTime", "()J");
130 | gMotionEventClassInfo.getDownTime =
131 | env->GetMethodID(motionEventClass, "getDownTime", "()J");
132 | gMotionEventClassInfo.getFlags =
133 | env->GetMethodID(motionEventClass, "getFlags", "()I");
134 | gMotionEventClassInfo.getMetaState =
135 | env->GetMethodID(motionEventClass, "getMetaState", "()I");
136 | if (sdkVersion >= 23) {
137 | gMotionEventClassInfo.getActionButton =
138 | env->GetMethodID(motionEventClass, "getActionButton", "()I");
139 | }
140 | if (sdkVersion >= 14) {
141 | gMotionEventClassInfo.getButtonState =
142 | env->GetMethodID(motionEventClass, "getButtonState", "()I");
143 | }
144 | if (sdkVersion >= 29) {
145 | gMotionEventClassInfo.getClassification =
146 | env->GetMethodID(motionEventClass, "getClassification", "()I");
147 | }
148 | gMotionEventClassInfo.getEdgeFlags =
149 | env->GetMethodID(motionEventClass, "getEdgeFlags", "()I");
150 |
151 | gMotionEventClassInfo.getHistorySize =
152 | env->GetMethodID(motionEventClass, "getHistorySize", "()I");
153 | gMotionEventClassInfo.getHistoricalEventTime =
154 | env->GetMethodID(motionEventClass, "getHistoricalEventTime", "(I)J");
155 |
156 | gMotionEventClassInfo.getPointerCount =
157 | env->GetMethodID(motionEventClass, "getPointerCount", "()I");
158 | gMotionEventClassInfo.getPointerId =
159 | env->GetMethodID(motionEventClass, "getPointerId", "(I)I");
160 | gMotionEventClassInfo.getToolType =
161 | env->GetMethodID(motionEventClass, "getToolType", "(I)I");
162 | if (sdkVersion >= 29) {
163 | gMotionEventClassInfo.getRawX =
164 | env->GetMethodID(motionEventClass, "getRawX", "(I)F");
165 | gMotionEventClassInfo.getRawY =
166 | env->GetMethodID(motionEventClass, "getRawY", "(I)F");
167 | }
168 | gMotionEventClassInfo.getXPrecision =
169 | env->GetMethodID(motionEventClass, "getXPrecision", "()F");
170 | gMotionEventClassInfo.getYPrecision =
171 | env->GetMethodID(motionEventClass, "getYPrecision", "()F");
172 | gMotionEventClassInfo.getAxisValue =
173 | env->GetMethodID(motionEventClass, "getAxisValue", "(II)F");
174 |
175 | gMotionEventClassInfo.getHistoricalAxisValue =
176 | env->GetMethodID(motionEventClass, "getHistoricalAxisValue", "(III)F");
177 | }
178 |
179 | extern "C" void GameActivityMotionEvent_fromJava(
180 | JNIEnv *env, jobject motionEvent, GameActivityMotionEvent *out_event,
181 | int pointerCount, int historySize, int deviceId, int source, int action,
182 | int64_t eventTime, int64_t downTime, int flags, int metaState,
183 | int actionButton, int buttonState, int classification, int edgeFlags,
184 | float precisionX, float precisionY) {
185 | pointerCount =
186 | std::min(pointerCount, GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT);
187 | out_event->pointerCount = pointerCount;
188 | for (int i = 0; i < pointerCount; ++i) {
189 | out_event->pointers[i] = {
190 | /*id=*/env->CallIntMethod(motionEvent,
191 | gMotionEventClassInfo.getPointerId, i),
192 | /*toolType=*/
193 | env->CallIntMethod(motionEvent, gMotionEventClassInfo.getToolType,
194 | i),
195 | /*axisValues=*/{0},
196 | /*rawX=*/gMotionEventClassInfo.getRawX
197 | ? env->CallFloatMethod(motionEvent,
198 | gMotionEventClassInfo.getRawX, i)
199 | : 0,
200 | /*rawY=*/gMotionEventClassInfo.getRawY
201 | ? env->CallFloatMethod(motionEvent,
202 | gMotionEventClassInfo.getRawY, i)
203 | : 0,
204 | };
205 |
206 | for (int axisIndex = 0;
207 | axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; ++axisIndex) {
208 | if (enabledAxes[axisIndex]) {
209 | out_event->pointers[i].axisValues[axisIndex] =
210 | env->CallFloatMethod(motionEvent,
211 | gMotionEventClassInfo.getAxisValue,
212 | axisIndex, i);
213 | }
214 | }
215 | }
216 |
217 | out_event->historySize = historySize;
218 | out_event->historicalAxisValues =
219 | new float[historySize * pointerCount *
220 | GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
221 | out_event->historicalEventTimesMillis = new long[historySize];
222 | out_event->historicalEventTimesNanos = new long[historySize];
223 |
224 | for (int historyIndex = 0; historyIndex < historySize; historyIndex++) {
225 | out_event->historicalEventTimesMillis[historyIndex] =
226 | env->CallLongMethod(motionEvent,
227 | gMotionEventClassInfo.getHistoricalEventTime,
228 | historyIndex);
229 | out_event->historicalEventTimesNanos[historyIndex] =
230 | out_event->historicalEventTimesMillis[historyIndex] * 1000000;
231 | for (int i = 0; i < pointerCount; ++i) {
232 | int pointerOffset = i * GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT;
233 | int historyAxisOffset = historyIndex * pointerCount *
234 | GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT;
235 | float *axisValues =
236 | &out_event
237 | ->historicalAxisValues[historyAxisOffset + pointerOffset];
238 | for (int axisIndex = 0;
239 | axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT;
240 | ++axisIndex) {
241 | if (enabledAxes[axisIndex]) {
242 | axisValues[axisIndex] = env->CallFloatMethod(
243 | motionEvent,
244 | gMotionEventClassInfo.getHistoricalAxisValue, axisIndex,
245 | i, historyIndex);
246 | }
247 | }
248 | }
249 | }
250 |
251 | out_event->deviceId = deviceId;
252 | out_event->source = source;
253 | out_event->action = action;
254 |
255 | out_event->eventTime = eventTime;
256 | out_event->downTime = downTime;
257 |
258 | out_event->flags = flags;
259 | out_event->metaState = metaState;
260 |
261 | out_event->actionButton = actionButton;
262 | out_event->buttonState = buttonState;
263 | out_event->classification = classification;
264 | out_event->edgeFlags = edgeFlags;
265 |
266 | out_event->precisionX = precisionX;
267 | out_event->precisionY = precisionY;
268 | }
269 |
270 | static struct {
271 | jmethodID getDeviceId;
272 | jmethodID getSource;
273 | jmethodID getAction;
274 |
275 | jmethodID getEventTime;
276 | jmethodID getDownTime;
277 |
278 | jmethodID getFlags;
279 | jmethodID getMetaState;
280 |
281 | jmethodID getModifiers;
282 | jmethodID getRepeatCount;
283 | jmethodID getKeyCode;
284 | jmethodID getScanCode;
285 | jmethodID getUnicodeChar;
286 | } gKeyEventClassInfo;
287 |
288 | static void initKeyEvents(JNIEnv *env) {
289 | int sdkVersion = gamesdk::GetSystemPropAsInt("ro.build.version.sdk");
290 | gKeyEventClassInfo = {0};
291 | jclass keyEventClass = env->FindClass("android/view/KeyEvent");
292 | gKeyEventClassInfo.getDeviceId =
293 | env->GetMethodID(keyEventClass, "getDeviceId", "()I");
294 | gKeyEventClassInfo.getSource =
295 | env->GetMethodID(keyEventClass, "getSource", "()I");
296 | gKeyEventClassInfo.getAction =
297 | env->GetMethodID(keyEventClass, "getAction", "()I");
298 | gKeyEventClassInfo.getEventTime =
299 | env->GetMethodID(keyEventClass, "getEventTime", "()J");
300 | gKeyEventClassInfo.getDownTime =
301 | env->GetMethodID(keyEventClass, "getDownTime", "()J");
302 | gKeyEventClassInfo.getFlags =
303 | env->GetMethodID(keyEventClass, "getFlags", "()I");
304 | gKeyEventClassInfo.getMetaState =
305 | env->GetMethodID(keyEventClass, "getMetaState", "()I");
306 | if (sdkVersion >= 13) {
307 | gKeyEventClassInfo.getModifiers =
308 | env->GetMethodID(keyEventClass, "getModifiers", "()I");
309 | }
310 | gKeyEventClassInfo.getRepeatCount =
311 | env->GetMethodID(keyEventClass, "getRepeatCount", "()I");
312 | gKeyEventClassInfo.getKeyCode =
313 | env->GetMethodID(keyEventClass, "getKeyCode", "()I");
314 | gKeyEventClassInfo.getScanCode =
315 | env->GetMethodID(keyEventClass, "getScanCode", "()I");
316 | gKeyEventClassInfo.getUnicodeChar =
317 | env->GetMethodID(keyEventClass, "getUnicodeChar", "()I");
318 | }
319 |
320 | extern "C" void GameActivityKeyEvent_fromJava(JNIEnv *env, jobject keyEvent,
321 | GameActivityKeyEvent *out_event) {
322 | *out_event = {
323 | /*deviceId=*/env->CallIntMethod(keyEvent,
324 | gKeyEventClassInfo.getDeviceId),
325 | /*source=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getSource),
326 | /*action=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getAction),
327 | // TODO: introduce a millisecondsToNanoseconds helper:
328 | /*eventTime=*/
329 | env->CallLongMethod(keyEvent, gKeyEventClassInfo.getEventTime) *
330 | 1000000,
331 | /*downTime=*/
332 | env->CallLongMethod(keyEvent, gKeyEventClassInfo.getDownTime) * 1000000,
333 | /*flags=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getFlags),
334 | /*metaState=*/
335 | env->CallIntMethod(keyEvent, gKeyEventClassInfo.getMetaState),
336 | /*modifiers=*/gKeyEventClassInfo.getModifiers
337 | ? env->CallIntMethod(keyEvent, gKeyEventClassInfo.getModifiers)
338 | : 0,
339 | /*repeatCount=*/
340 | env->CallIntMethod(keyEvent, gKeyEventClassInfo.getRepeatCount),
341 | /*keyCode=*/
342 | env->CallIntMethod(keyEvent, gKeyEventClassInfo.getKeyCode),
343 | /*scanCode=*/
344 | env->CallIntMethod(keyEvent, gKeyEventClassInfo.getScanCode),
345 | /*unicodeChar=*/
346 | env->CallIntMethod(keyEvent, gKeyEventClassInfo.getUnicodeChar)};
347 | }
348 |
349 | extern "C" void GameActivityEventsInit(JNIEnv *env) {
350 | initMotionEvents(env);
351 | initKeyEvents(env);
352 | }
353 |
--------------------------------------------------------------------------------
/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | #include "game-text-input/gametextinput.h"
17 |
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #include
24 | #include
25 | #include
26 |
27 | #define LOG_TAG "GameTextInput"
28 |
29 | static constexpr int32_t DEFAULT_MAX_STRING_SIZE = 1 << 16;
30 |
31 | // Cache of field ids in the Java GameTextInputState class
32 | struct StateClassInfo {
33 | jfieldID text;
34 | jfieldID selectionStart;
35 | jfieldID selectionEnd;
36 | jfieldID composingRegionStart;
37 | jfieldID composingRegionEnd;
38 | };
39 |
40 | // Main GameTextInput object.
41 | struct GameTextInput {
42 | public:
43 | GameTextInput(JNIEnv *env, uint32_t max_string_size);
44 | ~GameTextInput();
45 | void setState(const GameTextInputState &state);
46 | const GameTextInputState &getState() const { return currentState_; }
47 | void setInputConnection(jobject inputConnection);
48 | void processEvent(jobject textInputEvent);
49 | void showIme(uint32_t flags);
50 | void hideIme(uint32_t flags);
51 | void setEventCallback(GameTextInputEventCallback callback, void *context);
52 | jobject stateToJava(const GameTextInputState &state) const;
53 | void stateFromJava(jobject textInputEvent,
54 | GameTextInputGetStateCallback callback,
55 | void *context) const;
56 | void setImeInsetsCallback(GameTextInputImeInsetsCallback callback,
57 | void *context);
58 | void processImeInsets(const ARect *insets);
59 | const ARect &getImeInsets() const { return currentInsets_; }
60 |
61 | private:
62 | // Copy string and set other fields
63 | void setStateInner(const GameTextInputState &state);
64 | static void processCallback(void *context, const GameTextInputState *state);
65 | JNIEnv *env_ = nullptr;
66 | // Cached at initialization from
67 | // com/google/androidgamesdk/gametextinput/State.
68 | jclass stateJavaClass_ = nullptr;
69 | // The latest text input update.
70 | GameTextInputState currentState_ = {};
71 | // An instance of gametextinput.InputConnection.
72 | jclass inputConnectionClass_ = nullptr;
73 | jobject inputConnection_ = nullptr;
74 | jmethodID inputConnectionSetStateMethod_;
75 | jmethodID setSoftKeyboardActiveMethod_;
76 | void (*eventCallback_)(void *context,
77 | const struct GameTextInputState *state) = nullptr;
78 | void *eventCallbackContext_ = nullptr;
79 | void (*insetsCallback_)(void *context,
80 | const struct ARect *insets) = nullptr;
81 | ARect currentInsets_ = {};
82 | void *insetsCallbackContext_ = nullptr;
83 | StateClassInfo stateClassInfo_ = {};
84 | // Constant-sized buffer used to store state text.
85 | std::vector stateStringBuffer_;
86 | };
87 |
88 | std::unique_ptr s_gameTextInput;
89 |
90 | extern "C" {
91 |
92 | ///////////////////////////////////////////////////////////
93 | /// GameTextInputState C Functions
94 | ///////////////////////////////////////////////////////////
95 |
96 | // Convert to a Java structure.
97 | jobject currentState_toJava(const GameTextInput *gameTextInput,
98 | const GameTextInputState *state) {
99 | if (state == nullptr) return NULL;
100 | return gameTextInput->stateToJava(*state);
101 | }
102 |
103 | // Convert from Java structure.
104 | void currentState_fromJava(const GameTextInput *gameTextInput,
105 | jobject textInputEvent,
106 | GameTextInputGetStateCallback callback,
107 | void *context) {
108 | gameTextInput->stateFromJava(textInputEvent, callback, context);
109 | }
110 |
111 | ///////////////////////////////////////////////////////////
112 | /// GameTextInput C Functions
113 | ///////////////////////////////////////////////////////////
114 |
115 | struct GameTextInput *GameTextInput_init(JNIEnv *env,
116 | uint32_t max_string_size) {
117 | if (s_gameTextInput.get() != nullptr) {
118 | __android_log_print(ANDROID_LOG_WARN, LOG_TAG,
119 | "Warning: called GameTextInput_init twice without "
120 | "calling GameTextInput_destroy");
121 | return s_gameTextInput.get();
122 | }
123 | // Don't use make_unique, for C++11 compatibility
124 | s_gameTextInput =
125 | std::unique_ptr(new GameTextInput(env, max_string_size));
126 | return s_gameTextInput.get();
127 | }
128 |
129 | void GameTextInput_destroy(GameTextInput *input) {
130 | if (input == nullptr || s_gameTextInput.get() == nullptr) return;
131 | s_gameTextInput.reset();
132 | }
133 |
134 | void GameTextInput_setState(GameTextInput *input,
135 | const GameTextInputState *state) {
136 | if (state == nullptr) return;
137 | input->setState(*state);
138 | }
139 |
140 | void GameTextInput_getState(GameTextInput *input,
141 | GameTextInputGetStateCallback callback,
142 | void *context) {
143 | callback(context, &input->getState());
144 | }
145 |
146 | void GameTextInput_setInputConnection(GameTextInput *input,
147 | jobject inputConnection) {
148 | input->setInputConnection(inputConnection);
149 | }
150 |
151 | void GameTextInput_processEvent(GameTextInput *input, jobject textInputEvent) {
152 | input->processEvent(textInputEvent);
153 | }
154 |
155 | void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets) {
156 | input->processImeInsets(insets);
157 | }
158 |
159 | void GameTextInput_showIme(struct GameTextInput *input, uint32_t flags) {
160 | input->showIme(flags);
161 | }
162 |
163 | void GameTextInput_hideIme(struct GameTextInput *input, uint32_t flags) {
164 | input->hideIme(flags);
165 | }
166 |
167 | void GameTextInput_setEventCallback(struct GameTextInput *input,
168 | GameTextInputEventCallback callback,
169 | void *context) {
170 | input->setEventCallback(callback, context);
171 | }
172 |
173 | void GameTextInput_setImeInsetsCallback(struct GameTextInput *input,
174 | GameTextInputImeInsetsCallback callback,
175 | void *context) {
176 | input->setImeInsetsCallback(callback, context);
177 | }
178 |
179 | void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets) {
180 | *insets = input->getImeInsets();
181 | }
182 |
183 | } // extern "C"
184 |
185 | ///////////////////////////////////////////////////////////
186 | /// GameTextInput C++ class Implementation
187 | ///////////////////////////////////////////////////////////
188 |
189 | GameTextInput::GameTextInput(JNIEnv *env, uint32_t max_string_size)
190 | : env_(env),
191 | stateStringBuffer_(max_string_size == 0 ? DEFAULT_MAX_STRING_SIZE
192 | : max_string_size) {
193 | stateJavaClass_ = (jclass)env_->NewGlobalRef(
194 | env_->FindClass("com/google/androidgamesdk/gametextinput/State"));
195 | inputConnectionClass_ = (jclass)env_->NewGlobalRef(env_->FindClass(
196 | "com/google/androidgamesdk/gametextinput/InputConnection"));
197 | inputConnectionSetStateMethod_ =
198 | env_->GetMethodID(inputConnectionClass_, "setState",
199 | "(Lcom/google/androidgamesdk/gametextinput/State;)V");
200 | setSoftKeyboardActiveMethod_ = env_->GetMethodID(
201 | inputConnectionClass_, "setSoftKeyboardActive", "(ZI)V");
202 |
203 | stateClassInfo_.text =
204 | env_->GetFieldID(stateJavaClass_, "text", "Ljava/lang/String;");
205 | stateClassInfo_.selectionStart =
206 | env_->GetFieldID(stateJavaClass_, "selectionStart", "I");
207 | stateClassInfo_.selectionEnd =
208 | env_->GetFieldID(stateJavaClass_, "selectionEnd", "I");
209 | stateClassInfo_.composingRegionStart =
210 | env_->GetFieldID(stateJavaClass_, "composingRegionStart", "I");
211 | stateClassInfo_.composingRegionEnd =
212 | env_->GetFieldID(stateJavaClass_, "composingRegionEnd", "I");
213 |
214 | s_gameTextInput.get();
215 | }
216 |
217 | GameTextInput::~GameTextInput() {
218 | if (stateJavaClass_ != NULL) {
219 | env_->DeleteGlobalRef(stateJavaClass_);
220 | stateJavaClass_ = NULL;
221 | }
222 | if (inputConnectionClass_ != NULL) {
223 | env_->DeleteGlobalRef(inputConnectionClass_);
224 | inputConnectionClass_ = NULL;
225 | }
226 | if (inputConnection_ != NULL) {
227 | env_->DeleteGlobalRef(inputConnection_);
228 | inputConnection_ = NULL;
229 | }
230 | }
231 |
232 | void GameTextInput::setState(const GameTextInputState &state) {
233 | if (inputConnection_ == nullptr) return;
234 | jobject jstate = stateToJava(state);
235 | env_->CallVoidMethod(inputConnection_, inputConnectionSetStateMethod_,
236 | jstate);
237 | env_->DeleteLocalRef(jstate);
238 | setStateInner(state);
239 | }
240 |
241 | void GameTextInput::setStateInner(const GameTextInputState &state) {
242 | // Check if we're setting using our own string (other parts may be
243 | // different)
244 | if (state.text_UTF8 == currentState_.text_UTF8) {
245 | currentState_ = state;
246 | return;
247 | }
248 | // Otherwise, copy across the string.
249 | auto bytes_needed =
250 | std::min(static_cast(state.text_length + 1),
251 | static_cast(stateStringBuffer_.size()));
252 | currentState_.text_UTF8 = stateStringBuffer_.data();
253 | std::copy(state.text_UTF8, state.text_UTF8 + bytes_needed - 1,
254 | stateStringBuffer_.data());
255 | currentState_.text_length = state.text_length;
256 | currentState_.selection = state.selection;
257 | currentState_.composingRegion = state.composingRegion;
258 | stateStringBuffer_[bytes_needed - 1] = 0;
259 | }
260 |
261 | void GameTextInput::setInputConnection(jobject inputConnection) {
262 | if (inputConnection_ != NULL) {
263 | env_->DeleteGlobalRef(inputConnection_);
264 | }
265 | inputConnection_ = env_->NewGlobalRef(inputConnection);
266 | }
267 |
268 | /*static*/ void GameTextInput::processCallback(
269 | void *context, const GameTextInputState *state) {
270 | auto thiz = static_cast(context);
271 | if (state != nullptr) thiz->setStateInner(*state);
272 | }
273 |
274 | void GameTextInput::processEvent(jobject textInputEvent) {
275 | stateFromJava(textInputEvent, processCallback, this);
276 | if (eventCallback_) {
277 | eventCallback_(eventCallbackContext_, ¤tState_);
278 | }
279 | }
280 |
281 | void GameTextInput::showIme(uint32_t flags) {
282 | if (inputConnection_ == nullptr) return;
283 | env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, true,
284 | flags);
285 | }
286 |
287 | void GameTextInput::setEventCallback(GameTextInputEventCallback callback,
288 | void *context) {
289 | eventCallback_ = callback;
290 | eventCallbackContext_ = context;
291 | }
292 |
293 | void GameTextInput::setImeInsetsCallback(
294 | GameTextInputImeInsetsCallback callback, void *context) {
295 | insetsCallback_ = callback;
296 | insetsCallbackContext_ = context;
297 | }
298 |
299 | void GameTextInput::processImeInsets(const ARect *insets) {
300 | currentInsets_ = *insets;
301 | if (insetsCallback_) {
302 | insetsCallback_(insetsCallbackContext_, ¤tInsets_);
303 | }
304 | }
305 |
306 | void GameTextInput::hideIme(uint32_t flags) {
307 | if (inputConnection_ == nullptr) return;
308 | env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, false,
309 | flags);
310 | }
311 |
312 | jobject GameTextInput::stateToJava(const GameTextInputState &state) const {
313 | static jmethodID constructor = nullptr;
314 | if (constructor == nullptr) {
315 | constructor = env_->GetMethodID(stateJavaClass_, "",
316 | "(Ljava/lang/String;IIII)V");
317 | if (constructor == nullptr) {
318 | __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
319 | "Can't find gametextinput.State constructor");
320 | return nullptr;
321 | }
322 | }
323 | const char *text = state.text_UTF8;
324 | if (text == nullptr) {
325 | static char empty_string[] = "";
326 | text = empty_string;
327 | }
328 | // Note that this expects 'modified' UTF-8 which is not the same as UTF-8
329 | // https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8
330 | jstring jtext = env_->NewStringUTF(text);
331 | jobject jobj =
332 | env_->NewObject(stateJavaClass_, constructor, jtext,
333 | state.selection.start, state.selection.end,
334 | state.composingRegion.start, state.composingRegion.end);
335 | env_->DeleteLocalRef(jtext);
336 | return jobj;
337 | }
338 |
339 | void GameTextInput::stateFromJava(jobject textInputEvent,
340 | GameTextInputGetStateCallback callback,
341 | void *context) const {
342 | jstring text =
343 | (jstring)env_->GetObjectField(textInputEvent, stateClassInfo_.text);
344 | // Note this is 'modified' UTF-8, not true UTF-8. It has no NULLs in it,
345 | // except at the end. It's actually not specified whether the value returned
346 | // by GetStringUTFChars includes a null at the end, but it *seems to* on
347 | // Android.
348 | const char *text_chars = env_->GetStringUTFChars(text, NULL);
349 | int text_len = env_->GetStringUTFLength(
350 | text); // Length in bytes, *not* including the null.
351 | int selectionStart =
352 | env_->GetIntField(textInputEvent, stateClassInfo_.selectionStart);
353 | int selectionEnd =
354 | env_->GetIntField(textInputEvent, stateClassInfo_.selectionEnd);
355 | int composingRegionStart =
356 | env_->GetIntField(textInputEvent, stateClassInfo_.composingRegionStart);
357 | int composingRegionEnd =
358 | env_->GetIntField(textInputEvent, stateClassInfo_.composingRegionEnd);
359 | GameTextInputState state{text_chars,
360 | text_len,
361 | {selectionStart, selectionEnd},
362 | {composingRegionStart, composingRegionEnd}};
363 | callback(context, &state);
364 | env_->ReleaseStringUTFChars(text, text_chars);
365 | env_->DeleteLocalRef(text);
366 | }
367 |
--------------------------------------------------------------------------------
/GameActivity/src/main/java/com/google/androidgamesdk/GameActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.google.androidgamesdk;
17 |
18 | import static android.view.inputmethod.EditorInfo.IME_ACTION_GO;
19 | import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
20 | import static android.view.inputmethod.EditorInfo.IME_MASK_ACTION;
21 | import static android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
22 |
23 | import android.app.Activity;
24 | import android.content.pm.ActivityInfo;
25 | import android.content.pm.PackageManager;
26 | import android.content.res.AssetManager;
27 | import android.content.res.Configuration;
28 | import android.graphics.PixelFormat;
29 | import android.os.Build;
30 | import android.os.Bundle;
31 | import android.text.InputType;
32 | import android.util.Log;
33 | import android.view.KeyEvent;
34 | import android.view.MotionEvent;
35 | import android.view.Surface;
36 | import android.view.SurfaceHolder;
37 | import android.view.SurfaceView;
38 | import android.view.View;
39 | import android.view.WindowManager;
40 | import android.view.inputmethod.EditorInfo;
41 | import android.widget.FrameLayout;
42 |
43 | import dalvik.system.BaseDexClassLoader;
44 | import java.io.File;
45 |
46 | public class GameActivity
47 | extends Activity
48 | implements SurfaceHolder.Callback2 {
49 | private static final String LOG_TAG = "GameActivity";
50 |
51 | /**
52 | * Optional meta-that can be in the manifest for this component, specifying
53 | * the name of the native shared library to load. If not specified,
54 | * "main" is used.
55 | */
56 | public static final String META_DATA_LIB_NAME = "android.app.lib_name";
57 |
58 | /**
59 | * Optional meta-that can be in the manifest for this component, specifying
60 | * the name of the main entry point for this native activity in the
61 | * {@link #META_DATA_LIB_NAME} native code. If not specified,
62 | * "GameActivity_onCreate" is used.
63 | */
64 | public static final String META_DATA_FUNC_NAME = "android.app.func_name";
65 |
66 | private static final String KEY_NATIVE_SAVED_STATE = "android:native_state";
67 |
68 | protected int contentViewId;
69 |
70 | private EditorInfo imeEditorInfo;
71 |
72 | /**
73 | * The SurfaceView used by default for displaying the game and getting a text
74 | * input.
75 | * You can override its creation in `onCreateSurfaceView`.
76 | * This can be null, usually if you override `onCreateSurfaceView` to render on
77 | * the whole activity
78 | * window.
79 | */
80 | protected SurfaceView mSurfaceView;
81 |
82 | protected boolean processMotionEvent(MotionEvent event) {
83 | int action = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) ? event.getActionButton() : 0;
84 | int cls = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ? event.getClassification() : 0;
85 |
86 | return onTouchEventNative(mNativeHandle, event, event.getPointerCount(),
87 | event.getHistorySize(), event.getDeviceId(), event.getSource(), event.getAction(),
88 | event.getEventTime(), event.getDownTime(), event.getFlags(), event.getMetaState(), action,
89 | event.getButtonState(), cls, event.getEdgeFlags(), event.getXPrecision(),
90 | event.getYPrecision());
91 | }
92 |
93 | @Override
94 | public boolean onTouchEvent(MotionEvent event) {
95 | if (processMotionEvent(event)) {
96 | return true;
97 | } else {
98 | return super.onTouchEvent(event);
99 | }
100 | }
101 |
102 | @Override
103 | public boolean onGenericMotionEvent(MotionEvent event) {
104 | if (processMotionEvent(event)) {
105 | return true;
106 | } else {
107 | return super.onGenericMotionEvent(event);
108 | }
109 | }
110 |
111 | @Override
112 | public boolean onKeyUp(final int keyCode, KeyEvent event) {
113 | if (onKeyUpNative(mNativeHandle, event)) {
114 | return true;
115 | } else {
116 | return super.onKeyUp(keyCode, event);
117 | }
118 | }
119 |
120 | @Override
121 | public boolean onKeyDown(final int keyCode, KeyEvent event) {
122 | if (onKeyDownNative(mNativeHandle, event)) {
123 | return true;
124 | } else {
125 | return super.onKeyDown(keyCode, event);
126 | }
127 | }
128 |
129 | private long mNativeHandle;
130 |
131 | private SurfaceHolder mCurSurfaceHolder;
132 |
133 | protected final int[] mLocation = new int[2];
134 |
135 | protected boolean mDestroyed;
136 |
137 | protected native long loadNativeCode(String path, String funcname, String internalDataPath,
138 | String obbPath, String externalDataPath, AssetManager assetMgr, byte[] savedState);
139 |
140 | protected native String getDlError();
141 |
142 | protected native void unloadNativeCode(long handle);
143 |
144 | protected native void onStartNative(long handle);
145 |
146 | protected native void onResumeNative(long handle);
147 |
148 | protected native byte[] onSaveInstanceStateNative(long handle);
149 |
150 | protected native void onPauseNative(long handle);
151 |
152 | protected native void onStopNative(long handle);
153 |
154 | protected native void onConfigurationChangedNative(long handle);
155 |
156 | protected native void onTrimMemoryNative(long handle, int level);
157 |
158 | protected native void onWindowFocusChangedNative(long handle, boolean focused);
159 |
160 | protected native void onSurfaceCreatedNative(long handle, Surface surface);
161 |
162 | protected native void onSurfaceChangedNative(
163 | long handle, Surface surface, int format, int width, int height);
164 |
165 | protected native void onSurfaceRedrawNeededNative(long handle, Surface surface);
166 |
167 | protected native void onSurfaceDestroyedNative(long handle);
168 |
169 | protected native boolean onTouchEventNative(long handle, MotionEvent motionEvent,
170 | int pointerCount, int historySize, int deviceId, int source, int action, long eventTime,
171 | long downTime, int flags, int metaState, int actionButton, int buttonState,
172 | int classification, int edgeFlags, float precisionX, float precisionY);
173 |
174 | protected native boolean onKeyDownNative(long handle, KeyEvent keyEvent);
175 |
176 | protected native boolean onKeyUpNative(long handle, KeyEvent keyEvent);
177 |
178 | protected native void onWindowInsetsChangedNative(long handle);
179 |
180 | /**
181 | * Get the pointer to the C `GameActivity` struct associated to this activity.
182 | *
183 | * @return the pointer to the C `GameActivity` struct associated to this
184 | * activity.
185 | */
186 | public long getGameActivityNativeHandle() {
187 | return this.mNativeHandle;
188 | }
189 |
190 | /**
191 | * Called to create the SurfaceView when the game will be rendered. It should be
192 | * stored in
193 | * the mSurfaceView field, and its ID in contentViewId (if applicable).
194 | *
195 | * You can also redefine this to not create a SurfaceView at all,
196 | * and call `getWindow().takeSurface(this);` instead if you want to render on
197 | * the whole activity
198 | * window.
199 | */
200 | protected void onCreateSurfaceView() {
201 | mSurfaceView = new SurfaceView(this);
202 | FrameLayout frameLayout = new FrameLayout(this);
203 | contentViewId = View.generateViewId();
204 | frameLayout.setId(contentViewId);
205 | frameLayout.addView(mSurfaceView);
206 |
207 | setContentView(frameLayout);
208 | frameLayout.requestFocus();
209 |
210 | mSurfaceView.getHolder().addCallback(
211 | this); // Register as a callback for the rendering of the surface, so that we can pass
212 | // this
213 | // surface to the native code
214 |
215 | }
216 |
217 | /**
218 | * Called to set up the window after the SurfaceView is created. Override this
219 | * if you want to
220 | * change the Format (default is `PixelFormat.RGB_565`) or the Soft Input Mode
221 | * (default is
222 | * `WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED |
223 | * WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE`).
224 | */
225 | protected void onSetUpWindow() {
226 | getWindow().setFormat(PixelFormat.RGB_565);
227 | getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
228 | | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
229 | }
230 |
231 | @Override
232 | protected void onCreate(Bundle savedInstanceState) {
233 | onCreateSurfaceView();
234 | onSetUpWindow();
235 |
236 | String libname = "main";
237 | if (null != getIntent().getStringExtra(META_DATA_LIB_NAME)) {
238 | libname = getIntent().getStringExtra(META_DATA_LIB_NAME);
239 | }
240 | String funcname = "GameActivity_onCreate";
241 | ActivityInfo ai;
242 | try {
243 | ai = getPackageManager().getActivityInfo(
244 | getIntent().getComponent(), PackageManager.GET_META_DATA);
245 | if (ai.metaData != null) {
246 | String ln = ai.metaData.getString(META_DATA_LIB_NAME);
247 | if (ln != null)
248 | libname = ln;
249 | ln = ai.metaData.getString(META_DATA_FUNC_NAME);
250 | if (ln != null)
251 | funcname = ln;
252 | }
253 | } catch (PackageManager.NameNotFoundException e) {
254 | throw new RuntimeException("Error getting activity info", e);
255 | }
256 |
257 | BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader();
258 | String path = classLoader.findLibrary(libname);
259 |
260 | if (path == null) {
261 | throw new IllegalArgumentException("Unable to find native library " + libname
262 | + " using classloader: " + classLoader.toString());
263 | }
264 |
265 | byte[] nativeSavedState = savedInstanceState != null ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE)
266 | : null;
267 |
268 | mNativeHandle = loadNativeCode(path, funcname, getAbsolutePath(getFilesDir()),
269 | getAbsolutePath(getObbDir()), getAbsolutePath(getExternalFilesDir(null)),
270 | getAssets(), nativeSavedState);
271 |
272 | if (mNativeHandle == 0) {
273 | throw new UnsatisfiedLinkError(
274 | "Unable to load native library \"" + path + "\": " + getDlError());
275 | }
276 |
277 | super.onCreate(savedInstanceState);
278 | }
279 |
280 | private static String getAbsolutePath(File file) {
281 | return (file != null) ? file.getAbsolutePath() : null;
282 | }
283 |
284 | @Override
285 | protected void onDestroy() {
286 | mDestroyed = true;
287 | if (mCurSurfaceHolder != null) {
288 | onSurfaceDestroyedNative(mNativeHandle);
289 | mCurSurfaceHolder = null;
290 | }
291 |
292 | unloadNativeCode(mNativeHandle);
293 | super.onDestroy();
294 | }
295 |
296 | @Override
297 | protected void onPause() {
298 | super.onPause();
299 | onPauseNative(mNativeHandle);
300 | }
301 |
302 | @Override
303 | protected void onResume() {
304 | super.onResume();
305 | onResumeNative(mNativeHandle);
306 | }
307 |
308 | @Override
309 | protected void onSaveInstanceState(Bundle outState) {
310 | super.onSaveInstanceState(outState);
311 | byte[] state = onSaveInstanceStateNative(mNativeHandle);
312 | if (state != null) {
313 | outState.putByteArray(KEY_NATIVE_SAVED_STATE, state);
314 | }
315 | }
316 |
317 | @Override
318 | protected void onStart() {
319 | super.onStart();
320 | onStartNative(mNativeHandle);
321 | }
322 |
323 | @Override
324 | protected void onStop() {
325 | super.onStop();
326 | onStopNative(mNativeHandle);
327 | }
328 |
329 | @Override
330 | public void onConfigurationChanged(Configuration newConfig) {
331 | super.onConfigurationChanged(newConfig);
332 | if (!mDestroyed) {
333 | onConfigurationChangedNative(mNativeHandle);
334 | }
335 | }
336 |
337 | @Override
338 | public void onTrimMemory(int level) {
339 | super.onTrimMemory(level);
340 | if (!mDestroyed) {
341 | onTrimMemoryNative(mNativeHandle, level);
342 | }
343 | }
344 |
345 | @Override
346 | public void onWindowFocusChanged(boolean hasFocus) {
347 | super.onWindowFocusChanged(hasFocus);
348 | if (!mDestroyed) {
349 | onWindowFocusChangedNative(mNativeHandle, hasFocus);
350 | }
351 | }
352 |
353 | public void surfaceCreated(SurfaceHolder holder) {
354 | if (!mDestroyed) {
355 | mCurSurfaceHolder = holder;
356 | onSurfaceCreatedNative(mNativeHandle, holder.getSurface());
357 | }
358 | }
359 |
360 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
361 | if (!mDestroyed) {
362 | mCurSurfaceHolder = holder;
363 | onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height);
364 | }
365 | }
366 |
367 | public void surfaceRedrawNeeded(SurfaceHolder holder) {
368 | if (!mDestroyed) {
369 | mCurSurfaceHolder = holder;
370 | onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface());
371 | }
372 | }
373 |
374 | public void surfaceDestroyed(SurfaceHolder holder) {
375 | mCurSurfaceHolder = null;
376 | if (!mDestroyed) {
377 | onSurfaceDestroyedNative(mNativeHandle);
378 | }
379 | }
380 |
381 | void setWindowFlags(int flags, int mask) {
382 | getWindow().setFlags(flags, mask);
383 | }
384 |
385 | void setWindowFormat(int format) {
386 | getWindow().setFormat(format);
387 | }
388 |
389 | /**
390 | * Get the EditorInfo structure used to initialize the IME when it is requested.
391 | * The default is to forward key requests to the app (InputType.TYPE_NULL) and
392 | * to
393 | * have no action button (IME_ACTION_NONE).
394 | * See
395 | * https://developer.android.com/reference/android/view/inputmethod/EditorInfo.
396 | */
397 | public EditorInfo getImeEditorInfo() {
398 | if (imeEditorInfo == null) {
399 | imeEditorInfo = new EditorInfo();
400 | imeEditorInfo.inputType = InputType.TYPE_NULL;
401 | imeEditorInfo.actionId = IME_ACTION_NONE;
402 | imeEditorInfo.imeOptions = IME_FLAG_NO_ENTER_ACTION;
403 | }
404 | return imeEditorInfo;
405 | }
406 |
407 | /**
408 | * Set the EditorInfo structure used to initialize the IME when it is requested.
409 | * Set the inputType and actionId in order to customize how the IME behaves.
410 | * See
411 | * https://developer.android.com/reference/android/view/inputmethod/EditorInfo.
412 | */
413 | public void setImeEditorInfo(EditorInfo info) {
414 | imeEditorInfo = info;
415 | }
416 |
417 | /**
418 | * Set the inpuType and actionId fields of the EditorInfo structure used to
419 | * initialize the IME when it is requested.
420 | * This is called from the native side by GameActivity_setImeEditorInfo.
421 | * See
422 | * https://developer.android.com/reference/android/view/inputmethod/EditorInfo.
423 | */
424 | public void setImeEditorInfoFields(int inputType, int actionId, int imeOptions) {
425 | EditorInfo info = getImeEditorInfo();
426 | info.inputType = inputType;
427 | info.actionId = actionId;
428 | info.imeOptions = imeOptions;
429 | }
430 | }
431 |
--------------------------------------------------------------------------------