@byvik/capacitor-play-games 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +478 -2
- package/android/build.gradle +54 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/byvik/capacitor/playgames/PlayGamesPlugin.java +416 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +763 -0
- package/dist/esm/definitions.d.ts +275 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +19 -0
- package/dist/esm/web.js +50 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +64 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +67 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +77 -4
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
package com.byvik.capacitor.playgames;
|
|
2
|
+
|
|
3
|
+
import android.content.Intent;
|
|
4
|
+
import android.net.Uri;
|
|
5
|
+
import android.util.Log;
|
|
6
|
+
import com.getcapacitor.JSObject;
|
|
7
|
+
import com.getcapacitor.Plugin;
|
|
8
|
+
import com.getcapacitor.PluginCall;
|
|
9
|
+
import com.getcapacitor.PluginMethod;
|
|
10
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
11
|
+
import com.google.android.gms.common.api.ApiException;
|
|
12
|
+
import com.google.android.gms.games.AuthenticationResult;
|
|
13
|
+
import com.google.android.gms.games.GamesClientStatusCodes;
|
|
14
|
+
import com.google.android.gms.games.PlayGames;
|
|
15
|
+
import com.google.android.gms.games.PlayGamesSdk;
|
|
16
|
+
import com.google.android.gms.games.SnapshotsClient;
|
|
17
|
+
import com.google.android.gms.games.leaderboard.Leaderboard;
|
|
18
|
+
import com.google.android.gms.games.leaderboard.LeaderboardVariant;
|
|
19
|
+
import com.google.android.gms.games.snapshot.Snapshot;
|
|
20
|
+
import com.google.android.gms.games.snapshot.SnapshotMetadataChange;
|
|
21
|
+
import com.google.android.gms.tasks.Task;
|
|
22
|
+
import java.io.IOException;
|
|
23
|
+
import java.nio.charset.StandardCharsets;
|
|
24
|
+
import org.json.JSONObject;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Google Play Games Services v2: sign-in, leaderboards, achievements and saved games.
|
|
28
|
+
*/
|
|
29
|
+
@CapacitorPlugin(name = "PlayGames")
|
|
30
|
+
public class PlayGamesPlugin extends Plugin {
|
|
31
|
+
|
|
32
|
+
private static final String TAG = "CapacitorPlayGames";
|
|
33
|
+
|
|
34
|
+
// The Play Games app is where a Google account is added and a player profile is created.
|
|
35
|
+
private static final String PLAY_GAMES_PACKAGE = "com.google.android.play.games";
|
|
36
|
+
|
|
37
|
+
private static final int RC_LEADERBOARD_UI = 9004;
|
|
38
|
+
private static final int RC_ACHIEVEMENTS_UI = 9005;
|
|
39
|
+
|
|
40
|
+
@Override
|
|
41
|
+
public void load() {
|
|
42
|
+
// Initializing the v2 SDK also starts its silent sign-in attempt. It only succeeds when the
|
|
43
|
+
// player already has a Play Games profile and has not declined before, which is why
|
|
44
|
+
// isAuthenticated() and signIn() exist.
|
|
45
|
+
try {
|
|
46
|
+
PlayGamesSdk.initialize(getContext());
|
|
47
|
+
} catch (Exception e) {
|
|
48
|
+
// A missing or wrong setup must not take the whole app down at launch.
|
|
49
|
+
Log.e(TAG, "PlayGamesSdk.initialize failed. Is com.google.android.gms.games.APP_ID in the manifest?", e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------- Session ----------
|
|
54
|
+
|
|
55
|
+
@PluginMethod
|
|
56
|
+
public void isAuthenticated(PluginCall call) {
|
|
57
|
+
PlayGames.getGamesSignInClient(getActivity())
|
|
58
|
+
.isAuthenticated()
|
|
59
|
+
.addOnCompleteListener((task) -> call.resolve(authResult("isAuthenticated", task)));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// The only SDK call that shows Google UI. This is where a player with a Google account but no
|
|
63
|
+
// Play Games profile gets to create one.
|
|
64
|
+
@PluginMethod
|
|
65
|
+
public void signIn(PluginCall call) {
|
|
66
|
+
PlayGames.getGamesSignInClient(getActivity())
|
|
67
|
+
.signIn()
|
|
68
|
+
.addOnCompleteListener((task) -> call.resolve(authResult("signIn", task)));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Neither session call rejects: "it failed" and "the player said no" both mean the game carries
|
|
72
|
+
// on signed out. The status code is what tells a declined prompt (16, 12501) apart from a
|
|
73
|
+
// misconfigured app (10: this build's SHA-1 or package name is missing from the OAuth
|
|
74
|
+
// credentials, so it will never work for anyone until the Play Console is fixed).
|
|
75
|
+
private JSObject authResult(String method, Task<AuthenticationResult> task) {
|
|
76
|
+
boolean authenticated = task.isSuccessful() && task.getResult() != null && task.getResult().isAuthenticated();
|
|
77
|
+
JSObject ret = new JSObject();
|
|
78
|
+
ret.put("isAuthenticated", authenticated);
|
|
79
|
+
Exception e = task.getException();
|
|
80
|
+
if (!authenticated && e != null) {
|
|
81
|
+
String message = describe(e);
|
|
82
|
+
if (e instanceof ApiException) {
|
|
83
|
+
ret.put("errorCode", ((ApiException) e).getStatusCode());
|
|
84
|
+
}
|
|
85
|
+
ret.put("errorMessage", message);
|
|
86
|
+
Log.w(TAG, method + " failed: " + message, e);
|
|
87
|
+
}
|
|
88
|
+
return ret;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@PluginMethod
|
|
92
|
+
public void getCurrentPlayer(PluginCall call) {
|
|
93
|
+
PlayGames.getPlayersClient(getActivity())
|
|
94
|
+
.getCurrentPlayer()
|
|
95
|
+
.addOnSuccessListener((player) -> {
|
|
96
|
+
JSObject ret = new JSObject();
|
|
97
|
+
ret.put("playerId", player.getPlayerId());
|
|
98
|
+
ret.put("displayName", player.getDisplayName());
|
|
99
|
+
ret.put("title", player.getTitle());
|
|
100
|
+
call.resolve(ret);
|
|
101
|
+
})
|
|
102
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// For when signIn() cannot help: the device has no Google account at all. The Play Games app
|
|
106
|
+
// can add one and create the profile; back in the game, the silent sign-in then succeeds.
|
|
107
|
+
// Falls back to the store listing when the app is not installed (or not visible to us).
|
|
108
|
+
@PluginMethod
|
|
109
|
+
public void openPlayGamesApp(PluginCall call) {
|
|
110
|
+
Intent app = getContext().getPackageManager().getLaunchIntentForPackage(PLAY_GAMES_PACKAGE);
|
|
111
|
+
if (app != null) {
|
|
112
|
+
app.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
113
|
+
try {
|
|
114
|
+
getContext().startActivity(app);
|
|
115
|
+
call.resolve();
|
|
116
|
+
return;
|
|
117
|
+
} catch (Exception e) {
|
|
118
|
+
// Fall through to the store listing.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (
|
|
122
|
+
openUri("market://details?id=" + PLAY_GAMES_PACKAGE) ||
|
|
123
|
+
openUri("https://play.google.com/store/apps/details?id=" + PLAY_GAMES_PACKAGE)
|
|
124
|
+
) {
|
|
125
|
+
call.resolve();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
call.reject("Cannot open Play Games");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private boolean openUri(String uri) {
|
|
132
|
+
try {
|
|
133
|
+
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
|
134
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
135
|
+
getContext().startActivity(intent);
|
|
136
|
+
return true;
|
|
137
|
+
} catch (Exception e) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------- Leaderboards ----------
|
|
143
|
+
|
|
144
|
+
@PluginMethod
|
|
145
|
+
public void submitScore(PluginCall call) {
|
|
146
|
+
String leaderboardId = call.getString("leaderboardId");
|
|
147
|
+
Long score = longArg(call, "score");
|
|
148
|
+
if (leaderboardId == null || score == null) {
|
|
149
|
+
call.reject("leaderboardId and score are required");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (Boolean.TRUE.equals(call.getBoolean("immediate", false))) {
|
|
153
|
+
// Waits for the server, so a standing read right after this resolves sees the new score.
|
|
154
|
+
PlayGames.getLeaderboardsClient(getActivity())
|
|
155
|
+
.submitScoreImmediate(leaderboardId, score)
|
|
156
|
+
.addOnSuccessListener((result) -> call.resolve())
|
|
157
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
158
|
+
} else {
|
|
159
|
+
PlayGames.getLeaderboardsClient(getActivity()).submitScore(leaderboardId, score);
|
|
160
|
+
call.resolve();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Rank, score and number of entries come from the same leaderboard variant, so they are read
|
|
165
|
+
// in one call: fetched separately, a rank could be paired with a count from another moment.
|
|
166
|
+
@PluginMethod
|
|
167
|
+
public void getLeaderboardStanding(PluginCall call) {
|
|
168
|
+
String leaderboardId = call.getString("leaderboardId");
|
|
169
|
+
if (leaderboardId == null) {
|
|
170
|
+
call.reject("leaderboardId is required");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
int timeSpan = timeSpan(call);
|
|
174
|
+
int collection = collection(call);
|
|
175
|
+
boolean forceReload = Boolean.TRUE.equals(call.getBoolean("forceReload", false));
|
|
176
|
+
PlayGames.getLeaderboardsClient(getActivity())
|
|
177
|
+
.loadLeaderboardMetadata(leaderboardId, forceReload)
|
|
178
|
+
.addOnSuccessListener((data) -> {
|
|
179
|
+
// Play reports "unknown" as -1; it is surfaced as null instead.
|
|
180
|
+
JSObject ret = new JSObject();
|
|
181
|
+
ret.put("rank", JSONObject.NULL);
|
|
182
|
+
ret.put("score", JSONObject.NULL);
|
|
183
|
+
ret.put("numScores", JSONObject.NULL);
|
|
184
|
+
Leaderboard leaderboard = data == null ? null : data.get();
|
|
185
|
+
if (leaderboard != null && leaderboard.getVariants() != null) {
|
|
186
|
+
for (LeaderboardVariant variant : leaderboard.getVariants()) {
|
|
187
|
+
if (variant.getTimeSpan() != timeSpan || variant.getCollection() != collection) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (variant.getNumScores() != LeaderboardVariant.NUM_SCORES_UNKNOWN) {
|
|
191
|
+
ret.put("numScores", variant.getNumScores());
|
|
192
|
+
}
|
|
193
|
+
if (variant.hasPlayerInfo()) {
|
|
194
|
+
if (variant.getPlayerRank() != LeaderboardVariant.PLAYER_RANK_UNKNOWN) {
|
|
195
|
+
ret.put("rank", variant.getPlayerRank());
|
|
196
|
+
}
|
|
197
|
+
if (variant.getRawPlayerScore() != LeaderboardVariant.PLAYER_SCORE_UNKNOWN) {
|
|
198
|
+
ret.put("score", variant.getRawPlayerScore());
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
call.resolve(ret);
|
|
205
|
+
})
|
|
206
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@PluginMethod
|
|
210
|
+
public void showLeaderboard(PluginCall call) {
|
|
211
|
+
String leaderboardId = call.getString("leaderboardId");
|
|
212
|
+
if (leaderboardId == null) {
|
|
213
|
+
call.reject("leaderboardId is required");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
PlayGames.getLeaderboardsClient(getActivity())
|
|
217
|
+
.getLeaderboardIntent(leaderboardId, timeSpan(call), collection(call))
|
|
218
|
+
.addOnSuccessListener((intent) -> {
|
|
219
|
+
getActivity().startActivityForResult(intent, RC_LEADERBOARD_UI);
|
|
220
|
+
call.resolve();
|
|
221
|
+
})
|
|
222
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@PluginMethod
|
|
226
|
+
public void showAllLeaderboards(PluginCall call) {
|
|
227
|
+
PlayGames.getLeaderboardsClient(getActivity())
|
|
228
|
+
.getAllLeaderboardsIntent()
|
|
229
|
+
.addOnSuccessListener((intent) -> {
|
|
230
|
+
getActivity().startActivityForResult(intent, RC_LEADERBOARD_UI);
|
|
231
|
+
call.resolve();
|
|
232
|
+
})
|
|
233
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private static int timeSpan(PluginCall call) {
|
|
237
|
+
String value = call.getString("timeSpan", "allTime");
|
|
238
|
+
if ("daily".equals(value)) {
|
|
239
|
+
return LeaderboardVariant.TIME_SPAN_DAILY;
|
|
240
|
+
}
|
|
241
|
+
if ("weekly".equals(value)) {
|
|
242
|
+
return LeaderboardVariant.TIME_SPAN_WEEKLY;
|
|
243
|
+
}
|
|
244
|
+
return LeaderboardVariant.TIME_SPAN_ALL_TIME;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private static int collection(PluginCall call) {
|
|
248
|
+
return "friends".equals(call.getString("collection", "public"))
|
|
249
|
+
? LeaderboardVariant.COLLECTION_FRIENDS
|
|
250
|
+
: LeaderboardVariant.COLLECTION_PUBLIC;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ---------- Achievements ----------
|
|
254
|
+
// The fire-and-forget variants are used on purpose: the SDK queues them while offline and
|
|
255
|
+
// syncs later, so these calls resolve as soon as the request is handed over.
|
|
256
|
+
|
|
257
|
+
@PluginMethod
|
|
258
|
+
public void unlockAchievement(PluginCall call) {
|
|
259
|
+
String achievementId = call.getString("achievementId");
|
|
260
|
+
if (achievementId == null) {
|
|
261
|
+
call.reject("achievementId is required");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
PlayGames.getAchievementsClient(getActivity()).unlock(achievementId);
|
|
265
|
+
call.resolve();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@PluginMethod
|
|
269
|
+
public void revealAchievement(PluginCall call) {
|
|
270
|
+
String achievementId = call.getString("achievementId");
|
|
271
|
+
if (achievementId == null) {
|
|
272
|
+
call.reject("achievementId is required");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
PlayGames.getAchievementsClient(getActivity()).reveal(achievementId);
|
|
276
|
+
call.resolve();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@PluginMethod
|
|
280
|
+
public void incrementAchievement(PluginCall call) {
|
|
281
|
+
String achievementId = call.getString("achievementId");
|
|
282
|
+
Integer steps = call.getInt("steps");
|
|
283
|
+
if (achievementId == null || steps == null) {
|
|
284
|
+
call.reject("achievementId and steps are required");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
PlayGames.getAchievementsClient(getActivity()).increment(achievementId, steps);
|
|
288
|
+
call.resolve();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Absolute progress rather than increments, for games whose local save is the source of truth.
|
|
292
|
+
// Play ignores a value lower than the one it already has, so a reinstall never lowers a counter.
|
|
293
|
+
@PluginMethod
|
|
294
|
+
public void setAchievementSteps(PluginCall call) {
|
|
295
|
+
String achievementId = call.getString("achievementId");
|
|
296
|
+
Integer steps = call.getInt("steps");
|
|
297
|
+
if (achievementId == null || steps == null) {
|
|
298
|
+
call.reject("achievementId and steps are required");
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
PlayGames.getAchievementsClient(getActivity()).setSteps(achievementId, steps);
|
|
302
|
+
call.resolve();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
@PluginMethod
|
|
306
|
+
public void showAchievements(PluginCall call) {
|
|
307
|
+
PlayGames.getAchievementsClient(getActivity())
|
|
308
|
+
.getAchievementsIntent()
|
|
309
|
+
.addOnSuccessListener((intent) -> {
|
|
310
|
+
getActivity().startActivityForResult(intent, RC_ACHIEVEMENTS_UI);
|
|
311
|
+
call.resolve();
|
|
312
|
+
})
|
|
313
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ---------- Saved games ----------
|
|
317
|
+
// Conflicts are resolved with "most recently modified": the plugin treats the cloud copy as a
|
|
318
|
+
// mirror of the local save, not as a second timeline to merge.
|
|
319
|
+
|
|
320
|
+
@PluginMethod
|
|
321
|
+
public void saveGame(PluginCall call) {
|
|
322
|
+
String name = call.getString("name");
|
|
323
|
+
String data = call.getString("data");
|
|
324
|
+
if (name == null || data == null) {
|
|
325
|
+
call.reject("name and data are required");
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
String description = call.getString("description");
|
|
329
|
+
SnapshotsClient client = PlayGames.getSnapshotsClient(getActivity());
|
|
330
|
+
client
|
|
331
|
+
.open(name, true, SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED)
|
|
332
|
+
.addOnSuccessListener((result) -> {
|
|
333
|
+
Snapshot snapshot = result.getData();
|
|
334
|
+
if (snapshot == null) {
|
|
335
|
+
call.reject("Unresolved snapshot conflict");
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
snapshot.getSnapshotContents().writeBytes(data.getBytes(StandardCharsets.UTF_8));
|
|
339
|
+
SnapshotMetadataChange.Builder change = new SnapshotMetadataChange.Builder();
|
|
340
|
+
if (description != null) {
|
|
341
|
+
change.setDescription(description);
|
|
342
|
+
}
|
|
343
|
+
client
|
|
344
|
+
.commitAndClose(snapshot, change.build())
|
|
345
|
+
.addOnSuccessListener((metadata) -> call.resolve())
|
|
346
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
347
|
+
})
|
|
348
|
+
.addOnFailureListener((e) -> reject(call, e));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Resolves with data = null when the saved game does not exist yet (a player's first launch),
|
|
352
|
+
// so "nothing saved" is not confused with "could not read".
|
|
353
|
+
@PluginMethod
|
|
354
|
+
public void loadGame(PluginCall call) {
|
|
355
|
+
String name = call.getString("name");
|
|
356
|
+
if (name == null) {
|
|
357
|
+
call.reject("name is required");
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
SnapshotsClient client = PlayGames.getSnapshotsClient(getActivity());
|
|
361
|
+
client
|
|
362
|
+
.open(name, false, SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED)
|
|
363
|
+
.addOnSuccessListener((result) -> {
|
|
364
|
+
Snapshot snapshot = result.getData();
|
|
365
|
+
if (snapshot == null) {
|
|
366
|
+
call.reject("Unresolved snapshot conflict");
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
String data = new String(snapshot.getSnapshotContents().readFully(), StandardCharsets.UTF_8);
|
|
371
|
+
JSObject ret = new JSObject();
|
|
372
|
+
ret.put("data", data);
|
|
373
|
+
call.resolve(ret);
|
|
374
|
+
} catch (IOException e) {
|
|
375
|
+
reject(call, e);
|
|
376
|
+
} finally {
|
|
377
|
+
client.discardAndClose(snapshot);
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
.addOnFailureListener((e) -> {
|
|
381
|
+
if (e instanceof ApiException && ((ApiException) e).getStatusCode() == GamesClientStatusCodes.SNAPSHOT_NOT_FOUND) {
|
|
382
|
+
JSObject ret = new JSObject();
|
|
383
|
+
ret.put("data", JSONObject.NULL);
|
|
384
|
+
call.resolve(ret);
|
|
385
|
+
} else {
|
|
386
|
+
reject(call, e);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---------- Helpers ----------
|
|
392
|
+
|
|
393
|
+
// PluginCall.getLong() only accepts values already parsed as Long, and JSON integers that fit in
|
|
394
|
+
// an int are not.
|
|
395
|
+
private static Long longArg(PluginCall call, String key) {
|
|
396
|
+
Object value = call.getData().opt(key);
|
|
397
|
+
return value instanceof Number ? ((Number) value).longValue() : null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// The Google API status code travels as the error code, so callers can branch on it.
|
|
401
|
+
private void reject(PluginCall call, Exception e) {
|
|
402
|
+
String code = e instanceof ApiException ? String.valueOf(((ApiException) e).getStatusCode()) : null;
|
|
403
|
+
call.reject(describe(e), code, e);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// An ApiException's own message is just "<code>: ". The status name says what happened.
|
|
407
|
+
private static String describe(Exception e) {
|
|
408
|
+
if (e instanceof ApiException) {
|
|
409
|
+
ApiException api = (ApiException) e;
|
|
410
|
+
String name = GamesClientStatusCodes.getStatusCodeString(api.getStatusCode());
|
|
411
|
+
String detail = api.getStatus().getStatusMessage();
|
|
412
|
+
return detail == null || detail.isEmpty() ? name : name + ": " + detail;
|
|
413
|
+
}
|
|
414
|
+
return String.valueOf(e.getMessage());
|
|
415
|
+
}
|
|
416
|
+
}
|
|
File without changes
|