├── SCsub
├── config.pyc
├── config.fsql
├── res
└── values
│ └── ids.xml
├── colors.py
├── android
├── AndroidManifestChunk.xml
├── AndroidPermissionsChunk.xml
├── GooglePlay.java
└── PlayService.java
├── config.py
├── README.md
├── frogutils
└── Utils.java
└── License.md
/SCsub:
--------------------------------------------------------------------------------
1 | #SCsub
2 |
3 | Import('env')
4 |
5 |
--------------------------------------------------------------------------------
/config.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FrogSquare/GodotGoogleService/HEAD/config.pyc
--------------------------------------------------------------------------------
/config.fsql:
--------------------------------------------------------------------------------
1 |
2 | [module]
3 | name = GodotGoogleService
4 | author = RameshRavone (Ramesh Mani Maran)
5 | email = frogsquare@gmail.com
6 | license = Apache License 2.0
7 |
--------------------------------------------------------------------------------
/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 413469287607
6 |
7 |
8 |
--------------------------------------------------------------------------------
/colors.py:
--------------------------------------------------------------------------------
1 | RED = "\033[1;31m"
2 | BLUE = "\033[1;34m"
3 | CYAN = "\033[1;36m"
4 | GREEN = "\033[0;32m"
5 | RESET = "\033[0;0m"
6 | BOLD = "\033[;1m"
7 | REVERSE = "\033[;7m"
8 |
--------------------------------------------------------------------------------
/android/AndroidManifestChunk.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
6 |
--------------------------------------------------------------------------------
/android/AndroidPermissionsChunk.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | """
2 | # Copyright 2017 FrogSquare. All Rights Reserved.
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 | import os
18 | from colors import *
19 |
20 | # Set your Android app ID
21 | p_app_id = "com.example.game"
22 |
23 | def can_build(env_plat, plat = None):
24 | #return False
25 | if plat == None:
26 | #print("`GodotAds`"+RED+" master "+RESET+" branch not compatable with godot 2.X")
27 | #print("Try using `GodotAds` "+GREEN+" 2.X "+RESET+" branch for Godot 2.X")
28 |
29 | if isinstance(env_plat, basestring):
30 | plat = env_plat
31 | else:
32 | print("GodotGoogleService: "+RED+" Platform not set, Disabling GodotGoogleService "+RESET)
33 | return False
34 |
35 | if plat == "android":
36 | print("GodotGoogleService: " + GREEN + "Enabled" + RESET)
37 | return True
38 | else:
39 | print("GodotGoogleService: " + RED + "Disabled" + RESET)
40 | return False
41 | pass
42 |
43 | def implement(api, support=True):
44 | supportv4 = "{exclude group: 'com.android.support' exclude module: 'support-v4'}"
45 | return "implementation('"+api+"')" + (supportv4 if support else "")
46 | pass
47 |
48 | def configure(env):
49 | global p_app_id
50 | if env["platform"] == "android":
51 |
52 | if env.get("application_id", None) != None:
53 | p_app_id = env["application_id"]
54 |
55 | env.android_add_maven_repository("url 'https://maven.google.com'")
56 | env.android_add_maven_repository("url 'https://oss.sonatype.org/content/repositories/snapshots'")
57 |
58 | env.android_add_gradle_classpath("com.google.gms:google-services:4.1.0")
59 | env.android_add_gradle_plugin("com.google.gms.google-services")
60 |
61 | env.android_add_dependency(implement("com.android.support:support-fragment:28.0.0", False))
62 | env.android_add_dependency(implement("com.google.android.gms:play-services-auth:16.0.1"))
63 | env.android_add_dependency(implement("com.google.android.gms:play-services-games:16.0.0"))
64 |
65 | env.android_add_dependency(implement("com.google.firebase:firebase-invites:16.1.0"))
66 |
67 | env.android_add_java_dir("android");
68 | env.android_add_res_dir("res");
69 |
70 | if "frogutils" in [os.path.split(path)[1] for path in env.android_java_dirs]: pass
71 | else: env.android_add_java_dir("frogutils");
72 |
73 | env.android_add_to_manifest("android/AndroidManifestChunk.xml");
74 | env.android_add_to_permissions("android/AndroidPermissionsChunk.xml");
75 | env.android_add_default_config("applicationId '"+p_app_id+"'")
76 | env.disable_module()
77 |
78 | pass
79 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Godot GoogleService
2 |
3 | GodotGoogleService is a google play games integration for godot android;
4 |
5 | [](https://github.com/FrogSquare/GodotFireBase)
6 | [](https://github.com/godotengine/godot)
7 | [](https://www.apache.org/licenses/LICENSE-2.0)
8 | [](https://www.patreon.com/bePatron?u=5130479)
9 |
10 | # Cloning
11 | ```
12 | cd ${GODOT_ROOT}/modules/
13 | git clone https://github.com/FrogSquare/GodotGoogleService GodotGoogleService
14 | git clone https://github.com/FrogSquare/GodotSql GodotSql
15 | ```
16 | and you must configure your module by editing `${GODOT_ROOT}/modules/GodotGoogleService/config.py`
17 |
18 | # Depends on
19 |
20 | > Godot game engine: `git clone https://github.com/godotengine/godot`
21 |
22 | # Available Features
23 |
24 | > Login
25 |
26 | > Logout
27 |
28 | > achievements
29 |
30 | > leaderboard
31 |
32 | # Build/Compile module
33 |
34 | * Edit file modules/GodotGoogleService/config.py at line 2
35 | ```
36 | p_app_id = "com.your.appid" # config.py L:2
37 | ```
38 |
39 | * Replay `com.your.appid` with you android application id.
40 |
41 | # Initialize GodotGoogleService
42 |
43 | Edit engine.cfg and add
44 | ```
45 | [android]
46 | modules="org/godotengine/godot/GooglePlay"
47 | ```
48 |
49 | # GDScript - getting module singleton and initializing;
50 |
51 | ### On 2.X
52 |
53 | ```
54 | var google = Globals.get_singleton("GooglePlay");
55 | ```
56 |
57 | ### On 3.X (latest from git)
58 |
59 | ```
60 | var google = Engine.get_singleton("GooglePlay");
61 | ```
62 |
63 | And initialize GodotGoogleService with script instance id
64 |
65 | ```
66 | func _ready():
67 | if OS.get_name() == "Android":
68 | google.init(get_instance_id()) # use get_instance_id () for Godot 3.X
69 |
70 | func _receive_message(tag, from, key, data):
71 | if from == "GooglePlay":
72 | print("Key: ", key, " Data: ", data)
73 |
74 | ```
75 |
76 | # google play service Signin / Signout
77 | ```
78 | google.login()
79 | google.logout()
80 | ```
81 |
82 | # Google play achievements
83 | ```
84 | google.unlock_achievement("achievementID") # unlock achievement;
85 | google.increse_achievement("achievementID", int(n)) # increse achievements by step.
86 | google.show_achievements() # show achievements;
87 | ```
88 |
89 | # Google play Leaderboards
90 | ```
91 | google.submit_leaderboard(int(score), "leaderboardID") # submit score to leaderboard
92 | google.show_leaderboard("leaderboardID") # show leaderboard
93 | google.show_leaderboards() # show all available leaderboard
94 | ```
95 |
96 | # Additional
97 | ```
98 | google.get_version_code() # get package version code (Helper)
99 | ```
100 |
101 | # Log Event
102 | ```
103 | adb -d logcat godot:V GoogleService:V FrogSquare:V SignInIntentService:V SignInIntentService:V SignInActivity:V DEBUG:V AndroidRuntime:V ValidateServiceOp:V *:S
104 | ```
105 |
106 | And if you are using [GodotFirebase](http://github.com/FrogSquare/GodotFireBase) add these, `GodotSQL:V FireBase:V` to the command
107 |
108 |
--------------------------------------------------------------------------------
/android/GooglePlay.java:
--------------------------------------------------------------------------------
1 |
2 | package org.godotengine.godot;
3 |
4 | import android.app.Activity;
5 | import android.app.AlertDialog;
6 | import android.app.ProgressDialog;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.pm.PackageInfo;
10 | import android.content.pm.PackageManager.NameNotFoundException;
11 | import android.content.IntentSender.SendIntentException;
12 | import android.util.Log;
13 | import android.view.View;
14 | import android.os.Bundle;
15 |
16 |
17 | import java.util.Arrays;
18 | import java.util.List;
19 |
20 | import org.json.JSONObject;
21 | import org.json.JSONException;
22 |
23 | public class GooglePlay extends Godot.SingletonBase {
24 |
25 | static public Godot.SingletonBase initialize (Activity p_activity) {
26 | return new GooglePlay(p_activity);
27 | }
28 |
29 | public GooglePlay(Activity p_activity) {
30 | registerClass ("GooglePlay", new String[] {
31 | "init", "set_debug", "login", "logout", "unlock_achievement",
32 | "increase_achievement", "show_achievements",
33 | "submit_leaderboard", "show_leaderboard", "show_leaderboards",
34 | "get_version_code"
35 | });
36 |
37 | activity = p_activity;
38 | }
39 |
40 | public int get_version_code(final int instanceID) {
41 | try {
42 | final PackageInfo pInfo =
43 | activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0);
44 |
45 | return pInfo.versionCode;
46 | } catch (NameNotFoundException e) { }
47 |
48 | return 0;
49 | }
50 |
51 |
52 | public void init(final int instanceID) {
53 | activity.runOnUiThread(new Runnable() {
54 | public void run() {
55 | PlayService.getInstance(activity).init(instanceID);
56 | }
57 | });
58 | }
59 |
60 | public void set_debug(final boolean p_value) {
61 | Utils.set_debug(TAG, p_value);
62 | }
63 |
64 | public void initWithDict(final Dictionary dict, final int instanceID) {
65 | activity.runOnUiThread(new Runnable() {
66 | public void run() {
67 | PlayService.getInstance(activity).initAdvanced(dict, instanceID);
68 | }
69 | });
70 | }
71 |
72 | public void login() {
73 | activity.runOnUiThread(new Runnable() {
74 | public void run() {
75 | PlayService.getInstance(activity).connect();
76 | }
77 | });
78 | }
79 |
80 | public void logout() {
81 | activity.runOnUiThread(new Runnable() {
82 | public void run() {
83 | PlayService.getInstance(activity).disconnect();
84 | }
85 | });
86 | }
87 |
88 | public boolean isConnected() {
89 | return PlayService.getInstance(activity).isConnected();
90 | }
91 |
92 | public void unlock_achievement(final String id) {
93 | activity.runOnUiThread(new Runnable() {
94 | public void run() {
95 | PlayService.getInstance(activity).achievement_unlock(id);
96 | }
97 | });
98 | }
99 |
100 | public void increase_achievement(final String id, final int steps) {
101 | activity.runOnUiThread(new Runnable() {
102 | public void run() {
103 | PlayService.getInstance(activity).achievement_increment(id, steps);
104 | }
105 | });
106 | }
107 |
108 | public void show_achievements() {
109 | activity.runOnUiThread(new Runnable() {
110 | public void run() {
111 | PlayService.getInstance(activity).achievement_show_list();
112 | }
113 | });
114 | }
115 |
116 | public void submit_leaderboard(final int score, final String l_id) {
117 | activity.runOnUiThread(new Runnable() {
118 | public void run() {
119 | PlayService.getInstance(activity).leaderboard_submit(l_id, score);
120 | }
121 | });
122 | }
123 |
124 | public void show_leaderboard(final String l_id) {
125 | activity.runOnUiThread(new Runnable() {
126 | public void run() {
127 | PlayService.getInstance(activity).leaderboard_show(l_id);
128 | }
129 | });
130 | }
131 |
132 | public void show_leaderboards() {
133 | activity.runOnUiThread(new Runnable() {
134 | public void run() {
135 | PlayService.getInstance(activity).leaderboard_show_list();
136 | }
137 | });
138 | }
139 |
140 | protected void onMainActivityResult (int requestCode, int resultCode, Intent data) {
141 | Utils.d(TAG, "onActivityResult: reqCode=" + requestCode + ", resCode=" + resultCode);
142 |
143 | PlayService.getInstance(activity).onActivityResult(requestCode, resultCode, data);
144 | }
145 |
146 | protected void onMainPause () {
147 | PlayService.getInstance(activity).onPause();
148 | }
149 |
150 | protected void onMainResume () {
151 | // mFirebaseAnalytics.setCurrentScreen(activity, "Main", currentScreen);
152 | PlayService.getInstance(activity).onResume();
153 | }
154 |
155 | protected void onMainDestroy () {
156 | PlayService.getInstance(activity).onStop();
157 | }
158 |
159 | private static Context context;
160 | private static Activity activity;
161 |
162 | static final String TAG = "godot";
163 | }
164 |
--------------------------------------------------------------------------------
/frogutils/Utils.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 FrogSquare. All Rights Reserved.
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 | package org.godotengine.godot;
18 |
19 | import android.app.Activity;
20 | import android.content.Context;
21 | import android.content.res.AssetManager;
22 | import android.graphics.Bitmap;
23 | import android.graphics.BitmapFactory;
24 | import android.os.Bundle;
25 | import android.os.Environment;
26 | import android.provider.Settings;
27 | import android.util.Log;
28 |
29 | import com.godot.game.BuildConfig;
30 | import com.godot.game.R;
31 |
32 | import org.json.JSONObject;
33 | import org.json.JSONException;
34 |
35 | import java.io.InputStream;
36 | import java.io.InputStreamReader;
37 | import java.io.IOException;
38 | import java.io.BufferedReader;
39 | import java.security.MessageDigest;
40 | import java.security.NoSuchAlgorithmException;
41 | import java.util.HashMap;
42 | import java.util.Map;
43 | import java.util.Iterator;
44 |
45 | import org.godotengine.godot.Utils;
46 |
47 | public class Utils {
48 |
49 | public static final String TAG = "FrogSquare";
50 |
51 | public static final int FIREBASE_INVITE_REQUEST = 8002;
52 | public static final int FIREBASE_NOTIFICATION_REQUEST = 8003;
53 | public static final int FIREBASE_GOOGLE_SIGN_IN = 8004;
54 | public static final int FIREBASE_FACEBOOK_SIGN_IN = 8005;
55 | public static final int FIREBASE_TWITTER_SIGN_IN = 8006;
56 | // public static final int FIREBASE_ = ;
57 |
58 | public static void initDebug(final String from) {
59 | if (DebugCfg == null) {
60 | DebugCfg = new HashMap< String, Boolean>();
61 | }
62 |
63 | set_debug(from, true);
64 | }
65 |
66 | public static void set_debug(final String from, final boolean value) {
67 | if (DebugCfg == null) {
68 | initDebug(from);
69 | }
70 |
71 | DebugCfg.put(from, value);
72 | }
73 |
74 | public static boolean get_debug(final String from) {
75 | if (DebugCfg == null) {
76 | initDebug(from);
77 | }
78 |
79 | if (DebugCfg.containsKey(from)) {
80 | return DebugCfg.get(from);
81 | } else {
82 | set_debug(from, true);
83 | return DebugCfg.get(from);
84 | }
85 | }
86 |
87 | /** GodotSQL **/
88 | public static boolean get_db_bool(final String p_key) {
89 | String val = get_db_value(p_key);
90 |
91 | if (val.equals("0") || val.equals("false")) {
92 | return false;
93 | } else {
94 | return true;
95 | }
96 | }
97 |
98 | public static String get_db_value(final String p_key) {
99 | return KeyValueStorage.getValue(p_key);
100 | }
101 |
102 | public static void set_db_value(final String p_key, final String p_value) {
103 | KeyValueStorage.setValue(p_key, p_value);
104 | }
105 | /** GodotSQL **/
106 |
107 | public static void d(final String from, final String message) {
108 | if (get_debug(from)) {
109 | Log.d(TAG, message);
110 | }
111 | }
112 |
113 | public static void e(final String from, final String message) {
114 | if (get_debug(from)) {
115 | Log.e(TAG, message);
116 | }
117 | }
118 |
119 | public static void i(final String from, final String message) {
120 | if (get_debug(from)) {
121 | Log.i(TAG, message);
122 | }
123 | }
124 |
125 | public static void w(final String from, final String message) {
126 | if (get_debug(from)) {
127 | Log.w(TAG, message);
128 | }
129 | }
130 |
131 | public static Map jsonToMap (String jsonData) {
132 | JSONObject jobject = null;
133 |
134 | try { jobject = new JSONObject(jsonData); }
135 | catch (JSONException e) { Utils.d(TAG, "JSONObject exception: " + e.toString()); }
136 |
137 | Map retMap = new HashMap();
138 | Iterator keysItr = jobject.keys();
139 |
140 | while(keysItr.hasNext()) {
141 | try {
142 | String key = keysItr.next();
143 | Object value = jobject.get(key);
144 |
145 | retMap.put(key, value);
146 | } catch (JSONException e) {
147 | Utils.d(TAG, "JSONObject get key error" + e.toString());
148 | }
149 | }
150 |
151 | return retMap;
152 | }
153 |
154 | public static Bitmap getBitmapFromAsset(Context context, String filePath) {
155 | if (filePath.startsWith("res://")) { filePath = filePath.replaceFirst("res://", ""); }
156 |
157 | AssetManager assetManager = context.getAssets();
158 |
159 | InputStream istr;
160 | Bitmap bitmap = null;
161 |
162 | try {
163 | istr = assetManager.open(filePath);
164 | bitmap = BitmapFactory.decodeStream(istr);
165 | } catch (IOException e) {
166 | // handle exception
167 | }
168 |
169 | return bitmap;
170 | }
171 |
172 | public static String readFromFile(String fPath, Context context) {
173 | Utils.d(TAG, "Reading File: " + fPath);
174 | StringBuilder returnString = new StringBuilder();
175 |
176 | String fileName = fPath;
177 | if (fPath.startsWith("res://")) { fileName = fileName.replaceFirst("res://", ""); }
178 |
179 | InputStream fIn = null;
180 | InputStreamReader isr = null;
181 | BufferedReader input = null;
182 |
183 | try {
184 | fIn = context.getResources().getAssets()
185 | .open(fileName, Context.MODE_WORLD_READABLE);
186 |
187 | isr = new InputStreamReader(fIn);
188 | input = new BufferedReader(isr);
189 |
190 | String line = "";
191 |
192 | while ((line = input.readLine()) != null) {
193 | returnString.append(line);
194 | }
195 |
196 | }
197 | catch (Exception e) {
198 | Utils.d(TAG, "FileRead Failed: " + e.getMessage());
199 | }
200 | finally {
201 | try {
202 | if (isr != null) { isr.close(); }
203 | if (fIn != null) { fIn.close(); }
204 | if (input != null) { input.close(); }
205 |
206 | } catch (Exception e2) { e2.getMessage(); }
207 | }
208 |
209 | return returnString.toString();
210 | }
211 |
212 | public static String askForPath(final String folder) {
213 | return Environment.getExternalStorageDirectory().getAbsolutePath() + folder;
214 | }
215 |
216 | // { Device ID - https://stackoverflow.com/questions/4524752/how-can-i-get-device-id-for-admob
217 |
218 | public static String getDeviceId(Activity activity) {
219 | String android_id =
220 | Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID);
221 |
222 | String deviceId = md5(android_id).toUpperCase();
223 |
224 | return android_id;
225 | }
226 |
227 | public static final String md5(final String s) {
228 | try {
229 | // Create MD5 Hash
230 | MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
231 | digest.update(s.getBytes());
232 |
233 | byte messageDigest[] = digest.digest();
234 |
235 | // Create Hex String
236 | StringBuffer hexString = new StringBuffer();
237 |
238 | for (int i = 0; i < messageDigest.length; i++) {
239 | String h = Integer.toHexString(0xFF & messageDigest[i]);
240 |
241 | while (h.length() < 2) {
242 | h = "0" + h;
243 | }
244 |
245 | hexString.append(h);
246 | }
247 |
248 | return hexString.toString();
249 | } catch (NoSuchAlgorithmException e) { Utils.w(TAG, "FB:MD5:Algorithm:" + e.toString()); }
250 |
251 | return "";
252 | }
253 |
254 | /* Checks if external storage is available for read and write */
255 | public static boolean isExternalStorageWritable() {
256 | String state = Environment.getExternalStorageState();
257 | if (Environment.MEDIA_MOUNTED.equals(state)) {
258 | return true;
259 | }
260 |
261 | return false;
262 | }
263 |
264 | /* Checks if external storage is available to at least read */
265 | public static boolean isExternalStorageReadable() {
266 | String state = Environment.getExternalStorageState();
267 | if (Environment.MEDIA_MOUNTED.equals(state) ||
268 | Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
269 |
270 | return true;
271 | }
272 |
273 | return false;
274 | }
275 |
276 | // Check for class
277 | public static boolean isClass(String className) {
278 | try {
279 | Class.forName(className);
280 | return true;
281 | } catch (ClassNotFoundException e) { return false; }
282 | }
283 |
284 | public static void setScriptInstance(int instanceID) {
285 | script_instanceID = instanceID;
286 | }
287 |
288 | public static void callScriptCallback(
289 | int script_id, String function, String from, Object key, Object value) {
290 |
291 | GodotLib.calldeferred(script_id, function, new Object[] { TAG, from, key, value });
292 | }
293 |
294 | public static void callScriptCallback(String function, String from, Object key, Object value) {
295 | if (script_instanceID == -1) {
296 | Utils.d(TAG, "Script::Instance::NotSset");
297 | return;
298 | }
299 |
300 | GodotLib.calldeferred(script_instanceID, function,
301 | new Object[] { TAG, from, key, value });
302 | }
303 |
304 | public static void callScriptFunc(int script_id, String from, Object key, Object value) {
305 | GodotLib.calldeferred(script_id, "_receive_message",
306 | new Object[] { TAG, from, key, value });
307 | }
308 |
309 | public static void callScriptFunc(String from, Object key, Object value) {
310 | if (script_instanceID == -1) {
311 | Utils.d(TAG, "Script::Instance::NotSset");
312 | return;
313 | }
314 |
315 | GodotLib.calldeferred(script_instanceID, "_receive_message",
316 | new Object[] { TAG, from, key, value });
317 | }
318 |
319 | public static boolean checkGooglePlayService(Activity activity) {
320 | return true;
321 | }
322 |
323 | public static void putAllInDict(Bundle bundle, Dictionary keyValues) {
324 | String[] keys = keyValues.get_keys();
325 | for(int i=0; i < keys.length; i++) {
326 | String key = keys[i];
327 | Utils.putGodotValue(bundle, key, keyValues.get(key));
328 | }
329 | }
330 |
331 | public static void putGodotValue(Bundle bundle, String key, Object value) {
332 |
333 | if (value instanceof Boolean) {
334 | bundle.putBoolean(key, (Boolean) value);
335 |
336 | } else if (value instanceof Integer) {
337 | bundle.putInt(key, (Integer) value);
338 |
339 | } else if (value instanceof Double) {
340 | bundle.putDouble(key, (Double) value);
341 |
342 | } else if (value instanceof String) {
343 | bundle.putString(key, (String) value);
344 |
345 | } else {
346 |
347 | if (value != null) {
348 | bundle.putString(key, value.toString());
349 | }
350 |
351 | }
352 | }
353 |
354 | private static Map< String, Boolean> DebugCfg = null;
355 | public static int script_instanceID = -1;
356 | }
357 |
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
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,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/android/PlayService.java:
--------------------------------------------------------------------------------
1 |
2 | package org.godotengine.godot;
3 |
4 | import android.app.Activity;
5 | import android.app.AlertDialog;
6 | import android.app.ProgressDialog;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentSender.SendIntentException;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.os.Bundle;
13 | import android.support.annotation.NonNull;
14 |
15 | import java.util.Arrays;
16 | import java.util.List;
17 |
18 | import org.json.JSONObject;
19 | import org.json.JSONException;
20 |
21 | import com.google.android.gms.auth.api.Auth;
22 | import com.google.android.gms.auth.api.signin.GoogleSignIn;
23 | import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
24 | import com.google.android.gms.auth.api.signin.GoogleSignInClient;
25 | import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
26 | import com.google.android.gms.auth.api.signin.GoogleSignInResult;
27 | import com.google.android.gms.common.Scopes;
28 | import com.google.android.gms.common.api.ApiException;
29 | import com.google.android.gms.common.api.Scope;
30 | import com.google.android.gms.common.api.Status;
31 | import com.google.android.gms.tasks.OnCompleteListener;
32 | import com.google.android.gms.tasks.OnSuccessListener;
33 | import com.google.android.gms.tasks.OnFailureListener;
34 | import com.google.android.gms.games.AchievementsClient;
35 | import com.google.android.gms.games.Games;
36 | import com.google.android.gms.games.LeaderboardsClient;
37 | import com.google.android.gms.games.Player;
38 | import com.google.android.gms.games.PlayersClient;
39 | import com.google.android.gms.tasks.Task;
40 |
41 | public class PlayService {
42 |
43 | public static PlayService getInstance (Activity p_activity) {
44 | if (mInstance == null) {
45 | synchronized (PlayService.class) {
46 | mInstance = new PlayService(p_activity);
47 | }
48 | }
49 |
50 | return mInstance;
51 | }
52 |
53 | public PlayService(Activity p_activity) {
54 | activity = p_activity;
55 | }
56 |
57 | public void initAdvanced(final Dictionary dict, final int instanceID) {
58 | config = dict;
59 | init(instanceID);
60 | }
61 |
62 | public void init (final int instanceID) {
63 | script_id = instanceID;
64 | Utils.setScriptInstance(script_id);
65 |
66 | if (Utils.checkGooglePlayService(activity)) {
67 | Utils.d(TAG, "Play Service Available.");
68 | }
69 |
70 | GoogleSignInOptions.Builder builder =
71 | new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN);
72 |
73 | GoogleSignInOptions gso = builder.build();
74 | mGoogleSignInClient = GoogleSignIn.getClient(activity, gso);
75 |
76 | Utils.d(TAG, "Google::Initialized");
77 | onStart();
78 | }
79 |
80 | public boolean isConnected() {
81 | mAccount = GoogleSignIn.getLastSignedInAccount(activity);
82 | return mAccount != null;
83 | }
84 |
85 | public void connect() {
86 | if (mGoogleSignInClient == null) {
87 | Utils.d(TAG, "GoogleSignInClient not initialized");
88 | return;
89 | }
90 |
91 | if (isConnected()) {
92 | Utils.d(TAG, "Google service is already connected");
93 | return;
94 | }
95 |
96 | Intent signInIntent = mGoogleSignInClient.getSignInIntent();
97 | activity.startActivityForResult(signInIntent, GOOGLE_SIGN_IN_REQUEST);
98 | }
99 |
100 | public void disconnect() {
101 | mGoogleSignInClient.signOut()
102 | .addOnCompleteListener(activity, new OnCompleteListener() {
103 | @Override
104 | public void onComplete(@NonNull Task task) {
105 | Utils.d(TAG, "Google signed out.");
106 |
107 | mAchievementsClient = null;
108 | mLeaderboardsClient = null;
109 | mPlayersClient = null;
110 |
111 | Utils.callScriptFunc("GoogleService", "login", "false");
112 | }
113 | });
114 | }
115 |
116 | public void succeedSignIn() {
117 | Utils.d(TAG, "Google signed in.");
118 |
119 | mAchievementsClient = Games.getAchievementsClient(activity, mAccount);
120 | mLeaderboardsClient = Games.getLeaderboardsClient(activity, mAccount);
121 | mPlayersClient = Games.getPlayersClient(activity, mAccount);
122 |
123 | Games.getGamesClient(activity, mAccount).setViewForPopups(
124 | activity.getWindow().getDecorView().findViewById(android.R.id.content));
125 |
126 | Utils.callScriptFunc("GoogleService", "login", "true");
127 |
128 | mPlayersClient.getCurrentPlayer()
129 | .addOnCompleteListener(new OnCompleteListener() {
130 | @Override
131 | public void onComplete(@NonNull Task task) {
132 | String displayName = "UserName";
133 |
134 | if (task.isSuccessful()) {
135 | displayName = task.getResult().getDisplayName();
136 | } else {
137 | Exception e = task.getException();
138 | }
139 |
140 | Utils.callScriptFunc("GoogleService", "user", displayName);
141 | }
142 | });
143 | }
144 |
145 | public void achievement_unlock(final String achievement_id) {
146 | connect();
147 |
148 | if (isConnected()) {
149 | // KeyValueStorage.setValue(achievement_id, "true");
150 | mAchievementsClient.unlock(achievement_id);
151 |
152 | Utils.i(TAG, "PlayGameServices: achievement_unlock");
153 | } else { Utils.w(TAG, "PlayGameServices: Google calling connect"); }
154 | }
155 |
156 | public void achievement_increment(final String achievement_id, final int amount) {
157 | connect();
158 |
159 | if (isConnected()) {
160 | mAchievementsClient.increment(achievement_id, amount);
161 |
162 | Utils.i(TAG, "PlayGameServices: achievement_increased");
163 | } else { Utils.i(TAG, "PlayGameServices: Google calling connect"); }
164 | }
165 |
166 | public void achievement_show_list() {
167 | connect();
168 |
169 | if (isConnected()) {
170 | mAchievementsClient.getAchievementsIntent()
171 | .addOnSuccessListener(new OnSuccessListener() {
172 | @Override
173 | public void onSuccess(Intent intent) {
174 | activity.startActivityForResult(intent, REQUEST_ACHIEVEMENTS);
175 | }
176 | })
177 | .addOnFailureListener(new OnFailureListener() {
178 | @Override
179 | public void onFailure(@NonNull Exception e) {
180 | Utils.d(TAG, "Showing::Loaderboard::Failed:: " + e.toString());
181 | }
182 | });
183 |
184 | } else { Utils.i(TAG, "PlayGameServices: Google calling connect"); }
185 | }
186 |
187 | public void leaderboard_submit(String id, int score) {
188 | connect();
189 |
190 | if (isConnected()) {
191 | Utils.i(TAG, "Leaderboard::Submit::" + id + "::Score::" + score);
192 |
193 | mLeaderboardsClient.submitScore(id, score);
194 | } else { Utils.i(TAG, "Google not connected calling connect"); }
195 | }
196 |
197 | public void leaderboard_show(final String id) {
198 | connect();
199 |
200 | if (isConnected()) {
201 | mLeaderboardsClient.getLeaderboardIntent(id)
202 | .addOnSuccessListener(new OnSuccessListener() {
203 | @Override
204 | public void onSuccess (Intent intent) {
205 | Utils.d(TAG, "Showing::Loaderboard::" + id);
206 | activity.startActivityForResult(intent, REQUEST_LEADERBOARD);
207 | }
208 | })
209 | .addOnFailureListener(new OnFailureListener() {
210 | @Override
211 | public void onFailure(@NonNull Exception e) {
212 | Utils.d(TAG, "Showing::Loaderboard::Failed:: " + e.toString());
213 | }
214 | });
215 |
216 | } else { Utils.i(TAG, "Google not connected calling connect"); }
217 | }
218 |
219 | public void leaderboard_show_list() {
220 | connect();
221 |
222 | if (isConnected()) {
223 | mLeaderboardsClient.getAllLeaderboardsIntent()
224 | .addOnSuccessListener(new OnSuccessListener() {
225 | @Override
226 | public void onSuccess (Intent intent) {
227 | Utils.d(TAG, "Showing::Loaderboard::List");
228 | activity.startActivityForResult(intent, REQUEST_LEADERBOARD);
229 | }
230 | })
231 | .addOnFailureListener(new OnFailureListener() {
232 | @Override
233 | public void onFailure(@NonNull Exception e) {
234 | Utils.d(TAG, "Showing::Loaderboard::Failed:: " + e.toString());
235 | }
236 | });
237 |
238 | } else { Utils.i(TAG, "Google not connected calling connect"); }
239 | }
240 |
241 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
242 | if (requestCode == GOOGLE_SIGN_IN_REQUEST) {
243 | isIntentInProgress = false;
244 |
245 | GoogleSignInResult result =
246 | Auth.GoogleSignInApi.getSignInResultFromIntent(data);
247 |
248 | handleSignInResult(result);
249 | }
250 | }
251 |
252 | private void handleSignInResult(GoogleSignInResult m_result) {
253 | if (m_result.isSuccess()) {
254 | mAccount = m_result.getSignInAccount();
255 | succeedSignIn();
256 | } else {
257 | Status s = m_result.getStatus();
258 |
259 | Utils.w(TAG, "SignInResult::Failed code="
260 | + s.getStatusCode() + ", Message: " + s.getStatusMessage());
261 |
262 | if (isResolvingConnectionFailure) { return; }
263 | if (!isIntentInProgress && m_result.getStatus().hasResolution()) {
264 | try {
265 | isIntentInProgress = true;
266 |
267 | activity.startIntentSenderForResult(
268 | s.getResolution().getIntentSender(),
269 | GOOGLE_SIGN_IN_REQUEST, null, 0, 0, 0);
270 | } catch (SendIntentException ex) {
271 | connect();
272 | }
273 |
274 | isResolvingConnectionFailure = true;
275 | }
276 | }
277 | }
278 |
279 | private void signInSilently() {
280 | if (isConnected()) { return; }
281 |
282 | GoogleSignInClient signInClient = GoogleSignIn.getClient(activity,
283 | GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN);
284 |
285 | signInClient.silentSignIn().addOnCompleteListener(activity,
286 | new OnCompleteListener() {
287 | @Override
288 | public void onComplete(@NonNull Task task) {
289 | if (task.isSuccessful()) {
290 | // The signed in account is stored in the task's result.
291 | try {
292 | mAccount = task.getResult(ApiException.class);
293 | succeedSignIn();
294 | } catch (ApiException e) {
295 | Utils.w(TAG, "SignInResult::Failed code="
296 | + e.getStatusCode() + ", Message: "
297 | + e.getStatusMessage());
298 | }
299 | } else {
300 | // Player will need to sign-in explicitly using via UI
301 | Utils.d(TAG, "Silent::Login::Failed");
302 | }
303 | }
304 | });
305 | }
306 |
307 | public void onStart() {
308 | mAccount = GoogleSignIn.getLastSignedInAccount(activity);
309 |
310 | if (mAccount != null) {
311 | Utils.d(TAG, "Google already connected to an account");
312 | succeedSignIn();
313 | } else {
314 | Utils.d(TAG, "Google not connected");
315 | connect();
316 | //signInSilently();
317 | }
318 |
319 | boolean autoLaunchDeepLink = true;
320 | /**
321 | // Check for App Invite invitations and launch deep-link activity if possible.
322 | // Requires that an Activity is registered in AndroidManifest.xml to handle
323 | // deep-link URLs.
324 |
325 | FirebaseDynamicLinks.getInstance().getDynamicLink(getIntent())
326 | .addOnSuccessListener(this, new OnSuccessListener() {
327 | @Override
328 | public void onSuccess(PendingDynamicLinkData data) {
329 | if (data == null) {
330 | Utils.d(TAG, "getInvitation: no data");
331 | return;
332 | }
333 |
334 | // Get the deep link
335 | Uri deepLink = data.getLink();
336 |
337 | // Extract invite
338 | FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(data);
339 | if (invite != null) {
340 | String invitationId = invite.getInvitationId();
341 | }
342 |
343 | // Handle the deep link
344 | // ...
345 | }
346 | }).addOnFailureListener(this, new OnFailureListener() {
347 | @Override
348 | public void onFailure(@NonNull Exception e) {
349 | Utils.w(TAG, "getDynamicLink:onFailure", e);
350 | }
351 | });
352 | **/
353 | }
354 |
355 | public void onPause() {
356 |
357 | }
358 |
359 | public void onResume() {
360 | // Hide Google play UI's
361 | //signInSilently();
362 | }
363 |
364 | public void onStop() {
365 | activity = null;
366 | }
367 |
368 | private static Activity activity = null;
369 | private static Context context = null;
370 | private static PlayService mInstance = null;
371 |
372 | private static int script_id;
373 |
374 | private static final int GOOGLE_SIGN_IN_REQUEST = 9001;
375 | private static final int REQUEST_ACHIEVEMENTS = 9002;
376 | private static final int REQUEST_LEADERBOARD = 9003;
377 |
378 | private Boolean isIntentInProgress = false;
379 | private Boolean isResolvingConnectionFailure = false;
380 |
381 | private GoogleSignInClient mGoogleSignInClient;
382 |
383 | private GoogleSignInAccount mAccount;
384 | private AchievementsClient mAchievementsClient;
385 | private LeaderboardsClient mLeaderboardsClient;
386 | private PlayersClient mPlayersClient;
387 | private Dictionary config;
388 |
389 | private static final String TAG = "GoogleService";
390 | }
391 |
--------------------------------------------------------------------------------