├── .gitignore
├── LICENSE
├── README.md
├── app
├── CMakeLists.txt
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ ├── GFKSimpleGame.cpp
│ ├── GFKSimpleGame.h
│ ├── NearbyConnection.cpp
│ ├── NearbyNativeActivity.cpp
│ ├── NearbyNativeActivity.h
│ ├── NearbyNativeActivity_Engine.cpp
│ ├── jui_helper
│ │ ├── CMakeLists.txt
│ │ ├── JavaUI.cpp
│ │ ├── JavaUI.h
│ │ ├── JavaUI_Dialog.cpp
│ │ ├── JavaUI_View.h
│ │ └── JavaUI_Window.cpp
│ ├── native-lib.cpp
│ └── ndk_helper
│ │ ├── CMakeLists.txt
│ │ ├── GLContext.cpp
│ │ ├── GLContext.h
│ │ ├── JNIHelper.cpp
│ │ ├── JNIHelper.h
│ │ ├── NDKHelper.h
│ │ ├── gl3stub.cpp
│ │ └── gl3stub.h
│ ├── java
│ └── com
│ │ ├── google
│ │ └── example
│ │ │ └── games
│ │ │ └── nearbyconnections
│ │ │ └── NearbyNativeActivity.java
│ │ └── sample
│ │ └── helper
│ │ ├── JUIButton.java
│ │ ├── JUIDialog.java
│ │ ├── JUIForwardingPopupWindow.java
│ │ ├── JUIHelper.java
│ │ ├── JUITextView.java
│ │ └── NDKHelper.java
│ └── res
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gpg-sdk
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | bin/
3 | build.xml
4 | gen/
5 | libs/
6 | local.properties
7 | obj/
8 | .settings/
9 | .DS_Store
10 |
11 | .gradle/
12 | build/
13 | .externalNativeBuild/
14 |
15 | *.iml
16 |
17 | gpg-sdk/gpg-cpp-sdk/
18 | gpg-sdk/gpg_cpp_sdk.zip
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction, and
10 | distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright
13 | owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all other entities
16 | that control, are controlled by, or are under common control with that entity.
17 | For the purposes of this definition, "control" means (i) the power, direct or
18 | indirect, to cause the direction or management of such entity, whether by
19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 | outstanding shares, or (iii) beneficial ownership of such entity.
21 |
22 | "You" (or "Your") shall mean an individual or Legal Entity exercising
23 | permissions granted by this License.
24 |
25 | "Source" form shall mean the preferred form for making modifications, including
26 | but not limited to software source code, documentation source, and configuration
27 | files.
28 |
29 | "Object" form shall mean any form resulting from mechanical transformation or
30 | translation of a Source form, including but not limited to compiled object code,
31 | generated documentation, and conversions to other media types.
32 |
33 | "Work" shall mean the work of authorship, whether in Source or Object form, made
34 | available under the License, as indicated by a copyright notice that is included
35 | in or attached to the work (an example is provided in the Appendix below).
36 |
37 | "Derivative Works" shall mean any work, whether in Source or Object form, that
38 | is based on (or derived from) the Work and for which the editorial revisions,
39 | annotations, elaborations, or other modifications represent, as a whole, an
40 | original work of authorship. For the purposes of this License, Derivative Works
41 | shall not include works that remain separable from, or merely link (or bind by
42 | name) to the interfaces of, the Work and Derivative Works thereof.
43 |
44 | "Contribution" shall mean any work of authorship, including the original version
45 | of the Work and any modifications or additions to that Work or Derivative Works
46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
47 | by the copyright owner or by an individual or Legal Entity authorized to submit
48 | on behalf of the copyright owner. For the purposes of this definition,
49 | "submitted" means any form of electronic, verbal, or written communication sent
50 | to the Licensor or its representatives, including but not limited to
51 | communication on electronic mailing lists, source code control systems, and
52 | issue tracking systems that are managed by, or on behalf of, the Licensor for
53 | the purpose of discussing and improving the Work, but excluding communication
54 | that is conspicuously marked or otherwise designated in writing by the copyright
55 | owner as "Not a Contribution."
56 |
57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58 | of whom a Contribution has been received by Licensor and subsequently
59 | incorporated within the Work.
60 |
61 | 2. Grant of Copyright License.
62 |
63 | Subject to the terms and conditions of this License, each Contributor hereby
64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65 | irrevocable copyright license to reproduce, prepare Derivative Works of,
66 | publicly display, publicly perform, sublicense, and distribute the Work and such
67 | Derivative Works in Source or Object form.
68 |
69 | 3. Grant of Patent License.
70 |
71 | Subject to the terms and conditions of this License, each Contributor hereby
72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73 | irrevocable (except as stated in this section) patent license to make, have
74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75 | such license applies only to those patent claims licensable by such Contributor
76 | that are necessarily infringed by their Contribution(s) alone or by combination
77 | of their Contribution(s) with the Work to which such Contribution(s) was
78 | submitted. If You institute patent litigation against any entity (including a
79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80 | Contribution incorporated within the Work constitutes direct or contributory
81 | patent infringement, then any patent licenses granted to You under this License
82 | for that Work shall terminate as of the date such litigation is filed.
83 |
84 | 4. Redistribution.
85 |
86 | You may reproduce and distribute copies of the Work or Derivative Works thereof
87 | in any medium, with or without modifications, and in Source or Object form,
88 | provided that You meet the following conditions:
89 |
90 | You must give any other recipients of the Work or Derivative Works a copy of
91 | this License; and
92 | You must cause any modified files to carry prominent notices stating that You
93 | changed the files; and
94 | You must retain, in the Source form of any Derivative Works that You distribute,
95 | all copyright, patent, trademark, and attribution notices from the Source form
96 | of the Work, excluding those notices that do not pertain to any part of the
97 | Derivative Works; and
98 | If the Work includes a "NOTICE" text file as part of its distribution, then any
99 | Derivative Works that You distribute must include a readable copy of the
100 | attribution notices contained within such NOTICE file, excluding those notices
101 | that do not pertain to any part of the Derivative Works, in at least one of the
102 | following places: within a NOTICE text file distributed as part of the
103 | Derivative Works; within the Source form or documentation, if provided along
104 | with the Derivative Works; or, within a display generated by the Derivative
105 | Works, if and wherever such third-party notices normally appear. The contents of
106 | the NOTICE file are for informational purposes only and do not modify the
107 | License. You may add Your own attribution notices within Derivative Works that
108 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
109 | provided that such additional attribution notices cannot be construed as
110 | modifying the License.
111 | You may add Your own copyright statement to Your modifications and may provide
112 | additional or different license terms and conditions for use, reproduction, or
113 | distribution of Your modifications, or for any such Derivative Works as a whole,
114 | provided Your use, reproduction, and distribution of the Work otherwise complies
115 | with the conditions stated in this License.
116 |
117 | 5. Submission of Contributions.
118 |
119 | Unless You explicitly state otherwise, any Contribution intentionally submitted
120 | for inclusion in the Work by You to the Licensor shall be under the terms and
121 | conditions of this License, without any additional terms or conditions.
122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
123 | any separate license agreement you may have executed with Licensor regarding
124 | such Contributions.
125 |
126 | 6. Trademarks.
127 |
128 | This License does not grant permission to use the trade names, trademarks,
129 | service marks, or product names of the Licensor, except as required for
130 | reasonable and customary use in describing the origin of the Work and
131 | reproducing the content of the NOTICE file.
132 |
133 | 7. Disclaimer of Warranty.
134 |
135 | Unless required by applicable law or agreed to in writing, Licensor provides the
136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138 | including, without limitation, any warranties or conditions of TITLE,
139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140 | solely responsible for determining the appropriateness of using or
141 | redistributing the Work and assume any risks associated with Your exercise of
142 | permissions under this License.
143 |
144 | 8. Limitation of Liability.
145 |
146 | In no event and under no legal theory, whether in tort (including negligence),
147 | contract, or otherwise, unless required by applicable law (such as deliberate
148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
149 | liable to You for damages, including any direct, indirect, special, incidental,
150 | or consequential damages of any character arising as a result of this License or
151 | out of the use or inability to use the Work (including but not limited to
152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153 | any and all other commercial damages or losses), even if such Contributor has
154 | been advised of the possibility of such damages.
155 |
156 | 9. Accepting Warranty or Additional Liability.
157 |
158 | While redistributing the Work or Derivative Works thereof, You may choose to
159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160 | other liability obligations and/or rights consistent with this License. However,
161 | in accepting such obligations, You may act only on Your own behalf and on Your
162 | sole responsibility, not on behalf of any other Contributor, and only if You
163 | agree to indemnify, defend, and hold each Contributor harmless for any liability
164 | incurred by, or claims asserted against, such Contributor by reason of your
165 | accepting any such warranty or additional liability.
166 |
167 | END OF TERMS AND CONDITIONS
168 |
169 | APPENDIX: How to apply the Apache License to your work
170 |
171 | To apply the Apache License to your work, attach the following boilerplate
172 | notice, with the fields enclosed by brackets "[]" replaced with your own
173 | identifying information. (Don't include the brackets!) The text should be
174 | enclosed in the appropriate comment syntax for the file format. We also
175 | recommend that a file or class name and description of purpose be included on
176 | the same "printed page" as the copyright notice for easier identification within
177 | third-party archives.
178 |
179 | Copyright [yyyy] [name of copyright owner]
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | http://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Google Nearby Connections C++ Samples
2 | =====================================
3 |
4 | This repo has been migrated to [github.com/android/connectivity][1]. Please check that repo for future updates. Thank you!
5 |
6 | [1]: https://github.com/android/connectivity
7 |
--------------------------------------------------------------------------------
/app/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2018 Google LLC
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 | # http://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 |
16 | # For more information about using CMake with Android Studio, read the
17 | # documentation: https://d.android.com/studio/projects/add-native-code.html
18 |
19 | # Sets the minimum version of CMake required to build the native library.
20 |
21 | cmake_minimum_required(VERSION 3.4.1)
22 |
23 | #include the native part of JUI Helper
24 | add_subdirectory (src/main/cpp/jui_helper)
25 |
26 | #include the native part of NDKHelper
27 | add_subdirectory (src/main/cpp/ndk_helper)
28 |
29 | #include the GPG C++ SDK
30 | add_library(gpg_sdk STATIC IMPORTED)
31 | set_target_properties(gpg_sdk PROPERTIES IMPORTED_LOCATION
32 | ${GPG_SDK_PATH}/lib/c++/${ANDROID_ABI}/libgpg.a)
33 |
34 | # build native_app_glue as a static lib
35 | add_library(native_app_glue STATIC
36 | ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
37 |
38 | # build cpufeatures as a static lib
39 | add_library(cpufeatures STATIC
40 | ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
41 |
42 | # Export ANativeActivity_onCreate(),
43 | # Refer to: https://github.com/android-ndk/ndk/issues/381.
44 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
45 |
46 | add_library(
47 | NearbyNativeActivity
48 | SHARED
49 | src/main/cpp/GFKSimpleGame.cpp
50 | src/main/cpp/NearbyConnection.cpp
51 | src/main/cpp/NearbyNativeActivity.cpp
52 | src/main/cpp/NearbyNativeActivity_Engine.cpp
53 | )
54 |
55 | target_include_directories(NearbyNativeActivity PRIVATE
56 | ${ANDROID_NDK}/sources/android/native_app_glue
57 | ${ANDROID_NDK}/sources/android/cpufeatures
58 | ${GPG_SDK_PATH}/include
59 | src/main/cpp/jui_helper
60 | src/main/cpp/ndk_helper
61 | )
62 |
63 | target_link_libraries(NearbyNativeActivity
64 | gpg_sdk
65 | native_app_glue
66 | cpufeatures
67 | juihelper
68 | ndkhelper
69 | log
70 | android
71 | EGL
72 | GLESv2
73 | z
74 | )
75 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 (C) Google LLC
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 | * http://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 |
16 | apply plugin : 'com.android.application'
17 |
18 | android {
19 | compileSdkVersion 26
20 | defaultConfig {
21 | applicationId "com.google.example.cpp.nearby"
22 | minSdkVersion 14
23 | targetSdkVersion 26
24 |
25 | ndk.abiFilters 'x86', 'armeabi-v7a', 'arm64-v8a'
26 |
27 | externalNativeBuild {
28 | cmake {
29 | cppFlags "-std=c++11 -Wall -frtti -Werror"
30 | arguments "-DANDROID_STL=c++_static",
31 | "-DGPG_SDK_PATH=${project(':gpg-sdk').projectDir}/gpg-cpp-sdk/android",
32 | "-DANDROID_TOOLCHAIN=clang"
33 | }
34 | }
35 | }
36 | externalNativeBuild {
37 | cmake.path "CMakeLists.txt"
38 | }
39 | }
40 |
41 | dependencies {
42 | compile 'com.android.support:appcompat-v7:26.1.0'
43 | compile 'com.google.android.gms:play-services-games:11.8.0'
44 | compile 'com.google.android.gms:play-services-nearby:11.8.0'
45 | }
46 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/wilkinsonclay/android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
37 |
40 |
43 |
48 |
49 |
50 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/cpp/GFKSimpleGame.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
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 | * GFKSimpleGame.cpp
18 | * A Simple Game to demo Google Nearby Connections Native Interface
19 | */
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include "GFKSimpleGame.h"
28 | #include "JNIHelper.h"
29 |
30 | namespace game_helper {
31 | /*
32 | * supported grade level: currently only supports first grade math level
33 | */
34 | const int32_t LEVEL_CAP = 2;
35 | const char OPS_CODE[] = {'+', '-', // 1st and 2nd grader
36 | '*', '/', // 3rd and above
37 | '^', 's'}; // 5th
38 |
39 | GFKSimple::GFKSimple() : choice_count_(0), level_(0) {
40 | Init();
41 | }
42 |
43 | GFKSimple::GFKSimple(int32_t choiceCount, int32_t gradeLevel)
44 | : choice_count_(choiceCount), level_(gradeLevel - 1) {
45 | Init();
46 | }
47 |
48 | const char *GFKSimple::GetQuestion(void) {
49 | int32_t op1 = GetOperand();
50 | int32_t op2 = GetOperand();
51 | char op = GetOperator();
52 |
53 | snprintf(question_, MAX_QUESTION_LEN, "%d %c %d = ?", op1, op, op2);
54 | int32_t ans;
55 | switch (op) {
56 | case '+':
57 | ans = op1 + op2;
58 | break;
59 | case '-':
60 | ans = op1 - op2;
61 | break;
62 | case '*':
63 | ans = op1 * op2;
64 | break;
65 | case '/':
66 | ans = op1 / op2;
67 | break;
68 | case '^':
69 | op2 %= 4;
70 | snprintf(question_, MAX_QUESTION_LEN, "%d %c %d = ?", op1, op, op2);
71 | ans = pow(op1, op2);
72 | break;
73 | case 's':
74 | ans = sqrt(op1);
75 | snprintf(question_, MAX_QUESTION_LEN, "square_root(%d) = ?", op1);
76 | break;
77 | default:
78 | // Not Recognized, default to '+'
79 | ans = op1 + op2;
80 | LOGE("Wrong operation generated: %c, taking default", op);
81 | break;
82 | }
83 | choices_[choice_count_] = ans;
84 |
85 | int32_t ans_idx = rand() % choice_count_;
86 | choices_[ans_idx] = ans;
87 | for (int32_t i = 0; i < ans_idx; i++) choices_[i] = ans + i - ans_idx;
88 | for (int32_t i = ans_idx + 1; i < choice_count_; i++)
89 | choices_[i] = ans + i - ans_idx;
90 |
91 | return (const char *)&question_[0];
92 | }
93 | const int32_t *GFKSimple::GetAllChoices(void) { return (const int32_t *)&choices_[0]; }
94 |
95 | void GetSupportedGrade(int32_t *minGrade, int32_t *maxGrade) {
96 | if (!minGrade || !maxGrade) {
97 | LOGE("NULL pointer to GetSupportedGrade()");
98 | return;
99 | }
100 | if (LEVEL_CAP <= 0) {
101 | *minGrade = *maxGrade = 0;
102 | }
103 | *maxGrade = LEVEL_CAP;
104 | *minGrade = 1; // Grades start with 1
105 | }
106 |
107 | void GFKSimple::Init(void) {
108 | srand((unsigned)time(NULL));
109 | switch (level_) {
110 | case 0:
111 | default:
112 | max_operand_ = 10;
113 | operation_count_ = 2;
114 | break;
115 | case 1:
116 | max_operand_ = 100;
117 | operation_count_ = 2;
118 | break;
119 | case 3:
120 | max_operand_ = 100;
121 | operation_count_ = 4;
122 | break;
123 | case 4:
124 | case 5:
125 | max_operand_ = 100;
126 | operation_count_ = 6;
127 | break;
128 | }
129 | choices_ = nullptr;
130 | if (choice_count_) {
131 | choices_ = std::unique_ptr(new int32_t[choice_count_ + 1]);
132 | if (choices_) {
133 | memset(choices_.get(), 0, sizeof(int32_t) * (choice_count_ + 1));
134 | } else {
135 | LOGE("Out of the memory in %s at %d", __FILE__, __LINE__);
136 | }
137 | }
138 | if (operation_count_ > (sizeof(OPS_CODE) / sizeof(OPS_CODE[0]))) {
139 | LOGW("Operation (%d) is bigger than simple game capability(%d)",
140 | operation_count_, static_cast(sizeof(OPS_CODE)/sizeof(OPS_CODE[0])));
141 | operation_count_ = (sizeof(OPS_CODE) / sizeof(OPS_CODE[0]));
142 | }
143 | }
144 |
145 | int32_t GFKSimple::GetOperand(void) {
146 | return rand() % max_operand_;
147 | }
148 |
149 | char GFKSimple::GetOperator(void) {
150 | return OPS_CODE[rand() % operation_count_];
151 | }
152 |
153 | bool GFKSimple::SetChoicesPerQuestion(int32_t choiceCount) {
154 | if (choiceCount == choice_count_) {
155 | return true;
156 | }
157 |
158 | std::unique_ptr newChoices = std::unique_ptr(new int32_t[choiceCount + 1]);
159 | if (!newChoices) {
160 | LOGE("Out of memory from GFKSimple::GerOperand");
161 | return false;
162 | }
163 | choices_ = std::move(newChoices);
164 | choice_count_ = choiceCount;
165 | return true;
166 | }
167 |
168 | int32_t GFKSimple::GetChoicesPerQuestion(void) {
169 | return choice_count_;
170 | }
171 |
172 | /*
173 | * GetCorrectChoice()
174 | * The correct answer index is the extra one returned in the answers --
175 | * 0 -- choice_count_ - 1 : game choices to be displayed on UI
176 | * choice_count_ : The correct_answer
177 | */
178 | int32_t GFKSimple::GetCorrectChoice(void) {
179 | if (nullptr != choices_) {
180 | return choice_count_;
181 | }
182 | return 0;
183 | }
184 |
185 | GFKSimple::~GFKSimple() {}
186 | } // namespace game_helper
187 |
--------------------------------------------------------------------------------
/app/src/main/cpp/GFKSimpleGame.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
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 | * GFKSimple.h
18 | * Simple Game Object to show case the nearby connection native interface
19 | */
20 | #ifndef _USR_LOCAL_GOOGLE_ANDROID_NEARBYCONNECTIONS_JNI_GFKSIMPLEGAME_H_
21 | #define _USR_LOCAL_GOOGLE_ANDROID_NEARBYCONNECTIONS_JNI_GFKSIMPLEGAME_H_
22 |
23 |
24 | namespace game_helper {
25 | const int MAX_QUESTION_LEN = 32;
26 |
27 | class GFKSimple {
28 | public:
29 | GFKSimple();
30 | GFKSimple(int choiceCount, int gradeLevel);
31 | ~GFKSimple();
32 |
33 | bool SetChoicesPerQuestion(int choiceCount);
34 | int32_t GetChoicesPerQuestion(void);
35 | const char *GetQuestion(void);
36 | const int *GetAllChoices(void);
37 | int32_t GetCorrectChoice(void);
38 | bool GetSupportedGrade(int *minGrade, int *maxGrade);
39 |
40 | private:
41 | std::unique_ptr choices_;
42 | int32_t choice_count_;
43 | char question_[MAX_QUESTION_LEN];
44 | int32_t level_;
45 | int32_t max_operand_;
46 | int32_t operation_count_;
47 | void Init(void);
48 | int32_t GetOperand(void);
49 | char GetOperator(void);
50 | };
51 | } // namespace game_helper
52 | #endif // _USR_LOCAL_GOOGLE_ANDROID_NEARBYCONNECTIONS_JNI_GFKSIMPLEGAME_H_
53 |
--------------------------------------------------------------------------------
/app/src/main/cpp/NearbyNativeActivity.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
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 | * This file demonstrates,
18 | * - How to use Nearby Connection features in C++ code with native client,
19 | * including
20 | * - Create nearby connection interface
21 | * - Set up connections with other devices
22 | * - Handle message sending and receiving
23 | * - Layout simple UI
24 | */
25 |
26 | /*
27 | * Include files
28 | */
29 | #include "NearbyNativeActivity.h"
30 |
31 | const int SCORE_STRING_LEN = 32;
32 |
33 | /*
34 | * PlayGame(): Start up game a new game. put it on UI thread since it is UI
35 | * activity
36 | */
37 | void Engine::PlayGame() {
38 | ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this]() {
39 | LOGI("Playing match");
40 | if (dialog_) delete dialog_;
41 |
42 | // Start game
43 | InitializeGame();
44 |
45 | //
46 | // Using jui_helper, a support library, to create and bind gameplay buttons.
47 | //
48 | dialog_ = new jui_helper::JUIDialog(app_->activity);
49 |
50 | // Setting up labels
51 | time_text_ = new jui_helper::JUITextView("Time Left: 0:00");
52 | time_text_->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_TOP,
53 | jui_helper::LAYOUT_PARAMETER_TRUE);
54 | time_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT,
55 | jui_helper::LAYOUT_PARAMETER_TRUE);
56 | time_text_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, 18.f);
57 | time_text_->SetAttribute("Padding", 10, 10, 10, 10);
58 |
59 | // Adding formula Text
60 | math_formula_ = new jui_helper::JUITextView("10 + 5 = ?");
61 | math_formula_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, time_text_);
62 | math_formula_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT,
63 | jui_helper::LAYOUT_PARAMETER_TRUE);
64 | math_formula_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP,
65 | 24.f);
66 | math_formula_->SetAttribute("Padding", 10, 10, 10, 10);
67 |
68 | // Adding Multiple Choice Buttons
69 | jui_helper::JUIButton *button = CreateChoiceButton("A", NULL);
70 | if (button) {
71 | button->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_LEFT,
72 | jui_helper::LAYOUT_PARAMETER_TRUE);
73 | button->SetMargins(40, 0, 0, 0); // todo: make it adaptive
74 | }
75 | game_buttons_[0] = button;
76 | for (int i = 1; i < CHOICES_PER_QUESTION; i++) {
77 | std::string cap(1, 'A' + i);
78 | button = CreateChoiceButton(cap.c_str(), game_buttons_[i - 1]);
79 | game_buttons_[i] = button;
80 | }
81 |
82 | const int32_t labelWidth = 600;
83 | const int32_t labelHeight = 300;
84 | scores_text_ = new jui_helper::JUITextView("0:00");
85 | scores_text_->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW,
86 | game_buttons_[CHOICES_PER_QUESTION - 1]);
87 | scores_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT,
88 | jui_helper::LAYOUT_PARAMETER_TRUE);
89 | scores_text_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, 18.f);
90 | scores_text_->SetAttribute("MinimumWidth", labelWidth);
91 | scores_text_->SetAttribute("MinimumHeight", labelHeight);
92 | scores_text_->SetAttribute("Padding", 10, 10, 10, 10);
93 |
94 | UpdateScoreBoardUI(false);
95 |
96 | dialog_->AddView(math_formula_);
97 |
98 | std::for_each(
99 | game_buttons_, game_buttons_ + CHOICES_PER_QUESTION,
100 | [this](jui_helper::JUIButton *button) { dialog_->AddView(button); });
101 | PlayOneRound();
102 |
103 | dialog_->AddView(time_text_);
104 | dialog_->AddView(scores_text_);
105 |
106 | dialog_->SetAttribute("Title", "Select an answer");
107 | dialog_->SetCallback(
108 | jui_helper::JUICALLBACK_DIALOG_DISMISSED,
109 | [this](jui_helper::JUIDialog *dialog, const int32_t message) {
110 | LOGI("Dialog dismissed");
111 | LeaveGame();
112 | dialog_ = nullptr;
113 | });
114 |
115 | dialog_->Show();
116 |
117 | //
118 | // Invoke time counter periodically
119 | //
120 | std::thread([this]() {
121 | ndk_helper::JNIHelper &helper = *ndk_helper::JNIHelper::GetInstance();
122 | helper.AttachCurrentThread();
123 | while (playing_ && game_time_ <= GAME_DURATION) {
124 | // Update game UI, UI update needs to be performed in UI thread
125 | ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this]() {
126 | char str[SCORE_STRING_LEN];
127 | std::lock_guard lock(mutex_);
128 | snprintf(str, SCORE_STRING_LEN, "Time Left: %d",
129 | GAME_DURATION - this->game_time_);
130 | time_text_->SetAttribute("Text", const_cast(str));
131 | });
132 |
133 | // maintain our private clock
134 | std::this_thread::sleep_for(std::chrono::milliseconds(1000));
135 | game_time_ += 1;
136 | }
137 |
138 | // finish game
139 | playing_ = false;
140 | ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this]() {
141 | std::lock_guard lock(mutex_);
142 | for (int i = 0; i < CHOICES_PER_QUESTION; i++) {
143 | game_buttons_[i]->SetAttribute("Enabled", false);
144 | }
145 | math_formula_->SetAttribute("Enabled", false);
146 | });
147 |
148 | BroadcastScore(score_counter_, true);
149 |
150 | UpdateScoreBoardUI(true);
151 | helper.DetachCurrentThread();
152 | }).detach();
153 | });
154 | }
155 |
156 | /*
157 | * UpdateScoreBoardUI()
158 | * Update game score UI when some player's score changed [ mine or other
159 | * players
160 | * from remote ends]. Scores are already saved in our own cache, just pull out
161 | * to display
162 | */
163 | void Engine::UpdateScoreBoardUI(bool UIThreadRequired) {
164 | // Lock mutex since this one can be called from multiple thread,
165 | // gpg callback tread and UI callback thread
166 | std::lock_guard lock(mutex_);
167 | if (dialog_ == nullptr) return;
168 |
169 | const int32_t SCORE_SIZE = 64;
170 | char str[SCORE_SIZE];
171 | snprintf(str, SCORE_SIZE, "%03d", score_counter_);
172 | std::string str_myscore(str);
173 |
174 | snprintf(str, SCORE_SIZE, "My score: %03d %s\n", score_counter_,
175 | playing_ ? "" : "*");
176 | std::string allstr(str);
177 |
178 | RetrieveScores(allstr);
179 | if (!UIThreadRequired) {
180 | scores_text_->SetAttribute("Text",
181 | const_cast(allstr.c_str()));
182 | return;
183 | }
184 |
185 | ndk_helper::JNIHelper::GetInstance()->RunOnUiThread(
186 | [this, str_myscore, allstr]() {
187 | scores_text_->SetAttribute("Text",
188 | const_cast(allstr.c_str()));
189 | });
190 | }
191 |
192 | /*
193 | * Initialize game state
194 | */
195 | void Engine::InitializeGame() {
196 | std::lock_guard lock(mutex_);
197 | playing_ = true;
198 | game_time_ = 0;
199 | score_counter_ = 0;
200 | }
201 |
202 | /*
203 | * Leave game
204 | */
205 | void Engine::LeaveGame() {
206 | std::lock_guard lock(mutex_);
207 | LOGI("Game is over");
208 | playing_ = false;
209 | }
210 |
211 | /*
212 | * Initialize game management UI,
213 | * invoking jui_helper functions to create java UIs
214 | */
215 | void Engine::InitUI() {
216 |
217 | const int32_t LEFT_MARGIN = 20;
218 |
219 | // The window initialization
220 | jui_helper::JUIWindow::Init(app_->activity, JUIHELPER_CLASS_NAME);
221 |
222 | // Using jui_helper, a support library, to create and bind game management UIs
223 | int32_t win_width = ANativeWindow_getWidth(app_->window);
224 | int32_t win_height = ANativeWindow_getHeight(app_->window);
225 |
226 | if (win_height <= 0 || win_width <= 0) {
227 | LOGE("Failed to get native window size");
228 | return;
229 | }
230 | if (win_height > win_width) {
231 | int32_t tmp = win_width;
232 | win_width = win_height;
233 | win_height = tmp;
234 | }
235 |
236 | int32_t button_raw_width = win_width / 4; // we have 4 buttons
237 | int32_t button_height = win_height / 4;
238 | int cur_idx = 0;
239 |
240 | // Create 4 buttons to control nearby sign-in
241 | // The sequence is dictated by enum BUTTON_INDEX,
242 | // it MUST match the button titles array defined here
243 | const char *titles[UI_BUTTON_COUNT] = {"Advertise", "Discover", "Play Game",
244 | "Stop"};
245 | std::function button_handlers[] = {
246 | [this](jui_helper::JUIView *button, const int32_t msg) {
247 | if (msg == jui_helper::JUICALLBACK_BUTTON_UP) {
248 | OnAdvertiseButtonClick();
249 | }
250 | },
251 | [this](jui_helper::JUIView *button, const int32_t msg) {
252 | if (msg == jui_helper::JUICALLBACK_BUTTON_UP) {
253 | OnDiscoverButtonClick();
254 | }
255 | },
256 | [this](jui_helper::JUIView *button, const int32_t msg) {
257 | if (msg == jui_helper::JUICALLBACK_BUTTON_UP) {
258 | OnPlayButtonClick();
259 | }
260 | },
261 | [this](jui_helper::JUIView *button, const int32_t msg) {
262 | if (msg == jui_helper::JUICALLBACK_BUTTON_UP) {
263 | OnStopButtonClick();
264 | }
265 | },
266 | };
267 |
268 | for (cur_idx = 0; cur_idx < UI_BUTTON_COUNT; cur_idx++) {
269 | jui_helper::JUIButton *button = new jui_helper::JUIButton(titles[cur_idx]);
270 | button->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_VERTICAL,
271 | jui_helper::LAYOUT_PARAMETER_TRUE);
272 | button->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_LEFT,
273 | jui_helper::LAYOUT_PARAMETER_TRUE);
274 | button->SetAttribute("MinimumWidth", button_raw_width - LEFT_MARGIN);
275 | button->SetAttribute("MinimumHeight", button_height);
276 | button->SetMargins(LEFT_MARGIN + cur_idx * button_raw_width, 0, 0, 0);
277 | button->SetCallback(button_handlers[cur_idx]);
278 | jui_helper::JUIWindow::GetInstance()->AddView(button);
279 | ui_buttons_[cur_idx] = button;
280 | }
281 |
282 | status_text_ = new jui_helper::JUITextView("Nearby Connection is Idle");
283 | status_text_->AddRule(jui_helper::LAYOUT_PARAMETER_ALIGN_PARENT_BOTTOM,
284 | jui_helper::LAYOUT_PARAMETER_TRUE);
285 | status_text_->AddRule(jui_helper::LAYOUT_PARAMETER_CENTER_IN_PARENT,
286 | jui_helper::LAYOUT_PARAMETER_TRUE);
287 | status_text_->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, 17.f);
288 | jui_helper::JUIWindow::GetInstance()->AddView(status_text_);
289 |
290 | // Init nearby connections...
291 | std::thread([this]() {
292 | ndk_helper::JNIHelper &helper = *ndk_helper::JNIHelper::GetInstance();
293 | helper.AttachCurrentThread();
294 |
295 | InitGoogleNearbyConnection();
296 |
297 | helper.DetachCurrentThread();
298 | }).detach();
299 |
300 | EnableUI(true);
301 | return;
302 | }
303 |
304 | /*
305 | * Enable/Disable management UI
306 | */
307 | void Engine::EnableUI(bool enable) {
308 | LOGI("Updating UI:%d", enable);
309 | ndk_helper::JNIHelper::GetInstance()->RunOnUiThread([this, enable]() {
310 | ui_buttons_[BUTTON_ADVERTISE]->SetAttribute(
311 | "Enabled",
312 | enable && !(nbc_state_ & nearby_connection_state::ADVERTISING));
313 | ui_buttons_[BUTTON_DISCOVER]->SetAttribute(
314 | "Enabled",
315 | enable && !(nbc_state_ & nearby_connection_state::DISCOVERING));
316 | ui_buttons_[BUTTON_PLAY_GAME]->SetAttribute(
317 | "Enabled", enable && (nbc_state_ & nearby_connection_state::CONNECTED));
318 | /*
319 | * For experimental purpose, Stop button is always enabled
320 | */
321 | ui_buttons_[BUTTON_STOP]->SetAttribute("Enabled", true);
322 |
323 | std::string str;
324 | str += "Nearby Connection Status: Connected Clients = ";
325 | str += std::to_string(CountConnections());
326 | str += "; ";
327 | if (nbc_state_ & nearby_connection_state::IDLE) {
328 | str += "Currently Idle; ";
329 | }
330 | if (nbc_state_ & nearby_connection_state::ADVERTISING) {
331 | str += "In Advertising; ";
332 | }
333 | if (nbc_state_ & nearby_connection_state::DISCOVERING) {
334 | str += "In Discovering; ";
335 | }
336 | if (nbc_state_ & nearby_connection_state::FAILED) {
337 | str += "FAILED; ";
338 | }
339 | str = str.substr(0, str.size() - 2);
340 | status_text_->SetAttribute("Text", const_cast(str.c_str()));
341 | });
342 | }
343 |
344 | /*
345 | * Help function to create(multiple choice buttons)
346 | */
347 | jui_helper::JUIButton *Engine::CreateChoiceButton(const char *cap,
348 | jui_helper::JUIButton *left,
349 | float fontSize) {
350 | jui_helper::JUIButton *button = new jui_helper::JUIButton(cap);
351 | if (!button) {
352 | LOGE("Out of Memory in %s @ line %d", __FILE__, __LINE__);
353 | return NULL;
354 | }
355 | button->SetCallback([this](jui_helper::JUIView *view, const int32_t msg) {
356 | switch (msg) {
357 | case jui_helper::JUICALLBACK_BUTTON_UP:
358 | if (!playing_) return;
359 | CheckChoice(static_cast(view));
360 | PlayOneRound();
361 | }
362 | });
363 | if (left) button->AddRule(jui_helper::LAYOUT_PARAMETER_RIGHT_OF, left);
364 | button->AddRule(jui_helper::LAYOUT_PARAMETER_BELOW, math_formula_);
365 | button->SetAttribute("TextSize", jui_helper::ATTRIBUTE_UNIT_SP, fontSize);
366 | button->SetAttribute("Padding", 2, 5, 2, 5);
367 | button->SetMargins(0, 0, 0, 0);
368 | return button;
369 | }
370 |
371 | /*
372 | * Play one round of game: generate a questions and config them to UI
373 | */
374 | bool Engine::PlayOneRound(void) {
375 | const int32_t CHOICE_LEN = 16;
376 | math_formula_->SetAttribute("Text", game_->GetQuestion());
377 | const int *allChoices = game_->GetAllChoices();
378 | for (int i = 0; i < game_->GetChoicesPerQuestion(); i++) {
379 | char choice[CHOICE_LEN];
380 | snprintf(choice, CHOICE_LEN, "%d", allChoices[i]);
381 | game_buttons_[i]->SetAttribute("Text", (const char*)choice);
382 | game_buttons_[i]->SetAttribute("Enabled", true);
383 | }
384 | return true;
385 | }
386 | /*
387 | * Check the selection is the correct answer and update our local score
388 | */
389 | void Engine::CheckChoice(jui_helper::JUIButton *selection) {
390 | int idx = 0;
391 | while (idx < CHOICES_PER_QUESTION && game_buttons_[idx] != selection) {
392 | ++idx;
393 | }
394 | const int *allChoices = game_->GetAllChoices();
395 | if (allChoices[idx] == allChoices[game_->GetCorrectChoice()]) {
396 | // Update my own UI score
397 | ++score_counter_;
398 |
399 | // Broadcast this new score to other players
400 | BroadcastScore(score_counter_, false);
401 | UpdateScoreBoardUI(true);
402 | }
403 | }
404 |
405 | /*
406 | * JNI functions those manage activity lifecycle
407 | */
408 | extern "C" {
409 | /*
410 | * These callbacks are necessary to work Google Play Game Services UIs properly
411 | *
412 | * For apps which target Android 2.3 or 3.x devices (API Version prior to 14),
413 | * Play Game Services has no way to automatically receive Activity lifecycle
414 | * callbacks. In these cases, Play Game Services relies on the owning Activity
415 | * to notify it of lifecycle events. Any Activity which owns a GameServices
416 | * object should call the AndroidSupport::* functions from within their own
417 | * lifecycle callback functions. The arguments in these functions match those
418 | * provided by Android, so no additional processing is necessary.
419 | */
420 | JNIEXPORT void
421 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivityResult(
422 | JNIEnv *env, jobject thiz, jobject activity, jint requestCode,
423 | jint resultCode, jobject data) {
424 | gpg::AndroidSupport::OnActivityResult(env, activity, requestCode, resultCode,
425 | data);
426 | }
427 |
428 | JNIEXPORT void
429 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivityCreated(
430 | JNIEnv *env, jobject thiz, jobject activity, jobject saved_instance_state) {
431 | gpg::AndroidSupport::OnActivityCreated(env, activity, saved_instance_state);
432 | }
433 |
434 | JNIEXPORT void
435 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivityDestroyed(
436 | JNIEnv *env, jobject thiz, jobject activity) {
437 | gpg::AndroidSupport::OnActivityDestroyed(env, activity);
438 | }
439 |
440 | JNIEXPORT void
441 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivityPaused(
442 | JNIEnv *env, jobject thiz, jobject activity) {
443 | gpg::AndroidSupport::OnActivityPaused(env, activity);
444 | }
445 |
446 | JNIEXPORT void
447 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivityResumed(
448 | JNIEnv *env, jobject thiz, jobject activity) {
449 | gpg::AndroidSupport::OnActivityResumed(env, activity);
450 | }
451 |
452 | JNIEXPORT void
453 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivitySaveInstanceState(
454 | JNIEnv *env, jobject thiz, jobject activity, jobject out_state) {
455 | gpg::AndroidSupport::OnActivitySaveInstanceState(env, activity, out_state);
456 | }
457 |
458 | JNIEXPORT void
459 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivityStarted(
460 | JNIEnv *env, jobject thiz, jobject activity) {
461 | gpg::AndroidSupport::OnActivityStarted(env, activity);
462 | }
463 |
464 | JNIEXPORT void
465 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_nativeOnActivityStopped(
466 | JNIEnv *env, jobject thiz, jobject activity) {
467 | gpg::AndroidSupport::OnActivityStopped(env, activity);
468 | }
469 | }
470 |
--------------------------------------------------------------------------------
/app/src/main/cpp/NearbyNativeActivity.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
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 | #pragma once
17 |
18 | /*
19 | * Include files
20 | */
21 | #include
22 | #include
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | // For GPGS
31 | #include "gpg/gpg.h"
32 | #include "GFKSimpleGame.h"
33 | #include "NDKHelper.h"
34 | #include "JavaUI.h"
35 |
36 | /*
37 | * Preprocessors
38 | */
39 |
40 | // Class name of helper function
41 | #define HELPER_CLASS_NAME "com.sample.helper.NDKHelper"
42 | // Class name of JUIhelper function
43 | #define JUIHELPER_CLASS_NAME "com.sample.helper.JUIHelper"
44 | // Share object name of helper function library
45 | #define HELPER_CLASS_SONAME "NearbyNativeActivity"
46 |
47 | #define PAYLOAD_HEADER_LENGTH 1
48 | #define PAYLOAD_HEADER_NEW_CONNECTION 'c'
49 | #define PAYLOAD_HEADER_DISCONNECTED 'd'
50 | #define PAYLOAD_HEADER_RENEW_CONNECTION 'r'
51 | #define PAYLOAD_HEADER_GUEST_READY 'n'
52 | #define PAYLOAD_HEADER_INTERMEDIATE_SCORE 'i'
53 | #define PAYLOAD_HEADER_FINAL_SCORE 'f'
54 |
55 | const int32_t GAME_DURATION = 30;
56 | const int32_t CHOICES_PER_QUESTION = 4;
57 |
58 | struct PLAYER_STATUS {
59 | std::string device_id_;
60 | std::string endpoint_id_;
61 | int32_t score_;
62 | bool connected_; // this node [endpoint_id_] is still online
63 | bool finished_; // this node finished the game
64 | bool is_host_; // I am hosting this link[I was advertising and this node was
65 | // discovering
66 | bool is_direct_connection_; // this node directly connects to me,
67 | // not relayed to me by another device
68 | };
69 |
70 | class LogFunc {
71 | public:
72 | explicit LogFunc(const char *func_name) {
73 | func_name_ = std::string(func_name);
74 | LOGI("===>%s", func_name_.c_str());
75 | }
76 | ~LogFunc() { LOGI("<==%s", func_name_.c_str()); }
77 |
78 | private:
79 | std::string func_name_;
80 | };
81 |
82 | /*
83 | * Engine class of the sample: my class should be IEndpointDiscoverListener()
84 | */
85 | struct android_app;
86 | class Engine : public gpg::IEndpointDiscoveryListener {
87 | public:
88 | /*
89 | * nearby_connection_state are bit flags, they could co-exist.
90 | */
91 | enum nearby_connection_state {
92 | IDLE = 1,
93 | ADVERTISING = 2,
94 | DISCOVERING = 4,
95 | CONNECTED = 8,
96 | PLAYING = 16,
97 | FAILED = 32
98 | };
99 |
100 | // GPG-related methods
101 | void InitGoogleNearbyConnection();
102 |
103 | void InitializeGame();
104 | void PlayGame();
105 | void LeaveGame();
106 |
107 | // Event handling
108 | static void HandleCmd(struct android_app *app, int32_t cmd);
109 | static int32_t HandleInput(android_app *app, AInputEvent *event);
110 |
111 | // Engine life cycles
112 | Engine();
113 | ~Engine();
114 | void SetState(android_app *state);
115 | void InitDisplay(const int32_t cmd);
116 | void DrawFrame();
117 | void TermDisplay(const int32_t cmd);
118 | bool IsReady();
119 |
120 | // IEndpointDiscoverListener members
121 | virtual void OnEndpointFound(int64_t client_id,
122 | gpg::EndpointDetails const &endpoint_details);
123 | virtual void OnEndpointLost(int64_t client_id,
124 | std::string const &remote_endpoint_id);
125 |
126 | // MessageListnerHelper to handle the messages
127 | void OnMessageDisconnectedCallback(int64_t receiver_id,
128 | std::string const &remote_endpoint);
129 | void OnMessageReceivedCallback(int64_t receiver_id,
130 | std::string const &remote_endpoint,
131 | std::vector const &payload,
132 | bool is_reliable);
133 | void OnStopButtonClick(void);
134 |
135 | private:
136 | void OnAdvertiseButtonClick(void);
137 | void OnDiscoverButtonClick(void);
138 | void OnPlayButtonClick(void);
139 |
140 | void BroadcastNewConnection(std::string const &endpoint);
141 | void SendAllConnections(const std::string& accepting_endpoint_id);
142 | void OnConnectionResponse(gpg::ConnectionResponse const &response);
143 | void ProcessEndPointNotconnected(std::string const &remote_endpoint_id);
144 | void AddConnectionEndpoint(std::string const &remote_endpoint_id,
145 | bool is_native, bool is_host);
146 | void RemoveConnectionEndpoint(std::string const &remote_endpoint_id,
147 | bool need_broadcast = false);
148 | int32_t CountConnections(void);
149 | void UpdatePlayerScore(std::string const & endpoint_id,
150 | int score, bool final);
151 | void BroadcastScore(int32_t score, bool final);
152 | void RetrieveScores(std::string &score_str);
153 | bool BuildScorePayload(std::vector &payload, int score,
154 | std::string const & endpoint, bool final);
155 | bool DecodeScorePayload(std::vector const &payload, int *p_score,
156 | const std::string &endpoint);
157 |
158 | void DebugDumpConnections(void);
159 |
160 | void InitUI();
161 | void EnableUI(bool enable);
162 | void UpdateScoreBoardUI(bool UIThreadRequired);
163 |
164 | jui_helper::JUIButton *CreateChoiceButton(const char *cap,
165 | jui_helper::JUIButton *left,
166 | float fontSize = 17.f);
167 | void CheckChoice(jui_helper::JUIButton *selection);
168 | bool PlayOneRound(void);
169 |
170 | std::unique_ptr nearby_connection_;
171 |
172 | // hashmap to keep tracking of player scores
173 | std::unordered_map players_score_;
174 | int32_t score_counter_; // Score counter of local player
175 | bool playing_; // Am I playing a game?
176 | int game_time_; // Game start time
177 | game_helper::GFKSimple *game_;
178 |
179 | mutable std::mutex mutex_;
180 |
181 | // GLContext instance
182 | ndk_helper::GLContext *gl_context_;
183 |
184 | bool initialized_resources_;
185 | bool has_focus_;
186 |
187 | // Native activity app instance
188 | android_app *app_;
189 |
190 | // JUI dialog-related UI stuff here
191 | jui_helper::JUIDialog *dialog_;
192 |
193 | enum BUTTON_INDEX {
194 | BUTTON_ADVERTISE = 0,
195 | BUTTON_DISCOVER,
196 | BUTTON_PLAY_GAME,
197 | BUTTON_STOP,
198 | UI_BUTTON_COUNT
199 | };
200 | jui_helper::JUIButton *ui_buttons_[UI_BUTTON_COUNT];
201 | jui_helper::JUITextView *status_text_;
202 |
203 | jui_helper::JUITextView *time_text_;
204 | jui_helper::JUITextView *math_formula_;
205 | jui_helper::JUITextView *scores_text_;
206 | jui_helper::JUIButton *game_buttons_[CHOICES_PER_QUESTION];
207 |
208 | std::string service_id_;
209 | uint32_t nbc_state_;
210 | gpg::MessageListenerHelper msg_listener_;
211 | gpg::EndpointDiscoveryListenerHelper *discovery_helper_;
212 | };
213 |
--------------------------------------------------------------------------------
/app/src/main/cpp/NearbyNativeActivity_Engine.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
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 files
18 | */
19 | #include "NearbyNativeActivity.h"
20 |
21 | const int SELECTION_PER_QUESTION = 4;
22 | const int MATH_GRADE_LEVEL = 2;
23 | /*
24 | * Ctor
25 | */
26 | Engine::Engine()
27 | : initialized_resources_(false),
28 | has_focus_(false),
29 | app_(nullptr),
30 | dialog_(nullptr),
31 | status_text_(nullptr) {
32 | gl_context_ = ndk_helper::GLContext::GetInstance();
33 | game_ = new game_helper::GFKSimple(SELECTION_PER_QUESTION, MATH_GRADE_LEVEL);
34 | if (game_ == nullptr) {
35 | LOGE("Out of Memory in %s @line %d", __FILE__, __LINE__);
36 | return;
37 | }
38 | memset(ui_buttons_, 0, sizeof(ui_buttons_));
39 | memset(game_buttons_, 0, sizeof(game_buttons_));
40 | nbc_state_ = nearby_connection_state::IDLE;
41 | }
42 |
43 | /*
44 | * Dtor
45 | */
46 | Engine::~Engine() {
47 | // Clean up nearby connection ( PIGGYBACK on button stop action )
48 | OnStopButtonClick();
49 | delete game_;
50 | jui_helper::JUIWindow::GetInstance()->Close();
51 | delete dialog_;
52 | }
53 |
54 | /**
55 | * Initialize an EGL context for the current display.
56 | */
57 | void Engine::InitDisplay(const int32_t cmd) {
58 | if (!initialized_resources_) {
59 | gl_context_->Init(app_->window);
60 | InitUI();
61 | initialized_resources_ = true;
62 | } else {
63 | if (EGL_SUCCESS != gl_context_->Resume(app_->window)) {
64 | LOGE("Failed To Re-Initialize OpenGL functions");
65 | }
66 |
67 | jui_helper::JUIWindow::GetInstance()->Resume(app_->activity, cmd);
68 | }
69 |
70 | // Enable culling OpenGL state
71 | glEnable(GL_CULL_FACE);
72 |
73 | // Enabled depth test OpenGL state
74 | glEnable(GL_DEPTH_TEST);
75 | glDepthFunc(GL_LEQUAL);
76 |
77 | // Note that screen size might have been changed
78 | glViewport(0, 0, gl_context_->GetScreenWidth(),
79 | gl_context_->GetScreenHeight());
80 | }
81 |
82 | /**
83 | * Just the current frame in the display.
84 | */
85 | void Engine::DrawFrame() {
86 | float bkColor = .5f;
87 | glClearColor(bkColor, bkColor, bkColor, 1.f);
88 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
89 | gl_context_->Swap();
90 | }
91 |
92 | /**
93 | * Tear down the EGL context currently associated with the display.
94 | */
95 | void Engine::TermDisplay(const int32_t cmd) {
96 | gl_context_->Suspend();
97 | jui_helper::JUIWindow::GetInstance()->Suspend(cmd);
98 | }
99 |
100 | /**
101 | * HandleInput(): processing any gesture event we are interested in. For this
102 | * App, all event are handled on Java side
103 | */
104 | int32_t Engine::HandleInput(android_app *app, AInputEvent *event) {
105 | return 0;
106 | }
107 |
108 | /**
109 | * Process the next main command.
110 | */
111 | void Engine::HandleCmd(struct android_app *app, int32_t cmd) {
112 | Engine *eng = reinterpret_cast (app->userData);
113 | LOGI("message %d", cmd);
114 | switch (cmd) {
115 | case APP_CMD_INIT_WINDOW:
116 | if (app->window != NULL) {
117 | eng->InitDisplay(APP_CMD_INIT_WINDOW);
118 | eng->DrawFrame();
119 | }
120 | break;
121 | case APP_CMD_TERM_WINDOW:
122 | // Disconnect all connections before going down
123 | eng->OnStopButtonClick();
124 | // Note that JUI helper needs to know if a window has been terminated
125 | eng->TermDisplay(APP_CMD_TERM_WINDOW);
126 | eng->has_focus_ = false;
127 | break;
128 | case APP_CMD_RESUME:
129 | jui_helper::JUIWindow::GetInstance()->Resume(app->activity,
130 | APP_CMD_RESUME);
131 | // Players need re-connect since we teared down the connection when we
132 | // went suspension
133 | eng->EnableUI(true);
134 | break;
135 | case APP_CMD_GAINED_FOCUS:
136 | // Start animation
137 | eng->has_focus_ = true;
138 | jui_helper::JUIWindow::GetInstance()->Resume(app->activity,
139 | APP_CMD_GAINED_FOCUS);
140 | break;
141 | case APP_CMD_LOST_FOCUS:
142 | // Also stop animating.
143 | eng->has_focus_ = false;
144 | eng->DrawFrame();
145 | break;
146 | case APP_CMD_CONFIG_CHANGED:
147 | // Configuration changes
148 | eng->TermDisplay(APP_CMD_CONFIG_CHANGED);
149 | eng->InitDisplay(APP_CMD_CONFIG_CHANGED);
150 | break;
151 | case APP_CMD_DESTROY:
152 | ndk_helper::JNIHelper::GetInstance()->DetachCurrentThread();
153 | break;
154 | }
155 | }
156 |
157 | /*
158 | * Misc
159 | */
160 | void Engine::SetState(android_app *state) { app_ = state; }
161 |
162 | /*
163 | * disable 3D rendering for now
164 | */
165 | bool Engine::IsReady() {
166 | if (has_focus_) return true;
167 |
168 | return false;
169 | }
170 |
171 | /*
172 | * Our global instance for Game engine
173 | */
174 | Engine g_engine;
175 |
176 | /**
177 | * This is the main entry point of a native application that is using
178 | * android_native_app_glue. It runs in its own thread, with its own
179 | * event loop for receiving input events and doing other things.
180 | */
181 | void android_main(android_app *state) {
182 |
183 | g_engine.SetState(state);
184 |
185 | // Init helper functions
186 | ndk_helper::JNIHelper::Init(state->activity, HELPER_CLASS_NAME,
187 | HELPER_CLASS_SONAME);
188 |
189 | // Init play game services
190 | g_engine.InitGoogleNearbyConnection();
191 |
192 | state->userData = &g_engine;
193 | state->onAppCmd = Engine::HandleCmd;
194 | state->onInputEvent = Engine::HandleInput;
195 |
196 | // loop waiting for stuff to do.
197 | while (1) {
198 | // Read all pending events.
199 | int id;
200 | int events;
201 | android_poll_source *source;
202 |
203 | // If not animating, we will block forever waiting for events.
204 | // If animating, we loop until all events are read, then continue
205 | // to draw the next frame of animation.
206 | while ((id = ALooper_pollAll(g_engine.IsReady() ? 0 : -1, NULL, &events,
207 | reinterpret_cast(&source))) >= 0) {
208 | // Process this event.
209 | if (source != NULL) source->process(state, source);
210 |
211 | // Check if we are exiting.
212 | if (state->destroyRequested != 0) {
213 | g_engine.TermDisplay(APP_CMD_TERM_WINDOW);
214 | return;
215 | }
216 | }
217 |
218 | if (g_engine.IsReady()) {
219 | // Drawing is throttled to the screen update rate, so there
220 | // is no need to do timing here.
221 | g_engine.DrawFrame();
222 | }
223 | }
224 | }
225 |
226 | extern "C" {
227 | JNIEXPORT void
228 | Java_com_google_example_games_nearbyconnections_NearbyNativeActivity_OnPauseHandler(
229 | JNIEnv *env) {
230 | // This call is to suppress 'E/WindowManager(): android.view.WindowLeaked...'
231 | // errors.
232 | // Since orientation change events in NativeActivity comes later than
233 | // expected, we can not dismiss
234 | // popupWindow gracefully from NativeActivity.
235 | // So we are releasing popupWindows explicitly triggered from Java callback
236 | // through JNI call.
237 | jui_helper::JUIWindow::GetInstance()->Suspend(APP_CMD_PAUSE);
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/app/src/main/cpp/jui_helper/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2018 Google LLC
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 | # http://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 |
16 | # For more information about using CMake with Android Studio, read the
17 | # documentation: https://d.android.com/studio/projects/add-native-code.html
18 |
19 | # Sets the minimum version of CMake required to build the native library.
20 |
21 | cmake_minimum_required(VERSION 3.4.1)
22 | set(CMAKE_VERBOSE_MAKEFILE on)
23 |
24 | project(juihelper)
25 |
26 | ##include the native part of NDKHelper
27 | #add_subdirectory (${NDK_HELPER_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libndkhelper")
28 |
29 | add_library(juihelper STATIC
30 | JavaUI.cpp
31 | JavaUI_Dialog.cpp
32 | JavaUI_Window.cpp
33 | )
34 |
35 | target_include_directories(juihelper PRIVATE
36 | ${ANDROID_NDK}/sources/android/native_app_glue
37 | ${ndkhelper_SOURCE_DIR}/src/main/cpp
38 | ../ndk_helper
39 | )
40 | set_target_properties(juihelper
41 | PROPERTIES
42 | ARCHIVE_OUTPUT_DIRECTORY
43 | "${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}")
44 |
--------------------------------------------------------------------------------
/app/src/main/cpp/jui_helper/JavaUI.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
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 "JavaUI.h"
18 |
19 | namespace jui_helper {
20 |
21 | /*
22 | * Callback Handler for Java events
23 | */
24 | extern "C" {
25 | JNIEXPORT void Java_com_sample_helper_JUIHelper_JUICallbackHandler(
26 | JNIEnv *env, jobject thiz, int32_t id, int32_t message, int32_t param1,
27 | int32_t param2) {
28 | /*
29 | * id from Java is generated token from native side, which would be mapped back
30 | * into an UI pointer; so id is opaque to Java -- Java receives and returns back
31 | * the same id to native side
32 | */
33 | JUIBase *p = JUIBase::id_factory_.getUIBase(id);
34 | if( p )
35 | p->DispatchEvent(message, param1, param2);
36 | else {
37 | LOGE("Failed to get JUIBase pointer for %d in %s", id, __FILE__);
38 | }
39 | }
40 | }
41 |
42 | /*
43 | * IdFactory Implementation: our ID is just a simple integer starting from 1,and
44 | * keeps on counting up. There is no re-use -- even
45 | * one ID is removed from cache, we still counting up
46 | * It will take a long while to overflow a 32 bit integer
47 | * for UI ids. Even it does, it wrap back, at that time,
48 | * previous low values will long gone.
49 | */
50 | int32_t IdFactory::cur_id_ = 0;
51 | int32_t IdFactory::getId(const JUIBase* ui_object) {
52 | auto it = ids_.find(ui_object);
53 | if (it == ids_.end()) {
54 | LOGE("IDs not inside hash for %p, patching done (%d)", ui_object, cur_id_);
55 | ids_[ui_object] = ++cur_id_;
56 | return cur_id_;
57 | }
58 | return it->second;
59 | }
60 | JUIBase* IdFactory::getUIBase(int32_t ui_id) {
61 | for (auto id: ids_) {
62 | if(id.second == ui_id) {
63 | return const_cast(id.first);
64 | }
65 | }
66 | LOGE("Unable to find object point for ID(%d, %x)", ui_id, ui_id);
67 | return nullptr;
68 | }
69 | bool IdFactory::insert(const JUIBase* ui_object) {
70 | bool status = true;
71 | auto it = ids_.find(ui_object);
72 | if (it != ids_.end()) {
73 | LOGE("Error: %p is already in = %d", it->first, it->second);
74 | LOGE("Overwriting %p with %p for it", it->first, ui_object);
75 | status = false;
76 | //return status; ---> let it fall through
77 | }
78 | ids_[ui_object] = ++cur_id_;
79 | return status;
80 | }
81 | bool IdFactory::remove(const JUIBase* ui_object) {
82 | auto it = ids_.find(ui_object);
83 | if (it == ids_.end()) {
84 | LOGE("IDs not inside hash for %p!!!", ui_object);
85 | return false;
86 | }
87 | ids_.erase(it);
88 | return true;
89 | }
90 | void IdFactory::debugDumpCurrentHashTable(void) {
91 | for (auto id: ids_) {
92 | LOGI("Cached ID (%p = %d)", id.first, id.second);
93 | }
94 | }
95 |
96 | /*
97 | * Our global instance for id factory
98 | */
99 | IdFactory JUIBase::id_factory_;
100 |
101 | /*
102 | * JUIView
103 | */
104 | // Attribute types for View
105 | std::unordered_map JUIView::map_attributes_;
106 | const AttributeType JUIView::attributes_[] = {
107 | {"AccessibilityLiveRegion", ATTRIBUTE_PARAMETER_INT},
108 | {"Alpha", ATTRIBUTE_PARAMETER_FLOAT},
109 | {"BackgroundResource", ATTRIBUTE_PARAMETER_INT},
110 | {"Clickable", ATTRIBUTE_PARAMETER_BOOLEAN},
111 | {"Enabled", ATTRIBUTE_PARAMETER_BOOLEAN},
112 | {"DrawingCacheQuality", ATTRIBUTE_PARAMETER_INT},
113 | {"ScrollbarFadingEnabled", ATTRIBUTE_PARAMETER_BOOLEAN},
114 | {"FilterTouchesWhenObscured", ATTRIBUTE_PARAMETER_BOOLEAN},
115 | {"FitsSystemWindows", ATTRIBUTE_PARAMETER_BOOLEAN},
116 | {"Focusable", ATTRIBUTE_PARAMETER_BOOLEAN},
117 | {"FocusableATTRIBUTE_PARAMETER_INTouchMode", ATTRIBUTE_PARAMETER_BOOLEAN},
118 | {"HapticFeedbackEnabled", ATTRIBUTE_PARAMETER_BOOLEAN},
119 | {"Id", ATTRIBUTE_PARAMETER_INT},
120 | {"ImportantForAccessibility", ATTRIBUTE_PARAMETER_INT},
121 | {"ScrollContainer", ATTRIBUTE_PARAMETER_BOOLEAN},
122 | {"KeepScreenOn", ATTRIBUTE_PARAMETER_BOOLEAN},
123 | {"LayoutDirection", ATTRIBUTE_PARAMETER_INT},
124 | {"LongClickable", ATTRIBUTE_PARAMETER_BOOLEAN},
125 | {"MinimumHeight", ATTRIBUTE_PARAMETER_INT},
126 | {"MinimumWidth", ATTRIBUTE_PARAMETER_INT},
127 | {"NextFocusDownId", ATTRIBUTE_PARAMETER_INT},
128 | {"NextFocusForwardId", ATTRIBUTE_PARAMETER_INT},
129 | {"NextFocusLeftId", ATTRIBUTE_PARAMETER_INT},
130 | {"NextFocusRightId", ATTRIBUTE_PARAMETER_INT},
131 | {"NextFocusUpId", ATTRIBUTE_PARAMETER_INT},
132 | {"PaddingRelative", ATTRIBUTE_PARAMETER_IIII},
133 | {"Padding", ATTRIBUTE_PARAMETER_IIII},
134 | {"VerticalFadingEdgeEnabled", ATTRIBUTE_PARAMETER_BOOLEAN},
135 | {"Rotation", ATTRIBUTE_PARAMETER_FLOAT},
136 | {"RotationX", ATTRIBUTE_PARAMETER_FLOAT},
137 | {"RotationY", ATTRIBUTE_PARAMETER_FLOAT},
138 | {"SaveEnabled", ATTRIBUTE_PARAMETER_BOOLEAN},
139 | {"ScaleX", ATTRIBUTE_PARAMETER_FLOAT},
140 | {"ScaleY", ATTRIBUTE_PARAMETER_FLOAT},
141 | {"ScrollBarDefaultDelayBeforeFade", ATTRIBUTE_PARAMETER_INT},
142 | {"ScrollBarFadeDuration", ATTRIBUTE_PARAMETER_INT},
143 | {"ScrollBarSize", ATTRIBUTE_PARAMETER_INT},
144 | {"ScrollBarStyle", ATTRIBUTE_PARAMETER_INT},
145 | {"SoundEffectsEnabled", ATTRIBUTE_PARAMETER_BOOLEAN},
146 | {"TextAlignment", ATTRIBUTE_PARAMETER_INT},
147 | {"TextDirection", ATTRIBUTE_PARAMETER_INT},
148 | {"PivotX", ATTRIBUTE_PARAMETER_FLOAT},
149 | {"PivotY", ATTRIBUTE_PARAMETER_FLOAT},
150 | {"TranslationX", ATTRIBUTE_PARAMETER_FLOAT},
151 | {"TranslationY", ATTRIBUTE_PARAMETER_FLOAT},
152 | {"Visibility", ATTRIBUTE_PARAMETER_INT},
153 | };
154 |
155 | JUIView::JUIView()
156 | : layoutWidth_(ATTRIBUTE_SIZE_WRAP_CONTENT),
157 | layoutHeight_(ATTRIBUTE_SIZE_WRAP_CONTENT),
158 | layoutWeight_(0.f),
159 | marginLeft_(0),
160 | marginRight_(0),
161 | marginTop_(0),
162 | marginBottom_(0) {
163 | // setup attribute map (once)
164 | if (map_attributes_.size() == 0) {
165 | for (int32_t i = 0; i < sizeof(attributes_) / sizeof(attributes_[0]); ++i) {
166 | map_attributes_[std::string(attributes_[i].attribute_name)] =
167 | attributes_[i].attribute_type;
168 | }
169 | }
170 |
171 | for (int32_t i = 0; i < LAYOUT_PARAMETER_COUNT; ++i) {
172 | array_current_rules_[i] = LAYOUT_PARAMETER_UNKNOWN;
173 | }
174 | }
175 |
176 | JUIView::~JUIView() {
177 | auto it = map_attribute_parameters.begin();
178 | auto itEnd = map_attribute_parameters.end();
179 | while (it != itEnd) {
180 | AttributeParameterStore &p = map_attribute_parameters[it->first];
181 | switch (p.type) {
182 | case ATTRIBUTE_PARAMETER_STRING:
183 | if (it->second.str != NULL) delete it->second.str;
184 | break;
185 | default:
186 | break;
187 | }
188 | it++;
189 | }
190 | }
191 |
192 | /*
193 | * Add relative layout rule to the view
194 | */
195 | void JUIView::AddRule(const int32_t layoutParameterIndex,
196 | const int32_t parameter) {
197 | if (layoutParameterIndex < 0 ||
198 | layoutParameterIndex >= LAYOUT_PARAMETER_COUNT) {
199 | LOGE("Invalid rule index");
200 | return;
201 | }
202 | ndk_helper::JNIHelper::GetInstance()->CallVoidMethod(
203 | JUIWindow::GetHelperClassInstance(), "addRule",
204 | "(Landroid/view/View;II)V", GetJobject(), layoutParameterIndex,
205 | parameter);
206 | array_current_rules_[layoutParameterIndex] = parameter;
207 | }
208 |
209 | void JUIView::AddRule(const int32_t layoutParameterIndex,
210 | const JUIView *parameter) {
211 |
212 | /*
213 | * perform pointer to id xlation
214 | */
215 | AddRule(layoutParameterIndex, id_factory_.getId(parameter));
216 | }
217 |
218 | void JUIView::SetLayoutParams(const int32_t width, const int32_t height) {
219 | ndk_helper::JNIHelper::GetInstance()->CallVoidMethod(
220 | JUIWindow::GetHelperClassInstance(), "setLayoutParams",
221 | "(Landroid/view/View;II)V", GetJobject(), width, height);
222 | layoutWidth_ = width;
223 | layoutHeight_ = height;
224 | layoutWeight_ = 0.f;
225 | }
226 |
227 | void JUIView::SetLayoutParams(const int32_t width, const int32_t height,
228 | const float weight) {
229 | ndk_helper::JNIHelper::GetInstance()->CallVoidMethod(
230 | JUIWindow::GetHelperClassInstance(), "setLayoutParams",
231 | "(Landroid/view/View;IIF)V", GetJobject(), width, height, weight);
232 | layoutWidth_ = width;
233 | layoutHeight_ = height;
234 | layoutWeight_ = weight;
235 | }
236 |
237 | void JUIView::SetMargins(const int32_t left, const int32_t top,
238 | const int32_t right, const int32_t bottom) {
239 | ndk_helper::JNIHelper::GetInstance()->CallVoidMethod(
240 | JUIWindow::GetHelperClassInstance(), "setMargins",
241 | "(Landroid/view/View;IIII)V", GetJobject(), left, top, right, bottom);
242 | marginLeft_ = left;
243 | marginRight_ = right;
244 | marginTop_ = top;
245 | marginBottom_ = bottom;
246 | }
247 |
248 | void JUIView::RestoreParameters(std::unordered_map &map) {
249 | // Restore Layout Rule
250 | for (int32_t i = 0; i < LAYOUT_PARAMETER_COUNT; ++i) {
251 | if (array_current_rules_[i] != LAYOUT_PARAMETER_UNKNOWN) {
252 | AddRule(i, array_current_rules_[i]);
253 | }
254 | }
255 |
256 | auto it = map_attribute_parameters.begin();
257 | auto itEnd = map_attribute_parameters.end();
258 | while (it != itEnd) {
259 | AttributeParameterStore &p = map_attribute_parameters[it->first];
260 | switch (p.type) {
261 | case ATTRIBUTE_PARAMETER_INT:
262 | JUIBase::SetAttribute(map, it->first.c_str(), (int32_t)p.i);
263 | break;
264 | case ATTRIBUTE_PARAMETER_FLOAT:
265 | JUIBase::SetAttribute(map, it->first.c_str(), p.f);
266 | break;
267 | case ATTRIBUTE_PARAMETER_BOOLEAN:
268 | JUIBase::SetAttribute(map, it->first.c_str(), p.f);
269 | break;
270 | case ATTRIBUTE_PARAMETER_STRING:
271 | JUIBase::SetAttribute(map, it->first.c_str(), p.str->c_str());
272 | break;
273 | case ATTRIBUTE_PARAMETER_IF:
274 | JUIBase::SetAttribute(map, it->first.c_str(), p.param_if.i1,
275 | p.param_if.f2);
276 | break;
277 | case ATTRIBUTE_PARAMETER_FF:
278 | JUIBase::SetAttribute(map, it->first.c_str(), p.param_ff.f1,
279 | p.param_ff.f2);
280 | break;
281 | case ATTRIBUTE_PARAMETER_IIII:
282 | JUIBase::SetAttribute(map, it->first.c_str(), p.param_iiii.i1,
283 | p.param_iiii.i2, p.param_iiii.i3,
284 | p.param_iiii.i4);
285 | break;
286 | case ATTRIBUTE_PARAMETER_FFFI:
287 | JUIBase::SetAttribute(map, it->first.c_str(), p.param_fffi.f1,
288 | p.param_fffi.f2, p.param_fffi.f3, p.param_fffi.i);
289 | break;
290 | default:
291 | break;
292 | }
293 | it++;
294 | }
295 |
296 | if (layoutWidth_ != ATTRIBUTE_SIZE_WRAP_CONTENT ||
297 | layoutHeight_ != ATTRIBUTE_SIZE_WRAP_CONTENT || layoutWeight_ != 0.f) {
298 | if (layoutWeight_ != 0.f)
299 | SetLayoutParams(layoutWidth_, layoutHeight_, layoutWeight_);
300 | else
301 | SetLayoutParams(layoutWidth_, layoutHeight_);
302 | }
303 |
304 | if (marginLeft_ || marginRight_ || marginTop_ || marginBottom_) {
305 | SetMargins(marginLeft_, marginTop_, marginRight_, marginBottom_);
306 | }
307 | }
308 |
309 | std::unordered_map JUITextView::map_attributes_;
310 | const AttributeType JUITextView::attributes_[] = {
311 | {"AutoLinkMask", ATTRIBUTE_PARAMETER_INT},
312 | {"Text", ATTRIBUTE_PARAMETER_STRING},
313 | {"CursorVisible", ATTRIBUTE_PARAMETER_BOOLEAN},
314 | {"CompoundDrawablesWithIntrinsicBounds", ATTRIBUTE_PARAMETER_IIII},
315 | {"CompoundDrawablesRelativeWithIntrinsicBounds", ATTRIBUTE_PARAMETER_IIII},
316 | {"CompoundDrawablePadding", ATTRIBUTE_PARAMETER_INT},
317 | {"InputExtras", ATTRIBUTE_PARAMETER_INT},
318 | {"Ellipsize", ATTRIBUTE_PARAMETER_INT /*TextUtils.TruncateAt*/},
319 | {"Ems", ATTRIBUTE_PARAMETER_INT},
320 | {"Typeface", ATTRIBUTE_PARAMETER_INT /*Typeface*/},
321 | {"FreezesText", ATTRIBUTE_PARAMETER_BOOLEAN},
322 | {"Gravity", ATTRIBUTE_PARAMETER_INT},
323 | {"Height", ATTRIBUTE_PARAMETER_INT},
324 | {"Hint", ATTRIBUTE_PARAMETER_INT},
325 | {"ImeOptions", ATTRIBUTE_PARAMETER_INT},
326 | {"IncludeFontPadding", ATTRIBUTE_PARAMETER_BOOLEAN},
327 | {"RawInputType", ATTRIBUTE_PARAMETER_INT},
328 | {"LineSpacing", ATTRIBUTE_PARAMETER_FF},
329 | {"Lines", ATTRIBUTE_PARAMETER_INT},
330 | {"LinksClickable", ATTRIBUTE_PARAMETER_BOOLEAN},
331 | {"MarqueeRepeatLimit", ATTRIBUTE_PARAMETER_INT},
332 | {"MaxEms", ATTRIBUTE_PARAMETER_INT},
333 | {"MaxHeight", ATTRIBUTE_PARAMETER_INT},
334 | {"MaxLines", ATTRIBUTE_PARAMETER_INT},
335 | {"MaxWidth", ATTRIBUTE_PARAMETER_INT},
336 | {"MinEms", ATTRIBUTE_PARAMETER_INT},
337 | {"MinHeight", ATTRIBUTE_PARAMETER_INT},
338 | {"MinLines", ATTRIBUTE_PARAMETER_INT},
339 | {"MinWidth", ATTRIBUTE_PARAMETER_INT},
340 | {"PrivateImeOptions", ATTRIBUTE_PARAMETER_STRING},
341 | {"HorizontallyScrolling", ATTRIBUTE_PARAMETER_BOOLEAN},
342 | {"SelectAllOnFocus", ATTRIBUTE_PARAMETER_BOOLEAN},
343 | {"ShadowLayer", ATTRIBUTE_PARAMETER_FFFI},
344 | {"AllCaps", ATTRIBUTE_PARAMETER_BOOLEAN},
345 | {"TextColor", ATTRIBUTE_PARAMETER_INT},
346 | {"HighlightColor", ATTRIBUTE_PARAMETER_INT},
347 | {"HintTextColor", ATTRIBUTE_PARAMETER_INT},
348 | {"LinkTextColor", ATTRIBUTE_PARAMETER_INT},
349 | {"TextScaleX", ATTRIBUTE_PARAMETER_FLOAT},
350 | {"TextSize", ATTRIBUTE_PARAMETER_IF},
351 | {"Width", ATTRIBUTE_PARAMETER_INT}};
352 |
353 | JUITextView::JUITextView() : JUIView() {
354 | obj_ = JUIWindow::GetInstance()->CreateWidget("JUITextView", this);
355 | if (obj_ == NULL) LOGI("Class initialization failure");
356 |
357 | Init();
358 | }
359 |
360 | JUITextView::JUITextView(const char *str) : JUIView() {
361 | obj_ = JUIWindow::GetInstance()->CreateWidget("JUITextView", this);
362 | if (obj_ == NULL) LOGI("Class initialization failure");
363 |
364 | Init();
365 | SetAttribute("Text", str);
366 | }
367 |
368 | JUITextView::JUITextView(const bool b) : JUIView() {
369 | if (b == true)
370 | JUITextView();
371 | else
372 | Init();
373 | }
374 |
375 | void JUITextView::Init() {
376 | // setup attribute map (once)
377 | if (map_attributes_.size() == 0) {
378 | // Add base class's map
379 | map_attributes_.insert(JUIView::map_attributes_.begin(),
380 | JUIView::map_attributes_.end());
381 |
382 | for (int32_t i = 0; i < sizeof(attributes_) / sizeof(attributes_[0]); ++i) {
383 | map_attributes_[std::string(attributes_[i].attribute_name)] =
384 | attributes_[i].attribute_type;
385 | }
386 | }
387 | }
388 |
389 | JUITextView::~JUITextView() {
390 | if (obj_ != NULL) {
391 | jui_helper::JUIWindow::GetInstance()->CloseWidget(obj_);
392 | obj_ = NULL;
393 | }
394 | }
395 |
396 | void JUITextView::Restore() {
397 | // Recreate Java Widget when the activity has been disposed
398 | obj_ = JUIWindow::GetInstance()->CreateWidget("JUITextView", this);
399 | if (obj_ == NULL) LOGI("Class initialization failure");
400 |
401 | RestoreParameters(map_attributes_);
402 | }
403 |
404 | /*
405 | * Button
406 | */
407 | JUIButton::JUIButton() : JUITextView(false), onclick_callback_(NULL) { Init(); }
408 |
409 | JUIButton::JUIButton(const char *str) : JUITextView(false) {
410 | Init();
411 | SetAttribute("Text", str);
412 | }
413 |
414 | JUIButton::JUIButton(const bool b) : JUITextView(false) {
415 | if (b == true) JUIButton();
416 | }
417 |
418 | void JUIButton::Init() {
419 | obj_ = JUIWindow::GetInstance()->CreateWidget("JUIButton", this);
420 | if (obj_ == NULL) LOGI("Class initialization failure");
421 | }
422 |
423 | JUIButton::~JUIButton() {
424 | if (obj_ != NULL) {
425 | jui_helper::JUIWindow::GetInstance()->CloseWidget(obj_);
426 | obj_ = NULL;
427 | }
428 | }
429 |
430 | void JUIButton::Restore() {
431 | // Recreate Java Widget when the activity has been disposed
432 | obj_ = JUIWindow::GetInstance()->CreateWidget("JUIButton", this);
433 | if (obj_ == NULL) LOGI("Class initialization failure");
434 |
435 | RestoreParameters(map_attributes_);
436 | }
437 |
438 | void JUIButton::DispatchEvent(const int32_t message, const int32_t param1,
439 | const int32_t param2) {
440 | if (onclick_callback_ != NULL) onclick_callback_(this, message);
441 | }
442 |
443 | bool JUIButton::SetCallback(
444 | std::function callback) {
445 | bool b = true;
446 | onclick_callback_ = callback;
447 | return b;
448 | }
449 |
450 | } // namespace jui_helper
451 |
452 |
--------------------------------------------------------------------------------
/app/src/main/cpp/jui_helper/JavaUI.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
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 | #ifndef _USR_LOCAL_GOOGLE_SAMPLES_ANDROID_NEARBYCONNECTIONS_JNI_JUI_HELPER_JAVAUI_H_
18 | #define _USR_LOCAL_GOOGLE_SAMPLES_ANDROID_NEARBYCONNECTIONS_JNI_JUI_HELPER_JAVAUI_H_
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include