├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── doc
├── 1_YOUR_GOOGLE_PLAY_GAME_APP_ID.png
├── 2_YOUR_GOOGLE_PLAY_GAME_APP_ID.png
├── 3.png
├── 4.png
├── 5.png
├── 6_if_Signing_certificate_fingerprint_(SHA1)_is_blank.png
├── 7.png
├── 8.png
├── cocoon_APP_ID1.png
├── cocoon_APP_ID2.png
├── intelxdk.config.additions.xml
├── iossandbox1.png
├── iossandbox2.png
├── iossandbox3.png
└── xdk_APP_ID.png
├── example
├── basic
│ └── index.html
└── basic_tag
│ └── index.html
├── package.json
├── plugin.xml
├── src
├── android
│ ├── Game.java
│ ├── GameHelper.java
│ ├── GameHelperUtils.java
│ └── ids.xml
└── ios
│ ├── Game.h
│ └── Game.m
└── www
└── game.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # =========================
18 | # Operating System Files
19 | # =========================
20 |
21 | # OSX
22 | # =========================
23 |
24 | .DS_Store
25 | .AppleDouble
26 | .LSOverride
27 |
28 | # Icon must end with two \r
29 | Icon
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
38 | # Directories potentially created on remote AFP share
39 | .AppleDB
40 | .AppleDesktop
41 | Network Trash Folder
42 | Temporary Items
43 | .apdisk
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2014 Sang Ki Kwon (Cranberrygame)
4 | Email: cranberrygame@yahoo.com
5 | Homepage: http://www.github.com/cranberrygame
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Cordova Game plugin
2 | ====================
3 |
4 | # Overview #
5 | Show leaderboard and achievements (google play game and game center, SDK)
6 |
7 | [android, ios] [cordova cli] [xdk] [phonegap build service]
8 |
9 | Requires google play developer account https://play.google.com/apps/publish/
10 | Requires apple developer account https://developer.apple.com/devcenter/ios/index.action
11 |
12 | This is open source cordova plugin.
13 |
14 | You can see Cordova Plugins in one page: http://cranberrygame.github.io?referrer=github
15 |
16 | ```c
17 | cf)
18 |
19 | Leaderboard game: Best score game
20 | Limited life
21 | ex) 1, 3
22 | Limited time
23 | ex) 30 seconds
24 | Time is score
25 |
26 | Achievement
27 | Score
28 | ex) Achievement1 (Score 10)
29 | Achievement2 (Score 30)
30 | Achievement3 (Score 60)
31 | Achievement4 (Score 100)
32 | Achievement5 (Score 150)
33 | Level
34 | ex) Achievement1 (Level 1)
35 | Achievement2 (Level 2)
36 | Achievement3 (Level 4)
37 | Achievement4 (Level 6)
38 | Achievement5 (Level 8)
39 | Achievement6 (Level 10)
40 | Category
41 | ex) Achievement1 (Number)
42 | Achievement1 (Fruit)
43 | Achievement1 (Color)
44 | Achievement1 (Other)
45 | Achievement1 (Challenge (Limited time))
46 | ```
47 | # Change log #
48 | ```c
49 | 1.0.109
50 | Fixed crash issue when show leaderbord after logout.
51 | 1.0.112
52 | Added show leaderboards method.
53 | 1.0.113
54 | Fixed crash issue when submit score after logout.
55 | 1.0.115
56 | Refixed crash issue when submit score after logout.
57 | ```
58 | # Install plugin #
59 |
60 | ## Cordova cli ##
61 | https://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-Line%20Interface - npm install -g cordova@6.0.0
62 | ```c
63 | //caution: replace 1064334934918 with your google play game app id
64 | cordova plugin add cordova-plugin-game --variable APP_ID="1064334934918"
65 | ```
66 | ## Xdk ##
67 | //caution: replace 1064334934918 with your google play game app id
68 | 
69 | https://github.com/cranberrygame/cordova-plugin-game/blob/master/doc/intelxdk.config.additions.xml
70 | ```c
71 | ```
72 |
73 | ## Cocoon ##
74 | https://cocoon.io - Create project - [specific project] - Setting - Plugins - Custom - Git Url: https://github.com/cranberrygame/cordova-plugin-game.git - INSTALL - Save
75 | //caution: replace 1064334934918 with your google play game app id
76 | https://cocoon.io - Create project - [specific project] - Setting - Plugins - Installed - Git Url https://github.com/cranberrygame/cordova-plugin-game.git - ADD PARAMETER - Name: APP_ID Value: 1064334934918 - Save
77 | 
78 |
79 |
80 | ## Phonegap build service (config.xml) ##
81 | https://build.phonegap.com/ - Apps - [specific project] - Update code - Zip file including config.xml
82 | ```c
83 | //caution: replace 1064334934918 with your google play game app id
84 |
85 |
86 |
87 | ```
88 |
89 | ## Construct2 ##
90 | Download construct2 plugin: http://www.paywithapost.de/pay?id=4ef3f2be-26e8-4a04-b826-6680db13a8c8
91 |
92 | Now all the native plugins are installed automatically: https://plus.google.com/102658703990850475314/posts/XS5jjEApJYV
93 | # Server setting #
94 |
95 | ## android (Google Play Game) ##
96 | 
97 | 
98 | 
99 | If you migrate android app from one build system to another build system (e.g from xdk to cocoon), link Android step ~ again.
100 | 
101 | 
102 | _is_blank.png)
103 | 
104 | 
105 | ```c
106 | //add game
107 | google play developer console - Game services - Add a new game - Enter the name of your game: Test App, Category: Puzzle
108 |
109 | //get YOUR_GOOGLE_PLAY_GAME_APP_ID
110 | google play developer console - Game services - [specific app] - get YOUR_GOOGLE_PLAY_GAME_APP_ID (the number that appears beside the game name in the header of the Developer Console, e.g. "Test App - 12345678",. The YOUR_GOOGLE_PLAY_GAME_APP_ID in this case is 12345678.)
111 |
112 | //link app
113 | google play developer console - Game services - [specific app] - Linked App - Android - Save and continue - Authorize your app now - Continue - Create Client (if Signing certificate fingerprint (SHA1) is blank, do it after publishing app in alpha, beta, or normal (after publishing, it will be filled automatically))
114 |
115 | //add leaderboard, get YOUR_GOOGLE_PLAY_GAME_LEADERBOARD_ID
116 | google play developer console - Game services - [specific app] - leaderboard - Add leaderboard - Name: Leaderboard - get YOUR_GOOGLE_PLAY_GAME_LEADERBOARD_ID
117 |
118 | //add achievements (must minimum 5 achievements), get YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID1, YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID2, YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID3, YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID4, YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID5
119 | google play developer console - Game services - [specific app] - achievement - Add achievement - Name: Achievement1 (Score 10), Description: Achievement1 (Score 10) - get YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID1
120 | google play developer console - Game services - [specific app] - achievement - Add achievement - Name: Achievement2 (Score 30), Description: Achievement2 (Score 30) - get YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID2
121 | google play developer console - Game services - [specific app] - achievement - Add achievement - Name: Achievement3 (Score 60), Description: Achievement3 (Score 60) - get YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID3
122 | google play developer console - Game services - [specific app] - achievement - Add achievement - Name: Achievement4 (Score 100), Description: Achievement4 (Score 100) - get YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID4
123 | google play developer console - Game services - [specific app] - achievement - Add achievement - Name: Achievement5 (Score 150), Description: Achievement5 (Score 150) - get YOUR_GOOGLE_PLAY_GAME_ACHIEVEMENT_ID5
124 |
125 | //publish game
126 | google play developer console - Game services - [specific app] - prepare test - publish game
127 | ```
128 | ## ios (Game Center) ##
129 | ```c
130 | itunesconnect - 나의 App - [specific app] - Game Center - Enable for Single Game
131 |
132 | //leaderboard
133 | itunesconnect - 나의 App - [specific app] - Game Center - Add Leaderboard -
134 | 단일 순위표 -
135 | 순위표 세트 식별 정보: testapp_leaderboard -
136 | 순위표 ID: testapp_leaderboard -
137 | 점수 형식 유형: Integer -
138 | 점수 제출 유형: 가장 높은 점수
139 | 정렬 순서: 높은 점수순 -
140 | Add Language -
141 | 언어: English -
142 | 이름: Leaderboard -
143 | 점수 형식: Integer (100,000,122) -
144 | Save
145 |
146 | //achievement1
147 | itunesconnect - 나의 App - [specific app] - Game Center - 목표 달성 추가 -
148 | 목표 달성 식별 정보: testapp_achievement1
149 | 목표 달성 ID: testapp_achievement1
150 | 점수 값: 20 (max 100)
151 | 숨김: No
152 | 여러 번 달성 가능: No
153 | 언어 추가 -
154 | 언어: English
155 | 제목: Achievement1 (Score 10)
156 | 사전 획득한 목표 달성 설명: Achievement1 (Score 10)
157 | 획득한 목표 달성 설명: Achievement1 (Score 10)
158 | 이미지: 512x512 png
159 |
160 | //achievement2
161 | itunesconnect - 나의 App - [specific app] - Game Center - 목표 달성 추가 -
162 | 목표 달성 식별 정보: testapp_achievement2
163 | 목표 달성 ID: testapp_achievement2
164 | 점수 값: 40 (max 100)
165 | 숨김: No
166 | 여러 번 달성 가능: No
167 | 언어 추가 -
168 | 언어: English
169 | 제목: Achievement2 (Score 30)
170 | 사전 획득한 목표 달성 설명: Achievement2 (Score 30)
171 | 획득한 목표 달성 설명: Achievement2 (Score 30)
172 | 이미지: 512x512 png
173 |
174 | //achievement3
175 | itunesconnect - 나의 App - [specific app] - Game Center - 목표 달성 추가 -
176 | 목표 달성 식별 정보: testapp_achievement3
177 | 목표 달성 ID: testapp_achievement3
178 | 점수 값: 60 (max 100)
179 | 숨김: No
180 | 여러 번 달성 가능: No
181 | 언어 추가 -
182 | 언어: English
183 | 제목: Achievement3 (Score 60)
184 | 사전 획득한 목표 달성 설명: Achievement3 (Score 60)
185 | 획득한 목표 달성 설명: Achievement3 (Score 60)
186 | 이미지: 512x512 png
187 |
188 | //achievement4
189 | itunesconnect - 나의 App - [specific app] - Game Center - 목표 달성 추가 -
190 | 목표 달성 식별 정보: testapp_achievement4
191 | 목표 달성 ID: testapp_achievement4
192 | 점수 값: 80 (max 100)
193 | 숨김: No
194 | 여러 번 달성 가능: No
195 | 언어 추가 -
196 | 언어: English
197 | 제목: Achievement4 (Score 100)
198 | 사전 획득한 목표 달성 설명: Achievement4 (Score 100)
199 | 획득한 목표 달성 설명: Achievement4 (Score 100)
200 | 이미지: 512x512 png
201 |
202 | //achievement5
203 | itunesconnect - 나의 App - [specific app] - Game Center - 목표 달성 추가 -
204 | 목표 달성 식별 정보: testapp_achievement5
205 | 목표 달성 ID: testapp_achievement5
206 | 점수 값: 100 (leave blank, max 100)
207 | 숨김: No
208 | 여러 번 달성 가능: No
209 | 언어 추가 -
210 | 언어: English
211 | 제목: Achievement5 (Score 150)
212 | 사전 획득한 목표 달성 설명: Achievement5 (Score 150)
213 | 획득한 목표 달성 설명: Achievement5 (Score 150)
214 | 이미지: 512x512 png
215 |
216 | can test before publish
217 | ```
218 | # API #
219 | ```javascript
220 | //
221 | var leaderboardId = "REPLACE_THIS_WITH_YOUR_LEADERBOARD_ID";
222 | var achievementId1 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID1";
223 | var achievementId2 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID2";
224 | var achievementId3 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID3";
225 | var achievementId4 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID4";
226 | var achievementId5 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID5";
227 | /*
228 | var leaderboardId;
229 | var achievementId1;
230 | var achievementId2;
231 | var achievementId3;
232 | var achievementId4;
233 | var achievementId5;
234 | //android
235 | if (navigator.userAgent.match(/Android/i)) {
236 | leaderboardId = "REPLACE_THIS_WITH_YOUR_LEADERBOARD_ID";
237 | achievementId1 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID1";
238 | achievementId2 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID2";
239 | achievementId3 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID3";
240 | achievementId4 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID4";
241 | achievementId5 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID5";
242 | }
243 | //ios
244 | else if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
245 | leaderboardId = "REPLACE_THIS_WITH_YOUR_LEADERBOARD_ID";
246 | achievementId1 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID1";
247 | achievementId2 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID2";
248 | achievementId3 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID3";
249 | achievementId4 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID4";
250 | achievementId5 = "REPLACE_THIS_WITH_YOUR_ACHIEVEMENT_ID5";
251 | }
252 | */
253 |
254 | //
255 | document.addEventListener("deviceready", function(){
256 | window.game.setUp();
257 |
258 | //callback
259 | window.game.onLoginSucceeded = function(result) {
260 | var playerDetail = result;
261 | alert('onLoginSucceeded: ' + playerDetail['playerId'] + ' ' + playerDetail['playerDisplayName']);
262 | };
263 | window.game.onLoginFailed = function() {
264 | alert('onLoginFailed');
265 | };
266 | //
267 | window.game.onSubmitScoreSucceeded = function() {
268 | alert('onSubmitScoreSucceeded');
269 | };
270 | window.game.onSubmitScoreFailed = function() {
271 | alert('onSubmitScoreFailed');
272 | };
273 | window.game.onGetPlayerScoreSucceeded = function(result) {
274 | var playerScore = result;
275 | alert('onGetPlayerScoreSucceeded: ' + playerScore);
276 | };
277 | window.game.onGetPlayerScoreFailed = function() {
278 | alert('onGetPlayerScoreFailed');
279 | };
280 | //
281 | window.game.onUnlockAchievementSucceeded = function() {
282 | alert('onUnlockAchievementSucceeded');
283 | };
284 | window.game.onUnlockAchievementFailed = function() {
285 | alert('onUnlockAchievementFailed');
286 | };
287 | window.game.onIncrementAchievementSucceeded = function() {
288 | alert('onIncrementAchievementSucceeded');
289 | };
290 | window.game.onIncrementAchievementFailed = function() {
291 | alert('onIncrementAchievementFailed');
292 | };
293 | window.game.onResetAchievementsSucceeded = function() {
294 | alert('onResetAchievementsSucceeded');
295 | };
296 | window.game.onResetAchievementsFailed = function() {
297 | alert('onResetAchievementsFailed');
298 | };
299 | //
300 | window.game.onGetPlayerImageSucceeded = function(result) {
301 | var playerImageUrl = result;
302 | alert('onGetPlayerImageSucceeded: ' + playerImageUrl);
303 | };
304 | window.game.onGetPlayerImageFailed = function() {
305 | alert('onGetPlayerImageFailed');
306 | };
307 | }, false);
308 |
309 | //
310 | window.game.login();
311 | window.game.logout();
312 | alert(window.game.isLoggedIn());
313 |
314 | //
315 | window.game.submitScore(leaderboardId, 5);//leaderboardId, score
316 | window.game.showLeaderboard(leaderboardId);
317 | window.game.showLeaderboards();
318 | window.game.getPlayerScore(leaderboardId);
319 |
320 | //
321 | window.game.unlockAchievement(achievementId1);
322 | window.game.unlockAchievement(achievementId2);
323 | window.game.unlockAchievement(achievementId3);
324 | window.game.unlockAchievement(achievementId4);
325 | window.game.unlockAchievement(achievementId5);
326 | window.game.incrementAchievement(achievementId1, 2); //achievementId, incrementalStepOrCurrentPercent: Incremental step (android) or current percent (ios) for incremental achievement.
327 | window.game.incrementAchievement(achievementId2, 2);
328 | window.game.incrementAchievement(achievementId3, 2);
329 | window.game.incrementAchievement(achievementId4, 2);
330 | window.game.incrementAchievement(achievementId5, 2);
331 | window.game.showAchievements();
332 | window.game.resetAchievements();//only supported on ios
333 |
334 | //
335 | window.game.getPlayerImage();
336 |
337 | ```
338 | # Examples #
339 | example/index.html
340 | example_tag/index.html
341 |
342 | # Test #
343 |
344 | ## android (Google Play Game) ##
345 |
346 | ```c
347 | //publish as alpha test (recommend) or beta test instead of production.
348 | google play developer console - [specific app] - APK - Alpha test - Upload as alpha test - Drag and drop apk and publish now as alpha test.
349 |
350 | //add test user for game
351 | google play developer console - Game services - [specific game] - test - add tester (google play account)
352 |
353 | //add test community for alpha test or beta test download
354 | google play developer console -
355 | All applications -
356 | [specific app] -
357 | APK -
358 | Alpha testers -
359 | Manage list of testers -
360 | Add Google groups or Google+ community: https://plus.google.com/communities/xxx (if you want make Google+ Community, go to this: https://plus.google.com/communities) -
361 | Add -
362 | Let test user download and install apk from this url: https://play.google.com/apps/testing/YOUR_PACKAGE (invite test user in your Google+ community, wait until this url is available, take hours)
363 | ```
364 |
365 | ```c
366 | Clear the default account so that a different account can be signed in without having to clear app data:
367 | 1.android
368 | Setting - Account - Google - Logout with previous google account and login with other google account
369 | 2.ios
370 | Setting - Game Center - Logout with previous ios account and login with other ios account
371 | ```
372 |
373 | ## ios (Game Center) ##
374 |
375 | 
376 | 
377 |
378 |
379 | ```c
380 | //itunes connect sand box (Caution!)
381 | itunes connect - User and role - Sand box test - add tester (not real email but faked email)
382 |
383 | //iphone sand box (Caution!)
384 | //The requested operation could not be completed because the application is not recognized by Game Center.
385 | iphone - Setting - Game Center - activate sand box mode - login with itunes connect sand box account in the app
386 | ```
387 |
388 | ```c
389 | the requested operation has been canceled or disabled by the user
390 | Reenabling GameCenter after user-cancelled 3 times (iOS7 only)
391 | iphone - Setting - General - Reset - Reset All Settings
392 | http://stackoverflow.com/questions/18927723/reenabling-gamecenter-after-user-cancelled-3-times-ios7-only
393 | ```
394 |
395 | # Useful links #
396 |
397 | Cordova Plugins
398 | http://cranberrygame.github.io?referrer=github
399 |
400 | # Credits #
401 |
--------------------------------------------------------------------------------
/doc/1_YOUR_GOOGLE_PLAY_GAME_APP_ID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/1_YOUR_GOOGLE_PLAY_GAME_APP_ID.png
--------------------------------------------------------------------------------
/doc/2_YOUR_GOOGLE_PLAY_GAME_APP_ID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/2_YOUR_GOOGLE_PLAY_GAME_APP_ID.png
--------------------------------------------------------------------------------
/doc/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/3.png
--------------------------------------------------------------------------------
/doc/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/4.png
--------------------------------------------------------------------------------
/doc/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/5.png
--------------------------------------------------------------------------------
/doc/6_if_Signing_certificate_fingerprint_(SHA1)_is_blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/6_if_Signing_certificate_fingerprint_(SHA1)_is_blank.png
--------------------------------------------------------------------------------
/doc/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/7.png
--------------------------------------------------------------------------------
/doc/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/8.png
--------------------------------------------------------------------------------
/doc/cocoon_APP_ID1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/cocoon_APP_ID1.png
--------------------------------------------------------------------------------
/doc/cocoon_APP_ID2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/cocoon_APP_ID2.png
--------------------------------------------------------------------------------
/doc/intelxdk.config.additions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/doc/iossandbox1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/iossandbox1.png
--------------------------------------------------------------------------------
/doc/iossandbox2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/iossandbox2.png
--------------------------------------------------------------------------------
/doc/iossandbox3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/iossandbox3.png
--------------------------------------------------------------------------------
/doc/xdk_APP_ID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cranberrygame/cordova-plugin-game/f69c35efb408e8f312c27f570a914283007ae238/doc/xdk_APP_ID.png
--------------------------------------------------------------------------------
/example/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/example/basic_tag/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cordova-plugin-game",
3 | "version": "1.0.120",
4 | "description": "show leaderboard and achievements (google play game and game center, SDK)",
5 | "cordova": {
6 | "id": "cordova-plugin-game",
7 | "platforms": [
8 | "android",
9 | "ios"
10 | ]
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/cranberrygame/cordova-plugin-game.git"
15 | },
16 | "keywords": [
17 | "cordova",
18 | "plugin",
19 | "game",
20 | "google play game",
21 | "game center",
22 | "ecosystem:cordova",
23 | "cordova-android",
24 | "cordova-ios"
25 | ],
26 | "engines": [
27 | {
28 | "name": "cordova",
29 | "version": ">=3.0.0"
30 | }
31 | ],
32 | "author": "cranberrygame",
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/cranberrygame/cordova-plugin-game/issues"
36 | },
37 | "homepage": "https://github.com/cranberrygame/cordova-plugin-game#readme"
38 | }
39 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | Cordova Game plugin
8 | show leaderboard and achievements (google play game and game center, SDK)
9 | Sang Ki Kwon (Cranberrygame)
10 | MIT
11 | cordova,plugin,game,google play game,game center
12 | https://github.com/cranberrygame/cordova-plugin-game
13 | https://github.com/cranberrygame/cordova-plugin-game/issues
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | $APP_ID
44 |
45 |
46 |
47 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
62 |
63 |
64 |
70 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/src/android/Game.java:
--------------------------------------------------------------------------------
1 | //Copyright (c) 2014 Sang Ki Kwon (Cranberrygame)
2 | //Email: cranberrygame@yahoo.com
3 | //Homepage: http://cranberrygame.github.io
4 | //License: MIT (http://opensource.org/licenses/MIT)
5 | package com.cranberrygame.cordova.plugin.game;
6 |
7 | import org.apache.cordova.CordovaPlugin;
8 | import org.apache.cordova.PluginResult;
9 | import org.apache.cordova.CallbackContext;
10 | import org.json.JSONArray;
11 | import org.json.JSONObject;
12 | import org.json.JSONException;
13 | import org.apache.cordova.CordovaInterface;
14 | import org.apache.cordova.CordovaWebView;
15 | import android.app.Activity;
16 | import android.util.Log;
17 | //
18 | //import com.google.android.gms.common.GooglePlayServicesUtil;//deprecated
19 | import com.google.android.gms.common.GoogleApiAvailability;//
20 | import android.content.Intent;
21 | import com.google.example.games.basegameutils.GameHelper;
22 | import com.google.android.gms.common.ConnectionResult;
23 | import com.google.android.gms.common.api.GoogleApiClient;
24 | //import com.google.android.gms.appstate.AppStateManager;//deprecated
25 | import android.view.Gravity;
26 | import com.google.android.gms.games.Games;//cranberrygame
27 | import android.content.IntentSender;
28 | import android.os.Bundle;
29 | import com.google.android.gms.games.achievement.Achievements;
30 | import com.google.android.gms.games.leaderboard.Leaderboards;
31 | import com.google.android.gms.common.api.*;
32 | import com.google.android.gms.games.GamesStatusCodes;
33 | import com.google.android.gms.games.leaderboard.LeaderboardScore;
34 | import com.google.android.gms.games.Player;
35 | import android.net.Uri;
36 | import com.google.android.gms.games.leaderboard.LeaderboardVariant;
37 |
38 | //Util
39 | import android.app.AlertDialog;
40 | import android.content.DialogInterface;
41 |
42 | class Util {
43 |
44 | //ex) Util.alert(cordova.getActivity(),"message");
45 | public static void alert(Activity activity, String message) {
46 | AlertDialog ad = new AlertDialog.Builder(activity).create();
47 | ad.setCancelable(false); // This blocks the 'BACK' button
48 | ad.setMessage(message);
49 | ad.setButton("OK", new DialogInterface.OnClickListener() {
50 | @Override
51 | public void onClick(DialogInterface dialog, int which) {
52 | dialog.dismiss();
53 | }
54 | });
55 | ad.show();
56 | }
57 | }
58 |
59 | public class Game extends CordovaPlugin implements GameHelper.GameHelperListener{
60 | private String LOG_TAG = "Game";
61 | private GameHelper mHelper;
62 | private CallbackContext loginCC;
63 | private CallbackContext getPlayerImageCC;
64 | private CallbackContext getPlayerScoreCC;
65 | private CallbackContext submitScoreCC;
66 | private CallbackContext unlockAchievementCC;
67 | private CallbackContext incrementAchievementCC;
68 | //
69 | private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000 ;//
70 |
71 | public void initialize(CordovaInterface cordova, CordovaWebView webView) {
72 | super.initialize(cordova, webView);
73 |
74 | }
75 | @Override
76 | public boolean execute(String action, JSONArray args,CallbackContext callbackContext) throws JSONException {
77 | PluginResult result = null;
78 |
79 | /*
80 | //deprecated
81 | if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this.cordova.getActivity()) != 0) {
82 | Log.e(LOG_TAG, "Google Play Services are unavailable");
83 | callbackContext.error("Unavailable");
84 | return true;
85 | }
86 | else {
87 | Log.d(LOG_TAG, "** Google Play Services are available **");
88 | }
89 | */
90 | GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
91 | int res = googleAPI.isGooglePlayServicesAvailable(this.cordova.getActivity());
92 | if(res != ConnectionResult.SUCCESS) {
93 | //if(googleAPI.isUserResolvableError(result)) {
94 | // googleAPI.getErrorDialog(this.cordova.getActivity(), res, PLAY_SERVICES_RESOLUTION_REQUEST).show();
95 | //}
96 |
97 | Log.e(LOG_TAG, "Google Play Services are unavailable");
98 | callbackContext.error("Unavailable");
99 | return true;
100 | }
101 | else {
102 | Log.d(LOG_TAG, "** Google Play Services are available **");
103 | }
104 |
105 | //args.length()
106 | //args.getString(0)
107 | //args.getString(1)
108 | //args.getInt(0)
109 | //args.getInt(1)
110 | //args.getBoolean(0)
111 | //args.getBoolean(1)
112 |
113 | if (action.equals("setUp")) {
114 | //Activity activity=cordova.getActivity();
115 | //webView
116 | //
117 |
118 | final CallbackContext delayedCC = callbackContext;
119 | cordova.getActivity().runOnUiThread(new Runnable(){
120 | @Override
121 | public void run() {
122 | _setUp();
123 |
124 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
125 | //pr.setKeepCallback(true);
126 | delayedCC.sendPluginResult(pr);
127 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
128 | //pr.setKeepCallback(true);
129 | //delayedCC.sendPluginResult(pr);
130 | }
131 | });
132 |
133 | return true;
134 | }
135 | else if (action.equals("login")) {
136 | //Activity activity=cordova.getActivity();
137 | //webView
138 | //
139 |
140 | loginCC = callbackContext;
141 |
142 | final CallbackContext delayedCC = callbackContext;
143 | cordova.getActivity().runOnUiThread(new Runnable(){
144 | @Override
145 | public void run() {
146 |
147 | if (getGameHelper().isSignedIn()) {
148 | /*
149 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
150 | //pr.setKeepCallback(true);
151 | //delayedCC.sendPluginResult(pr);
152 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Already logged in");
153 | //pr.setKeepCallback(true);
154 | delayedCC.sendPluginResult(pr);
155 | */
156 | onSignInSucceeded();
157 | }
158 | else {
159 | _login();
160 | }
161 | }
162 | });
163 |
164 | return true;
165 | }
166 | else if (action.equals("logout")) {
167 | //Activity activity=cordova.getActivity();
168 | //webView
169 | //
170 |
171 | final CallbackContext delayedCC = callbackContext;
172 | cordova.getActivity().runOnUiThread(new Runnable(){
173 | @Override
174 | public void run() {
175 | if (getGameHelper().isSignedIn()) {
176 | _logout();
177 |
178 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
179 | //pr.setKeepCallback(true);
180 | delayedCC.sendPluginResult(pr);
181 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
182 | //pr.setKeepCallback(true);
183 | //delayedCC.sendPluginResult(pr);
184 | }
185 | else {
186 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
187 | //pr.setKeepCallback(true);
188 | //delayedCC.sendPluginResult(pr);
189 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Already logged out");
190 | //pr.setKeepCallback(true);
191 | delayedCC.sendPluginResult(pr);
192 | }
193 | }
194 | });
195 |
196 | return true;
197 | }
198 | else if (action.equals("getPlayerImage")) {
199 | //Activity activity=cordova.getActivity();
200 | //webView
201 | //
202 |
203 | getPlayerImageCC = callbackContext;
204 |
205 | final CallbackContext delayedCC = callbackContext;
206 | cordova.getActivity().runOnUiThread(new Runnable(){
207 | @Override
208 | public void run() {
209 | if (getGameHelper().isSignedIn()) {
210 | _getPlayerImage();
211 | }
212 | else {
213 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
214 | //pr.setKeepCallback(true);
215 | //delayedCC.sendPluginResult(pr);
216 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
217 | //pr.setKeepCallback(true);
218 | delayedCC.sendPluginResult(pr);
219 | }
220 | }
221 | });
222 |
223 | return true;
224 | }
225 | else if (action.equals("getPlayerScore")) {
226 | //Activity activity=cordova.getActivity();
227 | //webView
228 | //
229 | final String leaderboardId = args.getString(0);
230 | Log.d(LOG_TAG, String.format("%s", leaderboardId));
231 |
232 | getPlayerScoreCC = callbackContext;
233 |
234 | final CallbackContext delayedCC = callbackContext;
235 | cordova.getActivity().runOnUiThread(new Runnable(){
236 | @Override
237 | public void run() {
238 | if (getGameHelper().isSignedIn()) {
239 | _getPlayerScore(leaderboardId);
240 | }
241 | else {
242 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
243 | //pr.setKeepCallback(true);
244 | //delayedCC.sendPluginResult(pr);
245 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
246 | //pr.setKeepCallback(true);
247 | delayedCC.sendPluginResult(pr);
248 | }
249 | }
250 | });
251 |
252 | return true;
253 | }
254 | else if (action.equals("submitScore")) {
255 | //Activity activity=cordova.getActivity();
256 | //webView
257 | //
258 | final String leaderboardId = args.getString(0);
259 | Log.d(LOG_TAG, String.format("%s", leaderboardId));
260 | final int score = args.getInt(1);
261 | Log.d(LOG_TAG, String.format("%d", score));
262 |
263 | submitScoreCC = callbackContext;
264 |
265 | final CallbackContext delayedCC = callbackContext;
266 | cordova.getActivity().runOnUiThread(new Runnable(){
267 | @Override
268 | public void run() {
269 | if (getGameHelper().isSignedIn()) {
270 | _submitScore(leaderboardId, score);
271 | }
272 | else {
273 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
274 | //pr.setKeepCallback(true);
275 | //delayedCC.sendPluginResult(pr);
276 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
277 | //pr.setKeepCallback(true);
278 | delayedCC.sendPluginResult(pr);
279 | }
280 | }
281 | });
282 |
283 | return true;
284 | }
285 | else if (action.equals("showLeaderboard")) {
286 | //Activity activity=cordova.getActivity();
287 | //webView
288 | //
289 | final String leaderboardId = args.getString(0);
290 | Log.d(LOG_TAG, String.format("%s", leaderboardId));
291 |
292 | final CallbackContext delayedCC = callbackContext;
293 | cordova.getActivity().runOnUiThread(new Runnable(){
294 | @Override
295 | public void run() {
296 | if (getGameHelper().isSignedIn()) {
297 | _showLeaderboard(leaderboardId);
298 |
299 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
300 | //pr.setKeepCallback(true);
301 | delayedCC.sendPluginResult(pr);
302 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
303 | //pr.setKeepCallback(true);
304 | //delayedCC.sendPluginResult(pr);
305 | }
306 | else {
307 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
308 | //pr.setKeepCallback(true);
309 | //delayedCC.sendPluginResult(pr);
310 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
311 | //pr.setKeepCallback(true);
312 | delayedCC.sendPluginResult(pr);
313 | }
314 | }
315 | });
316 |
317 | return true;
318 | }
319 | else if (action.equals("showLeaderboards")) {
320 | //Activity activity=cordova.getActivity();
321 | //webView
322 | //
323 |
324 | final CallbackContext delayedCC = callbackContext;
325 | cordova.getActivity().runOnUiThread(new Runnable(){
326 | @Override
327 | public void run() {
328 | if (getGameHelper().isSignedIn()) {
329 | _showLeaderboards();
330 |
331 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
332 | //pr.setKeepCallback(true);
333 | delayedCC.sendPluginResult(pr);
334 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
335 | //pr.setKeepCallback(true);
336 | //delayedCC.sendPluginResult(pr);
337 | }
338 | else {
339 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
340 | //pr.setKeepCallback(true);
341 | //delayedCC.sendPluginResult(pr);
342 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
343 | //pr.setKeepCallback(true);
344 | delayedCC.sendPluginResult(pr);
345 | }
346 | }
347 | });
348 |
349 | return true;
350 | }
351 | else if (action.equals("unlockAchievement")) {
352 | //Activity activity=cordova.getActivity();
353 | //webView
354 | //
355 | final String achievementId = args.getString(0);
356 | Log.d(LOG_TAG, String.format("%s", achievementId));
357 |
358 | unlockAchievementCC = callbackContext;
359 |
360 | final CallbackContext delayedCC = callbackContext;
361 | cordova.getActivity().runOnUiThread(new Runnable(){
362 | @Override
363 | public void run() {
364 | if (getGameHelper().isSignedIn()) {
365 | _unlockAchievement(achievementId);
366 | }
367 | else {
368 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
369 | //pr.setKeepCallback(true);
370 | //delayedCC.sendPluginResult(pr);
371 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
372 | //pr.setKeepCallback(true);
373 | delayedCC.sendPluginResult(pr);
374 | }
375 | }
376 | });
377 |
378 | return true;
379 | }
380 | else if (action.equals("incrementAchievement")) {
381 | //Activity activity=cordova.getActivity();
382 | //webView
383 | //
384 | final String achievementId = args.getString(0);
385 | Log.d(LOG_TAG, String.format("%s", achievementId));
386 | final int stepsOrPercent = args.getInt(1);
387 | Log.d(LOG_TAG, String.format("%d", stepsOrPercent));
388 |
389 | incrementAchievementCC = callbackContext;
390 |
391 | final CallbackContext delayedCC = callbackContext;
392 | cordova.getActivity().runOnUiThread(new Runnable(){
393 | @Override
394 | public void run() {
395 | if (getGameHelper().isSignedIn()) {
396 | _incrementAchievement(achievementId, stepsOrPercent);
397 | }
398 | else {
399 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
400 | //pr.setKeepCallback(true);
401 | //delayedCC.sendPluginResult(pr);
402 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
403 | //pr.setKeepCallback(true);
404 | delayedCC.sendPluginResult(pr);
405 | }
406 | }
407 | });
408 |
409 | return true;
410 | }
411 | else if (action.equals("showAchievements")) {
412 | //Activity activity=cordova.getActivity();
413 | //webView
414 | //
415 |
416 | final CallbackContext delayedCC = callbackContext;
417 | cordova.getActivity().runOnUiThread(new Runnable(){
418 | @Override
419 | public void run() {
420 | if (getGameHelper().isSignedIn()) {
421 | _showAchievements();
422 |
423 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
424 | //pr.setKeepCallback(true);
425 | delayedCC.sendPluginResult(pr);
426 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
427 | //pr.setKeepCallback(true);
428 | //delayedCC.sendPluginResult(pr);
429 | }
430 | else {
431 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
432 | //pr.setKeepCallback(true);
433 | //delayedCC.sendPluginResult(pr);
434 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR, "Not logged in");
435 | //pr.setKeepCallback(true);
436 | delayedCC.sendPluginResult(pr);
437 | }
438 | }
439 | });
440 |
441 | return true;
442 | }
443 | else if (action.equals("resetAchievements")) {
444 | //Activity activity=cordova.getActivity();
445 | //webView
446 | //
447 |
448 | final CallbackContext delayedCC = callbackContext;
449 | cordova.getActivity().runOnUiThread(new Runnable(){
450 | @Override
451 | public void run() {
452 | _resetAchievements();
453 |
454 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
455 | //pr.setKeepCallback(true);
456 | delayedCC.sendPluginResult(pr);
457 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
458 | //pr.setKeepCallback(true);
459 | //delayedCC.sendPluginResult(pr);
460 | }
461 | });
462 |
463 | return true;
464 | }
465 |
466 | return false; // Returning false results in a "MethodNotFound" error.
467 | }
468 |
469 | //-------------------------------------
470 | private void _setUp(){
471 | getGameHelper().setup(this);//public void setup(GameHelperListener listener) {
472 |
473 | cordova.setActivityResultCallback(this);
474 | }
475 | private GameHelper getGameHelper(){
476 | if (mHelper == null) {
477 | mHelper = new GameHelper(this.cordova.getActivity(), GameHelper.CLIENT_GAMES);//public GameHelper(Activity activity, int clientsToUse) {
478 | mHelper.enableDebugLog(true);
479 | }
480 | return mHelper;
481 | }
482 | private void _login(){
483 | //getGameHelper().beginUserInitiatedSignIn();
484 | getGameHelper().onStart(this.cordova.getActivity());
485 | }
486 | private void _logout(){
487 | //getGameHelper().signOut();
488 | getGameHelper().onStop();
489 | }
490 |
491 | private void _getPlayerImage() {
492 | Player player = Games.Players.getCurrentPlayer(getGameHelper().getApiClient());
493 | if (player != null)
494 | {
495 | boolean hasH = player.hasHiResImage();
496 | boolean hasI = player.hasIconImage();
497 | Uri playerImageUrl = null;
498 | if (hasH) {
499 | playerImageUrl = player.getHiResImageUri();
500 | }
501 | else if (hasI) {
502 | playerImageUrl = player.getIconImageUri();
503 | }
504 | else {
505 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
506 | //pr.setKeepCallback(true);
507 | //getPlayerImageCC.sendPluginResult(pr);
508 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
509 | //pr.setKeepCallback(true);
510 | getPlayerImageCC.sendPluginResult(pr);
511 |
512 | return;
513 | }
514 |
515 | PluginResult pr = new PluginResult(PluginResult.Status.OK, playerImageUrl.toString());
516 | //pr.setKeepCallback(true);
517 | getPlayerImageCC.sendPluginResult(pr);
518 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
519 | //pr.setKeepCallback(true);
520 | //getPlayerImageCC.sendPluginResult(pr);
521 | }
522 | else {
523 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
524 | //pr.setKeepCallback(true);
525 | //getPlayerImageCC.sendPluginResult(pr);
526 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
527 | //pr.setKeepCallback(true);
528 | getPlayerImageCC.sendPluginResult(pr);
529 | }
530 | }
531 |
532 | private void _getPlayerScore(String leaderboardId){
533 | class ResultCallbackSubmitScoreResult implements ResultCallback {
534 | @Override
535 | public void onResult(Leaderboards.LoadPlayerScoreResult result) {
536 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/Leaderboards.LoadPlayerScoreResult.html
537 | if (result.getStatus().getStatusCode() == GamesStatusCodes.STATUS_OK) {
538 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/LeaderboardScore.html
539 | LeaderboardScore ls = result.getScore();
540 | long score = 0;
541 | if (ls != null)
542 | score = ls.getRawScore();
543 |
544 | PluginResult pr = new PluginResult(PluginResult.Status.OK, score);
545 | //pr.setKeepCallback(true);
546 | getPlayerScoreCC.sendPluginResult(pr);
547 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
548 | //pr.setKeepCallback(true);
549 | //getPlayerScoreCC.sendPluginResult(pr);
550 | }
551 | else {
552 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
553 | //pr.setKeepCallback(true);
554 | //getPlayerScoreCC.sendPluginResult(pr);
555 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
556 | //pr.setKeepCallback(true);
557 | getPlayerScoreCC.sendPluginResult(pr);
558 | }
559 | }
560 | }
561 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/Leaderboards.html
562 | //span: Time span to retrieve data for. Valid values are TIME_SPAN_DAILY, TIME_SPAN_WEEKLY, or TIME_SPAN_ALL_TIME.
563 | //leaderboardCollection: The leaderboard collection to retrieve scores for. Valid values are either COLLECTION_PUBLIC or COLLECTION_SOCIAL.
564 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/Leaderboards.html#loadCurrentPlayerLeaderboardScore(com.google.android.gms.common.api.GoogleApiClient, java.lang.String, int, int)
565 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#TIME_SPAN_DAILY
566 | //http://stackoverflow.com/questions/23248157/how-to-get-score-from-google-play-game-services-leaderboard-of-current-player
567 | Games.Leaderboards.loadCurrentPlayerLeaderboardScore(getGameHelper().getApiClient(), leaderboardId, LeaderboardVariant.TIME_SPAN_ALL_TIME, LeaderboardVariant.COLLECTION_PUBLIC).setResultCallback(new ResultCallbackSubmitScoreResult());
568 | }
569 |
570 | private void _submitScore(String leaderboardId, int score){
571 | /*
572 | //https://developers.google.com/games/services/android/leaderboards
573 | Games.Leaderboards.submitScore(getGameHelper().getApiClient(), leaderboardId, score);
574 | */
575 | ///*
576 | //http://stackoverflow.com/questions/22896713/listener-for-leaderboard-in-google-game-services
577 | //https://developer.android.com/reference/gms-packages.html
578 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/package-summary.html
579 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/Leaderboards.html
580 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/Leaderboards.html#submitScoreImmediate(com.google.android.gms.common.api.GoogleApiClient, java.lang.String, long)
581 |
582 | class ResultCallbackSubmitScoreResult implements ResultCallback {
583 | @Override
584 | public void onResult(Leaderboards.SubmitScoreResult result) {
585 | //https://developer.android.com/reference/com/google/android/gms/games/leaderboard/Leaderboards.SubmitScoreResult.html
586 | if (result.getStatus().getStatusCode() == GamesStatusCodes.STATUS_OK) {
587 | // data sent successfully to server.
588 | // display toast.
589 |
590 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
591 | //pr.setKeepCallback(true);
592 | submitScoreCC.sendPluginResult(pr);
593 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
594 | //pr.setKeepCallback(true);
595 | //submitScoreCC.sendPluginResult(pr);
596 | }
597 | else {
598 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
599 | //pr.setKeepCallback(true);
600 | //submitScoreCC.sendPluginResult(pr);
601 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
602 | //pr.setKeepCallback(true);
603 | submitScoreCC.sendPluginResult(pr);
604 | }
605 | }
606 | }
607 | //Games.Leaderboards.submitScoreImmediate(getGameHelper().getApiClient(), leaderboardId, score).setResultCallback(new ResultCallbackSubmitScoreResult());
608 | try {
609 | Games.Leaderboards.submitScoreImmediate(getGameHelper().getApiClient(), leaderboardId, score).setResultCallback(new ResultCallbackSubmitScoreResult());
610 | }
611 | catch(SecurityException ex) {
612 | Log.d(LOG_TAG, String.format("%s", ex.getMessage()));
613 | }
614 | //*/
615 | }
616 |
617 | private void _showLeaderboard(String leaderboardId){
618 | try {
619 | //show a specific leaderboard
620 | this.cordova.getActivity().startActivityForResult(Games.Leaderboards.getLeaderboardIntent(getGameHelper().getApiClient(), leaderboardId), 0);
621 | }
622 | catch(SecurityException ex) {
623 | Log.d(LOG_TAG, String.format("%s", ex.getMessage()));
624 | }
625 | }
626 |
627 | private void _showLeaderboards(){
628 | try {
629 | //show all leaderboards
630 | this.cordova.getActivity().startActivityForResult(Games.Leaderboards.getAllLeaderboardsIntent(getGameHelper().getApiClient()), 0);
631 | //this.cordova.getActivity().startActivityFor(Games.Leaderboards.getAllLeaderboardsIntent(getGameHelper().getApiClient()));
632 | }
633 | catch(SecurityException ex) {
634 | Log.d(LOG_TAG, String.format("%s", ex.getMessage()));
635 | }
636 | }
637 |
638 | private void _unlockAchievement(String achievementId){
639 | /*
640 | //Unlocking achievements
641 | //To unlock an achievement, call the unlock() method and and pass in the achievement ID.
642 | //Games.Achievements.unlock(getApiClient(), "my_achievement_id");
643 | //If the achievement is of the incremental type (that is, several steps are required to unlock it), call increment() instead.
644 | //Games.Achievements.increment(getApiClient(), "my_incremental_achievment_id", 10000);
645 | //You do not need to write additional code to unlock the achievement; Play Games services automatically unlocks the achievement once it reaches its required number of steps.
646 | //https://developers.google.com/games/services/android/achievements
647 | Games.Achievements.unlock(getGameHelper().getApiClient(), achievementId);
648 | */
649 | ///*
650 | //https://developer.android.com/reference/gms-packages.html
651 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/package-summary.html
652 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/Achievements.html
653 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/Achievements.html#incrementImmediate(com.google.android.gms.common.api.GoogleApiClient, java.lang.String, int)
654 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/Achievements.html#unlockImmediate(com.google.android.gms.common.api.GoogleApiClient, java.lang.String)
655 | class ResultCallbackUpdateAchievementResult implements ResultCallback {
656 | @Override
657 | public void onResult(Achievements.UpdateAchievementResult result) {
658 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/Achievements.UpdateAchievementResult.html
659 | if (result.getStatus().getStatusCode() == GamesStatusCodes.STATUS_OK) {
660 | // data sent successfully to server.
661 | // display toast.
662 | //Log.d(LOG_TAG, String.format("%d", result.getStatus().getStatusCode()));
663 | //Util.alert(cordova.getActivity(), String.format("%d", result.getStatus().getStatusCode()));
664 |
665 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
666 | //pr.setKeepCallback(true);
667 | unlockAchievementCC.sendPluginResult(pr);
668 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
669 | //pr.setKeepCallback(true);
670 | //unlockAchievementCC.sendPluginResult(pr);
671 | }
672 | else{
673 | //Log.d(LOG_TAG, String.format("%d", result.getStatus().getStatusCode()));
674 | //Util.alert(cordova.getActivity(), String.format("%d", result.getStatus().getStatusCode()));
675 |
676 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
677 | //pr.setKeepCallback(true);
678 | //unlockAchievementCC.sendPluginResult(pr);
679 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
680 | //pr.setKeepCallback(true);
681 | unlockAchievementCC.sendPluginResult(pr);
682 | }
683 | }
684 | }
685 | Games.Achievements.unlockImmediate(getGameHelper().getApiClient(), achievementId).setResultCallback(new ResultCallbackUpdateAchievementResult());
686 | //*/
687 | }
688 |
689 | private void _incrementAchievement(String achievementId, int stepsOrPercent){
690 | /*
691 | //Unlocking achievements
692 | //To unlock an achievement, call the unlock() method and and pass in the achievement ID.
693 | //Games.Achievements.unlock(getApiClient(), "my_achievement_id");
694 | //If the achievement is of the incremental type (that is, several steps are required to unlock it), call increment() instead.
695 | //Games.Achievements.increment(getApiClient(), "my_incremental_achievment_id", 1);
696 | //You do not need to write additional code to unlock the achievement; Play Games services automatically unlocks the achievement once it reaches its required number of steps.
697 | //https://developers.google.com/games/services/android/achievements
698 | //Games.Achievements.unlock(getGameHelper().getApiClient(), achievementId);
699 | //
700 | Games.Achievements.increment(getGameHelper().getApiClient(), achievementId, stepsOrPercent);
701 | */
702 | ///*
703 | //https://developer.android.com/reference/gms-packages.html
704 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/package-summary.html
705 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/Achievements.html
706 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/Achievements.html#incrementImmediate(com.google.android.gms.common.api.GoogleApiClient, java.lang.String, int)
707 | class ResultCallbackUpdateAchievementResult implements ResultCallback {
708 | @Override
709 | public void onResult(Achievements.UpdateAchievementResult result) {
710 | //https://developer.android.com/reference/com/google/android/gms/games/achievement/Achievements.UpdateAchievementResult.html
711 | if (result.getStatus().getStatusCode() == GamesStatusCodes.STATUS_OK) {
712 | // data sent successfully to server.
713 | // display toast.
714 | //Log.d(LOG_TAG, String.format("%d", result.getStatus().getStatusCode()));
715 | //Util.alert(cordova.getActivity(), String.format("%d", result.getStatus().getStatusCode()));
716 |
717 | PluginResult pr = new PluginResult(PluginResult.Status.OK);
718 | //pr.setKeepCallback(true);
719 | incrementAchievementCC.sendPluginResult(pr);
720 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
721 | //pr.setKeepCallback(true);
722 | //incrementAchievementCC.sendPluginResult(pr);
723 | }
724 | else{
725 | //Log.d(LOG_TAG, String.format("%d", result.getStatus().getStatusCode()));
726 | //Util.alert(cordova.getActivity(), String.format("%d", result.getStatus().getStatusCode()));
727 |
728 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
729 | //pr.setKeepCallback(true);
730 | //incrementAchievementCC.sendPluginResult(pr);
731 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
732 | //pr.setKeepCallback(true);
733 | incrementAchievementCC.sendPluginResult(pr);
734 | }
735 | }
736 | }
737 | Games.Achievements.incrementImmediate(getGameHelper().getApiClient(), achievementId, stepsOrPercent).setResultCallback(new ResultCallbackUpdateAchievementResult());
738 | //*/
739 | }
740 |
741 | private void _showAchievements(){
742 | this.cordova.getActivity().startActivityForResult(Games.Achievements.getAchievementsIntent(getGameHelper().getApiClient()), 0);
743 | }
744 |
745 | private void _resetAchievements(){///////////////todo
746 |
747 | }
748 |
749 | //GameHelper.GameHelperListener
750 | @Override
751 | public void onSignInSucceeded() {
752 | //Util.alert(cordova.getActivity(), "onSignInSucceeded");
753 |
754 | //https://github.com/freshplanet/ANE-Google-Play-Game-Services/blob/master/android/src/com/freshplanet/googleplaygames/functions/AirGooglePlayGamesGetActivePlayerName.java
755 | //https://developer.android.com/reference/com/google/android/gms/games/Games.html#Players
756 | //https://developer.android.com/reference/com/google/android/gms/games/Players.html#getCurrentPlayer(com.google.android.gms.common.api.GoogleApiClient)
757 | //https://developer.android.com/reference/com/google/android/gms/games/Player.html
758 | Player player = Games.Players.getCurrentPlayer(getGameHelper().getApiClient());
759 | JSONObject playerDetail = new JSONObject();
760 | try {
761 | if (player != null)
762 | {
763 | String playerId = player.getPlayerId();
764 | String displayName = player.getDisplayName();
765 | //String title = player.getTitle();
766 |
767 | playerDetail.put("playerId", playerId);
768 | playerDetail.put("playerDisplayName", displayName);
769 | }
770 | }
771 | catch(JSONException ex){
772 | }
773 |
774 | PluginResult pr = new PluginResult(PluginResult.Status.OK, playerDetail);
775 | //pr.setKeepCallback(true);
776 | loginCC.sendPluginResult(pr);
777 | //PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
778 | //pr.setKeepCallback(true);
779 | //loginCC.sendPluginResult(pr);
780 | }
781 | @Override
782 | public void onSignInFailed() {
783 | //Util.alert(cordova.getActivity(), "onSignInFailed");
784 |
785 | //PluginResult pr = new PluginResult(PluginResult.Status.OK);
786 | //pr.setKeepCallback(true);
787 | //loginCC.sendPluginResult(pr);
788 | PluginResult pr = new PluginResult(PluginResult.Status.ERROR);
789 | //pr.setKeepCallback(true);
790 | loginCC.sendPluginResult(pr);
791 | }
792 |
793 | //CordovaPlugin
794 | @Override
795 | public void onActivityResult(int requestCode, int resultCode, Intent intent) {
796 | getGameHelper().onActivityResult(requestCode, resultCode, intent);
797 | }
798 |
799 | /*
800 | //javascript
801 | getPlayerImage: function (cb) {
802 | var self = this;
803 | //cordova.exec(function (result) {
804 | // var playerImageUrl = result;
805 | // if (self.onGetPlayerImageSucceeded)
806 | // self.onGetPlayerImageSucceeded(playerImageUrl);
807 | //},
808 | //function (error) {
809 | // if (self.onGetPlayerImageFailed)
810 | // self.onGetPlayerImageFailed();
811 | //}, "Game", "getPlayerImage", []);
812 |
813 | this.gapi.client.request({
814 | path : "/games/v1/players/me",
815 | callback : function(result) {
816 | var er = result && !result.error ? success(result) : null;
817 | cb(er, result.error);
818 | }
819 | });
820 | },
821 |
822 | //java
823 | @SuppressWarnings("unused")
824 | public void request(CordovaArgs args, final CallbackContext ctx) throws JSONException {
825 |
826 | JSONObject params = args.getJSONObject(0);
827 | String path = params.getString("path");
828 | JSONObject requestParams = params.optJSONObject("params");
829 | String method = params.optString("method");
830 | if (method == null || method.length() == 0) {
831 | method = "GET";
832 | }
833 | HashMap headers = null;
834 | JSONObject obj = params.optJSONObject("headers");
835 | if (obj != null) {
836 | headers = new HashMap();
837 | Iterator keys = obj.keys();
838 | while (keys.hasNext()) {
839 | String key = keys.next();
840 | headers.put(key, obj.get(key).toString());
841 | }
842 | }
843 |
844 | byte[] body = null;
845 | try
846 | {
847 | JSONObject bodyJSON = params.optJSONObject("body");
848 | if (bodyJSON != null) {
849 | body = bodyJSON.toString().getBytes("utf-8");
850 | }
851 | else {
852 | String bodyString = params.optString("body");
853 | if (bodyString != null && bodyString.length() > 0) {
854 | body = bodyString.getBytes("utf-8");
855 | }
856 | }
857 | }
858 | catch (UnsupportedEncodingException e)
859 | {
860 | e.printStackTrace();
861 | }
862 |
863 | request_inner(path, method, requestParams, body, headers, new GPGService.RequestCallback() {
864 | @Override
865 | public void onComplete(JSONObject responseJSON, GPGService.Error error) {
866 |
867 | JSONObject data = new JSONObject();
868 | try
869 | {
870 | if (responseJSON != null) {
871 | data.put("response", responseJSON);
872 | }
873 | if (error != null) {
874 | data.put("error", new JSONObject(error.toMap()));
875 | }
876 |
877 | }
878 | catch (JSONException ex) {
879 | ex.printStackTrace();
880 | }
881 | ctx.sendPluginResult(new PluginResult(PluginResult.Status.OK, data));
882 | }
883 | });
884 | }
885 |
886 | public void request_inner(String path,final String method, JSONObject params, final byte[] body, final Map headers, final RequestCallback callback) throws JSONException {
887 |
888 | if (!this.client.isConnected()) {
889 |
890 | if (callback != null) {
891 | callback.onComplete(null, new Error("User is not logged into Google Play Game Services", 0));
892 | }
893 | return;
894 | }
895 |
896 |
897 | if (path.startsWith("/")) {
898 | path = "https://www.googleapis.com" + path;
899 | }
900 |
901 | if (params != null) {
902 | String query = "";
903 | Iterator it = params.keys();
904 | while (it.hasNext()) {
905 | if (query.length() == 0)
906 | query+="&";
907 | String key = it.next();
908 | query+= key + "=" + params.get(key).toString();
909 | }
910 | path+= "?" + query;
911 | }
912 |
913 | final String absolutePath = path;
914 |
915 | AsyncTask task = new AsyncTask() {
916 |
917 | @Override
918 | protected Object doInBackground(Void... params) {
919 | HttpURLConnection connection = null;
920 |
921 | try {
922 | URL url = new URL(absolutePath);
923 | connection = (HttpURLConnection) url.openConnection();
924 | connection.setRequestProperty("Authorization", "Bearer " + GPGService.this.authToken);
925 | connection.setRequestMethod(method);
926 |
927 | if (headers != null) {
928 | for (String key : headers.keySet())
929 | {
930 | connection.setRequestProperty(key, headers.get(key));
931 | }
932 | }
933 |
934 | if (body != null)
935 | {
936 | connection.setFixedLengthStreamingMode(body.length);
937 | connection.setDoOutput(true);
938 | if (connection.getRequestProperty("Content-Type") == null)
939 | connection.setRequestProperty("Content-Type", "text/plain;charset=UTF-8");
940 |
941 | connection.setRequestProperty("Content-Length", Integer.toString(body.length));
942 | OutputStream output = null;
943 | try
944 | {
945 | output = connection.getOutputStream();
946 | output.write(body);
947 | }
948 | finally
949 | {
950 | if (output != null) {
951 | output.close();
952 | }
953 | }
954 | }
955 |
956 | int statusCode = connection.getResponseCode();
957 | InputStream inputStream;
958 | if (statusCode >= 200 && statusCode < 300) {
959 | inputStream = connection.getInputStream();
960 | } else {
961 | inputStream = connection.getErrorStream();
962 | }
963 |
964 | String content = convertStreamToString(inputStream);
965 |
966 | JSONObject result = new JSONObject(content);
967 | return result;
968 | }
969 | catch (Exception e) {
970 | return new Error(e.getLocalizedMessage(), 0);
971 | }
972 | finally {
973 | if (connection != null) {
974 | connection.disconnect();
975 | }
976 | }
977 | }
978 |
979 | @Override
980 | protected void onPostExecute(Object info) {
981 | if (callback == null) {
982 | return;
983 | }
984 | if (info == null) {
985 | callback.onComplete(null, null);
986 | }
987 | else if (info instanceof Error) {
988 | callback.onComplete(null, (Error)info);
989 | }
990 | else {
991 | callback.onComplete((JSONObject)info, null);
992 | }
993 | }
994 |
995 | };
996 |
997 | if (this.executor != null) {
998 | task.executeOnExecutor(executor);
999 | }
1000 | else {
1001 | task.execute();
1002 | }
1003 | }
1004 |
1005 | static String convertStreamToString(java.io.InputStream is) {
1006 | java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
1007 | return s.hasNext() ? s.next() : "";
1008 | }
1009 | */
1010 | }
--------------------------------------------------------------------------------
/src/android/GameHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Google Inc.
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 com.google.example.games.basegameutils;
18 |
19 | import java.util.ArrayList;
20 |
21 | import android.app.Activity;
22 | import android.app.AlertDialog;
23 | import android.app.Dialog;
24 | import android.content.Context;
25 | import android.content.Intent;
26 | import android.content.IntentSender.SendIntentException;
27 | import android.content.SharedPreferences;
28 | import android.os.Bundle;
29 | import android.os.Handler;
30 | import android.util.Log;
31 |
32 | //import com.google.android.gms.appstate.AppStateManager;//deprecated
33 | import com.google.android.gms.common.ConnectionResult;
34 | //import com.google.android.gms.common.GooglePlayServicesUtil;//deprecated
35 | import com.google.android.gms.common.GoogleApiAvailability;//
36 | import com.google.android.gms.common.api.Api.ApiOptions.NoOptions;
37 | import com.google.android.gms.common.api.GoogleApiClient;
38 | import com.google.android.gms.drive.Drive;
39 | import com.google.android.gms.games.Games;
40 | import com.google.android.gms.games.Games.GamesOptions;
41 | import com.google.android.gms.games.GamesActivityResultCodes;
42 | import com.google.android.gms.games.multiplayer.Invitation;
43 | import com.google.android.gms.games.multiplayer.Multiplayer;
44 | import com.google.android.gms.games.multiplayer.turnbased.TurnBasedMatch;
45 | import com.google.android.gms.games.request.GameRequest;
46 | import com.google.android.gms.plus.Plus;
47 | import com.google.android.gms.plus.Plus.PlusOptions;
48 |
49 | public class GameHelper implements GoogleApiClient.ConnectionCallbacks,
50 | GoogleApiClient.OnConnectionFailedListener {
51 |
52 | static final String TAG = "GameHelper";
53 | //
54 | private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000 ;//
55 |
56 | /** Listener for sign-in success or failure events. */
57 | public interface GameHelperListener {
58 | /**
59 | * Called when sign-in fails. As a result, a "Sign-In" button can be
60 | * shown to the user; when that button is clicked, call
61 | *
62 | * @link{GamesHelper#beginUserInitiatedSignIn . Note that not all calls
63 | * to this method mean an
64 | * error; it may be a result
65 | * of the fact that automatic
66 | * sign-in could not proceed
67 | * because user interaction
68 | * was required (consent
69 | * dialogs). So
70 | * implementations of this
71 | * method should NOT display
72 | * an error message unless a
73 | * call to @link{GamesHelper#
74 | * hasSignInError} indicates
75 | * that an error indeed
76 | * occurred.
77 | */
78 | void onSignInFailed();
79 |
80 | /** Called when sign-in succeeds. */
81 | void onSignInSucceeded();
82 | }
83 |
84 | // configuration done?
85 | private boolean mSetupDone = false;
86 |
87 | // are we currently connecting?
88 | private boolean mConnecting = false;
89 |
90 | // Are we expecting the result of a resolution flow?
91 | boolean mExpectingResolution = false;
92 |
93 | // was the sign-in flow cancelled when we tried it?
94 | // if true, we know not to try again automatically.
95 | boolean mSignInCancelled = false;
96 |
97 | /**
98 | * The Activity we are bound to. We need to keep a reference to the Activity
99 | * because some games methods require an Activity (a Context won't do). We
100 | * are careful not to leak these references: we release them on onStop().
101 | */
102 | Activity mActivity = null;
103 |
104 | // app context
105 | Context mAppContext = null;
106 |
107 | // Request code we use when invoking other Activities to complete the
108 | // sign-in flow.
109 | final static int RC_RESOLVE = 9001;
110 |
111 | // Request code when invoking Activities whose result we don't care about.
112 | final static int RC_UNUSED = 9002;
113 |
114 | // the Google API client builder we will use to create GoogleApiClient
115 | GoogleApiClient.Builder mGoogleApiClientBuilder = null;
116 |
117 | // Api options to use when adding each API, null for none
118 | GamesOptions mGamesApiOptions = GamesOptions.builder().build();
119 | PlusOptions mPlusApiOptions = null;
120 | NoOptions mAppStateApiOptions = null;
121 |
122 | // Google API client object we manage.
123 | GoogleApiClient mGoogleApiClient = null;
124 |
125 | // Client request flags
126 | public final static int CLIENT_NONE = 0x00;
127 | public final static int CLIENT_GAMES = 0x01;
128 | public final static int CLIENT_PLUS = 0x02;
129 | public final static int CLIENT_APPSTATE = 0x04;
130 | public final static int CLIENT_SNAPSHOT = 0x08;
131 | public final static int CLIENT_ALL = CLIENT_GAMES | CLIENT_PLUS
132 | | CLIENT_APPSTATE | CLIENT_SNAPSHOT;
133 |
134 | // What clients were requested? (bit flags)
135 | int mRequestedClients = CLIENT_NONE;
136 |
137 | // Whether to automatically try to sign in on onStart(). We only set this
138 | // to true when the sign-in process fails or the user explicitly signs out.
139 | // We set it back to false when the user initiates the sign in process.
140 | boolean mConnectOnStart = true;
141 |
142 | /*
143 | * Whether user has specifically requested that the sign-in process begin.
144 | * If mUserInitiatedSignIn is false, we're in the automatic sign-in attempt
145 | * that we try once the Activity is started -- if true, then the user has
146 | * already clicked a "Sign-In" button or something similar
147 | */
148 | boolean mUserInitiatedSignIn = false;
149 |
150 | // The connection result we got from our last attempt to sign-in.
151 | ConnectionResult mConnectionResult = null;
152 |
153 | // The error that happened during sign-in.
154 | SignInFailureReason mSignInFailureReason = null;
155 |
156 | // Should we show error dialog boxes?
157 | boolean mShowErrorDialogs = true;
158 |
159 | // Print debug logs?
160 | boolean mDebugLog = false;
161 |
162 | Handler mHandler;
163 |
164 | /*
165 | * If we got an invitation when we connected to the games client, it's here.
166 | * Otherwise, it's null.
167 | */
168 | Invitation mInvitation;
169 |
170 | /*
171 | * If we got turn-based match when we connected to the games client, it's
172 | * here. Otherwise, it's null.
173 | */
174 | TurnBasedMatch mTurnBasedMatch;
175 |
176 | /*
177 | * If we have incoming requests when we connected to the games client, they
178 | * are here. Otherwise, it's null.
179 | */
180 | ArrayList mRequests;
181 |
182 | // Listener
183 | GameHelperListener mListener = null;
184 |
185 | // Should we start the flow to sign the user in automatically on startup? If
186 | // so, up to
187 | // how many times in the life of the application?
188 | static final int DEFAULT_MAX_SIGN_IN_ATTEMPTS = 3;
189 | int mMaxAutoSignInAttempts = DEFAULT_MAX_SIGN_IN_ATTEMPTS;
190 |
191 | /**
192 | * Construct a GameHelper object, initially tied to the given Activity.
193 | * After constructing this object, call @link{setup} from the onCreate()
194 | * method of your Activity.
195 | *
196 | * @param clientsToUse
197 | * the API clients to use (a combination of the CLIENT_* flags,
198 | * or CLIENT_ALL to mean all clients).
199 | */
200 | public GameHelper(Activity activity, int clientsToUse) {
201 | mActivity = activity;
202 | mAppContext = activity.getApplicationContext();
203 | mRequestedClients = clientsToUse;
204 | mHandler = new Handler();
205 | }
206 |
207 | /**
208 | * Sets the maximum number of automatic sign-in attempts to be made on
209 | * application startup. This maximum is over the lifetime of the application
210 | * (it is stored in a SharedPreferences file). So, for example, if you
211 | * specify 2, then it means that the user will be prompted to sign in on app
212 | * startup the first time and, if they cancel, a second time the next time
213 | * the app starts, and, if they cancel that one, never again. Set to 0 if
214 | * you do not want the user to be prompted to sign in on application
215 | * startup.
216 | */
217 | public void setMaxAutoSignInAttempts(int max) {
218 | mMaxAutoSignInAttempts = max;
219 | }
220 |
221 | void assertConfigured(String operation) {
222 | if (!mSetupDone) {
223 | String error = "GameHelper error: Operation attempted without setup: "
224 | + operation
225 | + ". The setup() method must be called before attempting any other operation.";
226 | logError(error);
227 | throw new IllegalStateException(error);
228 | }
229 | }
230 |
231 | private void doApiOptionsPreCheck() {
232 | if (mGoogleApiClientBuilder != null) {
233 | String error = "GameHelper: you cannot call set*ApiOptions after the client "
234 | + "builder has been created. Call it before calling createApiClientBuilder() "
235 | + "or setup().";
236 | logError(error);
237 | throw new IllegalStateException(error);
238 | }
239 | }
240 |
241 | /**
242 | * Sets the options to pass when setting up the Games API. Call before
243 | * setup().
244 | */
245 | public void setGamesApiOptions(GamesOptions options) {
246 | doApiOptionsPreCheck();
247 | mGamesApiOptions = options;
248 | }
249 |
250 | /**
251 | * Sets the options to pass when setting up the AppState API. Call before
252 | * setup().
253 | */
254 | public void setAppStateApiOptions(NoOptions options) {
255 | doApiOptionsPreCheck();
256 | mAppStateApiOptions = options;
257 | }
258 |
259 | /**
260 | * Sets the options to pass when setting up the Plus API. Call before
261 | * setup().
262 | */
263 | public void setPlusApiOptions(PlusOptions options) {
264 | doApiOptionsPreCheck();
265 | mPlusApiOptions = options;
266 | }
267 |
268 | /**
269 | * Creates a GoogleApiClient.Builder for use with @link{#setup}. Normally,
270 | * you do not have to do this; use this method only if you need to make
271 | * nonstandard setup (e.g. adding extra scopes for other APIs) on the
272 | * GoogleApiClient.Builder before calling @link{#setup}.
273 | */
274 | public GoogleApiClient.Builder createApiClientBuilder() {
275 | if (mSetupDone) {
276 | String error = "GameHelper: you called GameHelper.createApiClientBuilder() after "
277 | + "calling setup. You can only get a client builder BEFORE performing setup.";
278 | logError(error);
279 | throw new IllegalStateException(error);
280 | }
281 |
282 | GoogleApiClient.Builder builder = new GoogleApiClient.Builder(
283 | mActivity, this, this);
284 |
285 | if (0 != (mRequestedClients & CLIENT_GAMES)) {
286 | builder.addApi(Games.API, mGamesApiOptions);
287 | builder.addScope(Games.SCOPE_GAMES);
288 | }
289 |
290 | if (0 != (mRequestedClients & CLIENT_PLUS)) {
291 | builder.addApi(Plus.API);
292 | builder.addScope(Plus.SCOPE_PLUS_LOGIN);
293 | }
294 |
295 | //if (0 != (mRequestedClients & CLIENT_APPSTATE)) {//deprecated
296 | // builder.addApi(AppStateManager.API);
297 | // builder.addScope(AppStateManager.SCOPE_APP_STATE);
298 | //}
299 |
300 | if (0 != (mRequestedClients & CLIENT_SNAPSHOT)) {
301 | builder.addScope(Drive.SCOPE_APPFOLDER);
302 | builder.addApi(Drive.API);
303 | }
304 |
305 | mGoogleApiClientBuilder = builder;
306 | return builder;
307 | }
308 |
309 | /**
310 | * Performs setup on this GameHelper object. Call this from the onCreate()
311 | * method of your Activity. This will create the clients and do a few other
312 | * initialization tasks. Next, call @link{#onStart} from the onStart()
313 | * method of your Activity.
314 | *
315 | * @param listener
316 | * The listener to be notified of sign-in events.
317 | */
318 | public void setup(GameHelperListener listener) {
319 | if (mSetupDone) {
320 | String error = "GameHelper: you cannot call GameHelper.setup() more than once!";
321 | logError(error);
322 | throw new IllegalStateException(error);
323 | }
324 | mListener = listener;
325 | debugLog("Setup: requested clients: " + mRequestedClients);
326 |
327 | if (mGoogleApiClientBuilder == null) {
328 | // we don't have a builder yet, so create one
329 | createApiClientBuilder();
330 | }
331 |
332 | mGoogleApiClient = mGoogleApiClientBuilder.build();
333 | mGoogleApiClientBuilder = null;
334 | mSetupDone = true;
335 | }
336 |
337 | /**
338 | * Returns the GoogleApiClient object. In order to call this method, you
339 | * must have called @link{setup}.
340 | */
341 | public GoogleApiClient getApiClient() {
342 | if (mGoogleApiClient == null) {
343 | throw new IllegalStateException(
344 | "No GoogleApiClient. Did you call setup()?");
345 | }
346 | return mGoogleApiClient;
347 | }
348 |
349 | /** Returns whether or not the user is signed in. */
350 | public boolean isSignedIn() {
351 | return mGoogleApiClient != null && mGoogleApiClient.isConnected();
352 | }
353 |
354 | /** Returns whether or not we are currently connecting */
355 | public boolean isConnecting() {
356 | return mConnecting;
357 | }
358 |
359 | /**
360 | * Returns whether or not there was a (non-recoverable) error during the
361 | * sign-in process.
362 | */
363 | public boolean hasSignInError() {
364 | return mSignInFailureReason != null;
365 | }
366 |
367 | /**
368 | * Returns the error that happened during the sign-in process, null if no
369 | * error occurred.
370 | */
371 | public SignInFailureReason getSignInError() {
372 | return mSignInFailureReason;
373 | }
374 |
375 | // Set whether to show error dialogs or not.
376 | public void setShowErrorDialogs(boolean show) {
377 | mShowErrorDialogs = show;
378 | }
379 |
380 | /** Call this method from your Activity's onStart(). */
381 | public void onStart(Activity act) {
382 | mActivity = act;
383 | mAppContext = act.getApplicationContext();
384 |
385 | debugLog("onStart");
386 | assertConfigured("onStart");
387 |
388 | if (mConnectOnStart) {
389 | if (mGoogleApiClient.isConnected()) {
390 | Log.w(TAG,
391 | "GameHelper: client was already connected on onStart()");
392 | } else {
393 | debugLog("Connecting client.");
394 | mConnecting = true;
395 | mGoogleApiClient.connect();
396 | }
397 | } else {
398 | debugLog("Not attempting to connect becase mConnectOnStart=false");
399 | debugLog("Instead, reporting a sign-in failure.");
400 | mHandler.postDelayed(new Runnable() {
401 | @Override
402 | public void run() {
403 | notifyListener(false);
404 | }
405 | }, 1000);
406 | }
407 | }
408 |
409 | /** Call this method from your Activity's onStop(). */
410 | public void onStop() {
411 | debugLog("onStop");
412 | assertConfigured("onStop");
413 | if (mGoogleApiClient.isConnected()) {
414 | debugLog("Disconnecting client due to onStop");
415 | mGoogleApiClient.disconnect();
416 | } else {
417 | debugLog("Client already disconnected when we got onStop.");
418 | }
419 | mConnecting = false;
420 | mExpectingResolution = false;
421 |
422 | // let go of the Activity reference
423 | mActivity = null;
424 | }
425 |
426 | /**
427 | * Returns the invitation ID received through an invitation notification.
428 | * This should be called from your GameHelperListener's
429 | *
430 | * @link{GameHelperListener#onSignInSucceeded method, to check if there's an
431 | * invitation available. In that
432 | * case, accept the invitation.
433 | * @return The id of the invitation, or null if none was received.
434 | */
435 | public String getInvitationId() {
436 | if (!mGoogleApiClient.isConnected()) {
437 | Log.w(TAG,
438 | "Warning: getInvitationId() should only be called when signed in, "
439 | + "that is, after getting onSignInSuceeded()");
440 | }
441 | return mInvitation == null ? null : mInvitation.getInvitationId();
442 | }
443 |
444 | /**
445 | * Returns the invitation received through an invitation notification. This
446 | * should be called from your GameHelperListener's
447 | *
448 | * @link{GameHelperListener#onSignInSucceeded method, to check if there's an
449 | * invitation available. In that
450 | * case, accept the invitation.
451 | * @return The invitation, or null if none was received.
452 | */
453 | public Invitation getInvitation() {
454 | if (!mGoogleApiClient.isConnected()) {
455 | Log.w(TAG,
456 | "Warning: getInvitation() should only be called when signed in, "
457 | + "that is, after getting onSignInSuceeded()");
458 | }
459 | return mInvitation;
460 | }
461 |
462 | public boolean hasInvitation() {
463 | return mInvitation != null;
464 | }
465 |
466 | public boolean hasTurnBasedMatch() {
467 | return mTurnBasedMatch != null;
468 | }
469 |
470 | public boolean hasRequests() {
471 | return mRequests != null;
472 | }
473 |
474 | public void clearInvitation() {
475 | mInvitation = null;
476 | }
477 |
478 | public void clearTurnBasedMatch() {
479 | mTurnBasedMatch = null;
480 | }
481 |
482 | public void clearRequests() {
483 | mRequests = null;
484 | }
485 |
486 | /**
487 | * Returns the tbmp match received through an invitation notification. This
488 | * should be called from your GameHelperListener's
489 | *
490 | * @link{GameHelperListener#onSignInSucceeded method, to check if there's a
491 | * match available.
492 | * @return The match, or null if none was received.
493 | */
494 | public TurnBasedMatch getTurnBasedMatch() {
495 | if (!mGoogleApiClient.isConnected()) {
496 | Log.w(TAG,
497 | "Warning: getTurnBasedMatch() should only be called when signed in, "
498 | + "that is, after getting onSignInSuceeded()");
499 | }
500 | return mTurnBasedMatch;
501 | }
502 |
503 | /**
504 | * Returns the requests received through the onConnected bundle. This should
505 | * be called from your GameHelperListener's
506 | *
507 | * @link{GameHelperListener#onSignInSucceeded method, to check if there are
508 | * incoming requests that must be
509 | * handled.
510 | * @return The requests, or null if none were received.
511 | */
512 | public ArrayList getRequests() {
513 | if (!mGoogleApiClient.isConnected()) {
514 | Log.w(TAG, "Warning: getRequests() should only be called "
515 | + "when signed in, "
516 | + "that is, after getting onSignInSuceeded()");
517 | }
518 | return mRequests;
519 | }
520 |
521 | /** Enables debug logging */
522 | public void enableDebugLog(boolean enabled) {
523 | mDebugLog = enabled;
524 | if (enabled) {
525 | debugLog("Debug log enabled.");
526 | }
527 | }
528 |
529 | @Deprecated
530 | public void enableDebugLog(boolean enabled, String tag) {
531 | Log.w(TAG, "GameHelper.enableDebugLog(boolean,String) is deprecated. "
532 | + "Use GameHelper.enableDebugLog(boolean)");
533 | enableDebugLog(enabled);
534 | }
535 |
536 | /** Sign out and disconnect from the APIs. */
537 | public void signOut() {
538 | if (!mGoogleApiClient.isConnected()) {
539 | // nothing to do
540 | debugLog("signOut: was already disconnected, ignoring.");
541 | return;
542 | }
543 |
544 | // for Plus, "signing out" means clearing the default account and
545 | // then disconnecting
546 | if (0 != (mRequestedClients & CLIENT_PLUS)) {
547 | debugLog("Clearing default account on PlusClient.");
548 | Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
549 | }
550 |
551 | // For the games client, signing out means calling signOut and
552 | // disconnecting
553 | if (0 != (mRequestedClients & CLIENT_GAMES)) {
554 | debugLog("Signing out from the Google API Client.");
555 | Games.signOut(mGoogleApiClient);
556 | }
557 |
558 | // Ready to disconnect
559 | debugLog("Disconnecting client.");
560 | mConnectOnStart = false;
561 | mConnecting = false;
562 | mGoogleApiClient.disconnect();
563 | }
564 |
565 | /**
566 | * Handle activity result. Call this method from your Activity's
567 | * onActivityResult callback. If the activity result pertains to the sign-in
568 | * process, processes it appropriately.
569 | */
570 | public void onActivityResult(int requestCode, int responseCode,
571 | Intent intent) {
572 | debugLog("onActivityResult: req="
573 | + (requestCode == RC_RESOLVE ? "RC_RESOLVE" : String
574 | .valueOf(requestCode)) + ", resp="
575 | + GameHelperUtils.activityResponseCodeToString(responseCode));
576 | if (requestCode != RC_RESOLVE) {
577 | debugLog("onActivityResult: request code not meant for us. Ignoring.");
578 | return;
579 | }
580 |
581 | // no longer expecting a resolution
582 | mExpectingResolution = false;
583 |
584 | if (!mConnecting) {
585 | debugLog("onActivityResult: ignoring because we are not connecting.");
586 | return;
587 | }
588 |
589 | // We're coming back from an activity that was launched to resolve a
590 | // connection problem. For example, the sign-in UI.
591 | if (responseCode == Activity.RESULT_OK) {
592 | // Ready to try to connect again.
593 | debugLog("onAR: Resolution was RESULT_OK, so connecting current client again.");
594 | connect();
595 | } else if (responseCode == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED) {
596 | debugLog("onAR: Resolution was RECONNECT_REQUIRED, so reconnecting.");
597 | connect();
598 | } else if (responseCode == Activity.RESULT_CANCELED) {
599 | // User cancelled.
600 | debugLog("onAR: Got a cancellation result, so disconnecting.");
601 | mSignInCancelled = true;
602 | mConnectOnStart = false;
603 | mUserInitiatedSignIn = false;
604 | mSignInFailureReason = null; // cancelling is not a failure!
605 | mConnecting = false;
606 | mGoogleApiClient.disconnect();
607 |
608 | // increment # of cancellations
609 | int prevCancellations = getSignInCancellations();
610 | int newCancellations = incrementSignInCancellations();
611 | debugLog("onAR: # of cancellations " + prevCancellations + " --> "
612 | + newCancellations + ", max " + mMaxAutoSignInAttempts);
613 |
614 | notifyListener(false);
615 | } else {
616 | // Whatever the problem we were trying to solve, it was not
617 | // solved. So give up and show an error message.
618 | debugLog("onAR: responseCode="
619 | + GameHelperUtils
620 | .activityResponseCodeToString(responseCode)
621 | + ", so giving up.");
622 | giveUp(new SignInFailureReason(mConnectionResult.getErrorCode(),
623 | responseCode));
624 | }
625 | }
626 |
627 | void notifyListener(boolean success) {
628 | debugLog("Notifying LISTENER of sign-in "
629 | + (success ? "SUCCESS"
630 | : mSignInFailureReason != null ? "FAILURE (error)"
631 | : "FAILURE (no error)"));
632 | if (mListener != null) {
633 | if (success) {
634 | mListener.onSignInSucceeded();
635 | } else {
636 | mListener.onSignInFailed();
637 | }
638 | }
639 | }
640 |
641 | /**
642 | * Starts a user-initiated sign-in flow. This should be called when the user
643 | * clicks on a "Sign In" button. As a result, authentication/consent dialogs
644 | * may show up. At the end of the process, the GameHelperListener's
645 | * onSignInSucceeded() or onSignInFailed() methods will be called.
646 | */
647 | public void beginUserInitiatedSignIn() {
648 | debugLog("beginUserInitiatedSignIn: resetting attempt count.");
649 | resetSignInCancellations();
650 | mSignInCancelled = false;
651 | mConnectOnStart = true;
652 |
653 | if (mGoogleApiClient.isConnected()) {
654 | // nothing to do
655 | logWarn("beginUserInitiatedSignIn() called when already connected. "
656 | + "Calling listener directly to notify of success.");
657 | notifyListener(true);
658 | return;
659 | } else if (mConnecting) {
660 | logWarn("beginUserInitiatedSignIn() called when already connecting. "
661 | + "Be patient! You can only call this method after you get an "
662 | + "onSignInSucceeded() or onSignInFailed() callback. Suggestion: disable "
663 | + "the sign-in button on startup and also when it's clicked, and re-enable "
664 | + "when you get the callback.");
665 | // ignore call (listener will get a callback when the connection
666 | // process finishes)
667 | return;
668 | }
669 |
670 | debugLog("Starting USER-INITIATED sign-in flow.");
671 |
672 | // indicate that user is actively trying to sign in (so we know to
673 | // resolve
674 | // connection problems by showing dialogs)
675 | mUserInitiatedSignIn = true;
676 |
677 | if (mConnectionResult != null) {
678 | // We have a pending connection result from a previous failure, so
679 | // start with that.
680 | debugLog("beginUserInitiatedSignIn: continuing pending sign-in flow.");
681 | mConnecting = true;
682 | resolveConnectionResult();
683 | } else {
684 | // We don't have a pending connection result, so start anew.
685 | debugLog("beginUserInitiatedSignIn: starting new sign-in flow.");
686 | mConnecting = true;
687 | connect();
688 | }
689 | }
690 |
691 | void connect() {
692 | if (mGoogleApiClient.isConnected()) {
693 | debugLog("Already connected.");
694 | return;
695 | }
696 | debugLog("Starting connection.");
697 | mConnecting = true;
698 | mInvitation = null;
699 | mTurnBasedMatch = null;
700 | mGoogleApiClient.connect();
701 | }
702 |
703 | /**
704 | * Disconnects the API client, then connects again.
705 | */
706 | public void reconnectClient() {
707 | if (!mGoogleApiClient.isConnected()) {
708 | Log.w(TAG, "reconnectClient() called when client is not connected.");
709 | // interpret it as a request to connect
710 | connect();
711 | } else {
712 | debugLog("Reconnecting client.");
713 | mGoogleApiClient.reconnect();
714 | }
715 | }
716 |
717 | /** Called when we successfully obtain a connection to a client. */
718 | @Override
719 | public void onConnected(Bundle connectionHint) {
720 | debugLog("onConnected: connected!");
721 |
722 | if (connectionHint != null) {
723 | debugLog("onConnected: connection hint provided. Checking for invite.");
724 | Invitation inv = connectionHint
725 | .getParcelable(Multiplayer.EXTRA_INVITATION);
726 | if (inv != null && inv.getInvitationId() != null) {
727 | // retrieve and cache the invitation ID
728 | debugLog("onConnected: connection hint has a room invite!");
729 | mInvitation = inv;
730 | debugLog("Invitation ID: " + mInvitation.getInvitationId());
731 | }
732 |
733 | // Do we have any requests pending?
734 | mRequests = Games.Requests
735 | .getGameRequestsFromBundle(connectionHint);
736 | if (!mRequests.isEmpty()) {
737 | // We have requests in onConnected's connectionHint.
738 | debugLog("onConnected: connection hint has " + mRequests.size()
739 | + " request(s)");
740 | }
741 |
742 | debugLog("onConnected: connection hint provided. Checking for TBMP game.");
743 | mTurnBasedMatch = connectionHint
744 | .getParcelable(Multiplayer.EXTRA_TURN_BASED_MATCH);
745 | }
746 |
747 | // we're good to go
748 | succeedSignIn();
749 | }
750 |
751 | void succeedSignIn() {
752 | debugLog("succeedSignIn");
753 | mSignInFailureReason = null;
754 | mConnectOnStart = true;
755 | mUserInitiatedSignIn = false;
756 | mConnecting = false;
757 | notifyListener(true);
758 | }
759 |
760 | private final String GAMEHELPER_SHARED_PREFS = "GAMEHELPER_SHARED_PREFS";
761 | private final String KEY_SIGN_IN_CANCELLATIONS = "KEY_SIGN_IN_CANCELLATIONS";
762 |
763 | // Return the number of times the user has cancelled the sign-in flow in the
764 | // life of the app
765 | int getSignInCancellations() {
766 | SharedPreferences sp = mAppContext.getSharedPreferences(
767 | GAMEHELPER_SHARED_PREFS, Context.MODE_PRIVATE);
768 | return sp.getInt(KEY_SIGN_IN_CANCELLATIONS, 0);
769 | }
770 |
771 | // Increments the counter that indicates how many times the user has
772 | // cancelled the sign in
773 | // flow in the life of the application
774 | int incrementSignInCancellations() {
775 | int cancellations = getSignInCancellations();
776 | SharedPreferences.Editor editor = mAppContext.getSharedPreferences(
777 | GAMEHELPER_SHARED_PREFS, Context.MODE_PRIVATE).edit();
778 | editor.putInt(KEY_SIGN_IN_CANCELLATIONS, cancellations + 1);
779 | editor.commit();
780 | return cancellations + 1;
781 | }
782 |
783 | // Reset the counter of how many times the user has cancelled the sign-in
784 | // flow.
785 | void resetSignInCancellations() {
786 | SharedPreferences.Editor editor = mAppContext.getSharedPreferences(
787 | GAMEHELPER_SHARED_PREFS, Context.MODE_PRIVATE).edit();
788 | editor.putInt(KEY_SIGN_IN_CANCELLATIONS, 0);
789 | editor.commit();
790 | }
791 |
792 | /** Handles a connection failure. */
793 | @Override
794 | public void onConnectionFailed(ConnectionResult result) {
795 | // save connection result for later reference
796 | debugLog("onConnectionFailed");
797 |
798 | mConnectionResult = result;
799 | debugLog("Connection failure:");
800 | debugLog(" - code: "
801 | + GameHelperUtils.errorCodeToString(mConnectionResult
802 | .getErrorCode()));
803 | debugLog(" - resolvable: " + mConnectionResult.hasResolution());
804 | debugLog(" - details: " + mConnectionResult.toString());
805 |
806 | int cancellations = getSignInCancellations();
807 | boolean shouldResolve = false;
808 |
809 | if (mUserInitiatedSignIn) {
810 | debugLog("onConnectionFailed: WILL resolve because user initiated sign-in.");
811 | shouldResolve = true;
812 | } else if (mSignInCancelled) {
813 | debugLog("onConnectionFailed WILL NOT resolve (user already cancelled once).");
814 | shouldResolve = false;
815 | } else if (cancellations < mMaxAutoSignInAttempts) {
816 | debugLog("onConnectionFailed: WILL resolve because we have below the max# of "
817 | + "attempts, "
818 | + cancellations
819 | + " < "
820 | + mMaxAutoSignInAttempts);
821 | shouldResolve = true;
822 | } else {
823 | shouldResolve = false;
824 | debugLog("onConnectionFailed: Will NOT resolve; not user-initiated and max attempts "
825 | + "reached: "
826 | + cancellations
827 | + " >= "
828 | + mMaxAutoSignInAttempts);
829 | }
830 |
831 | if (!shouldResolve) {
832 | // Fail and wait for the user to want to sign in.
833 | debugLog("onConnectionFailed: since we won't resolve, failing now.");
834 | mConnectionResult = result;
835 | mConnecting = false;
836 | notifyListener(false);
837 | return;
838 | }
839 |
840 | debugLog("onConnectionFailed: resolving problem...");
841 |
842 | // Resolve the connection result. This usually means showing a dialog or
843 | // starting an Activity that will allow the user to give the appropriate
844 | // consents so that sign-in can be successful.
845 | resolveConnectionResult();
846 | }
847 |
848 | /**
849 | * Attempts to resolve a connection failure. This will usually involve
850 | * starting a UI flow that lets the user give the appropriate consents
851 | * necessary for sign-in to work.
852 | */
853 | void resolveConnectionResult() {
854 | // Try to resolve the problem
855 | if (mExpectingResolution) {
856 | debugLog("We're already expecting the result of a previous resolution.");
857 | return;
858 | }
859 |
860 | debugLog("resolveConnectionResult: trying to resolve result: "
861 | + mConnectionResult);
862 | if (mConnectionResult.hasResolution()) {
863 | // This problem can be fixed. So let's try to fix it.
864 | debugLog("Result has resolution. Starting it.");
865 | try {
866 | // launch appropriate UI flow (which might, for example, be the
867 | // sign-in flow)
868 | mExpectingResolution = true;
869 | mConnectionResult.startResolutionForResult(mActivity,
870 | RC_RESOLVE);
871 | } catch (SendIntentException e) {
872 | // Try connecting again
873 | debugLog("SendIntentException, so connecting again.");
874 | connect();
875 | }
876 | } else {
877 | // It's not a problem what we can solve, so give up and show an
878 | // error.
879 | debugLog("resolveConnectionResult: result has no resolution. Giving up.");
880 | giveUp(new SignInFailureReason(mConnectionResult.getErrorCode()));
881 | }
882 | }
883 |
884 | public void disconnect() {
885 | if (mGoogleApiClient.isConnected()) {
886 | debugLog("Disconnecting client.");
887 | mGoogleApiClient.disconnect();
888 | } else {
889 | Log.w(TAG,
890 | "disconnect() called when client was already disconnected.");
891 | }
892 | }
893 |
894 | /**
895 | * Give up on signing in due to an error. Shows the appropriate error
896 | * message to the user, using a standard error dialog as appropriate to the
897 | * cause of the error. That dialog will indicate to the user how the problem
898 | * can be solved (for example, re-enable Google Play Services, upgrade to a
899 | * new version, etc).
900 | */
901 | void giveUp(SignInFailureReason reason) {
902 | mConnectOnStart = false;
903 | disconnect();
904 | mSignInFailureReason = reason;
905 |
906 | if (reason.mActivityResultCode == GamesActivityResultCodes.RESULT_APP_MISCONFIGURED) {
907 | // print debug info for the developer
908 | GameHelperUtils.printMisconfiguredDebugInfo(mAppContext);
909 | }
910 |
911 | showFailureDialog();
912 | mConnecting = false;
913 | notifyListener(false);
914 | }
915 |
916 | /** Called when we are disconnected from the Google API client. */
917 | @Override
918 | public void onConnectionSuspended(int cause) {
919 | debugLog("onConnectionSuspended, cause=" + cause);
920 | disconnect();
921 | mSignInFailureReason = null;
922 | debugLog("Making extraordinary call to onSignInFailed callback");
923 | mConnecting = false;
924 | notifyListener(false);
925 | }
926 |
927 | public void showFailureDialog() {
928 | if (mSignInFailureReason != null) {
929 | int errorCode = mSignInFailureReason.getServiceErrorCode();
930 | int actResp = mSignInFailureReason.getActivityResultCode();
931 |
932 | if (mShowErrorDialogs) {
933 | showFailureDialog(mActivity, actResp, errorCode);
934 | } else {
935 | debugLog("Not showing error dialog because mShowErrorDialogs==false. "
936 | + "" + "Error was: " + mSignInFailureReason);
937 | }
938 | }
939 | }
940 |
941 | /** Shows an error dialog that's appropriate for the failure reason. */
942 | public static void showFailureDialog(Activity activity, int actResp,
943 | int errorCode) {
944 | if (activity == null) {
945 | Log.e("GameHelper", "*** No Activity. Can't show failure dialog!");
946 | return;
947 | }
948 | Dialog errorDialog = null;
949 |
950 | switch (actResp) {
951 | case GamesActivityResultCodes.RESULT_APP_MISCONFIGURED:
952 | //errorDialog = makeSimpleDialog(activity, GameHelperUtils.getString(activity, GameHelperUtils.R_APP_MISCONFIGURED));//cranberrygame
953 | //https://developers.google.com/android/reference/com/google/android/gms/common/ConnectionResult#constants//cranberrygame
954 | errorDialog = makeSimpleDialog(activity, GameHelperUtils.getString(activity, GameHelperUtils.R_APP_MISCONFIGURED) + " " + GameHelperUtils.errorCodeToString(errorCode));//cranberrygame
955 | break;
956 | case GamesActivityResultCodes.RESULT_SIGN_IN_FAILED://cranberrygame
957 | //errorDialog = makeSimpleDialog(activity, GameHelperUtils.getString(activity, GameHelperUtils.R_SIGN_IN_FAILED));//cranberrygame
958 | errorDialog = makeSimpleDialog(activity, GameHelperUtils.getString(activity, GameHelperUtils.R_SIGN_IN_FAILED) + " " + GameHelperUtils.errorCodeToString(errorCode));//cranberrygame
959 | break;
960 | case GamesActivityResultCodes.RESULT_LICENSE_FAILED://cranberrygame
961 | //errorDialog = makeSimpleDialog(activity, GameHelperUtils.getString(activity, GameHelperUtils.R_LICENSE_FAILED));//cranberrygame
962 | errorDialog = makeSimpleDialog(activity, GameHelperUtils.getString(activity, GameHelperUtils.R_LICENSE_FAILED) + " " + GameHelperUtils.errorCodeToString(errorCode));//cranberrygame
963 | break;
964 | default:
965 | /*
966 | //deprecated
967 | // No meaningful Activity response code, so generate default Google
968 | // Play services dialog
969 | errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode,
970 | activity, RC_UNUSED, null);
971 | */
972 | GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
973 | int res = googleAPI.isGooglePlayServicesAvailable(activity);
974 | if(res != ConnectionResult.SUCCESS) {
975 | if(googleAPI.isUserResolvableError(res)) {
976 | errorDialog = googleAPI.getErrorDialog(activity, res, PLAY_SERVICES_RESOLUTION_REQUEST);
977 | }
978 | }
979 |
980 | if (errorDialog == null) {
981 |
982 | // get fallback dialog
983 | Log.e("GameHelper",
984 | "No standard error dialog available. Making fallback dialog.");
985 | errorDialog = makeSimpleDialog(
986 | activity,
987 | GameHelperUtils.getString(activity,
988 | GameHelperUtils.R_UNKNOWN_ERROR)
989 | + " "
990 | + GameHelperUtils.errorCodeToString(errorCode));
991 | }
992 | }
993 |
994 | errorDialog.show();
995 | }
996 |
997 | static Dialog makeSimpleDialog(Activity activity, String text) {
998 | return (new AlertDialog.Builder(activity)).setMessage(text)
999 | .setNeutralButton(android.R.string.ok, null).create();
1000 | }
1001 |
1002 | static Dialog
1003 | makeSimpleDialog(Activity activity, String title, String text) {
1004 | return (new AlertDialog.Builder(activity)).setMessage(text)
1005 | .setTitle(title).setNeutralButton(android.R.string.ok, null)
1006 | .create();
1007 | }
1008 |
1009 | public Dialog makeSimpleDialog(String text) {
1010 | if (mActivity == null) {
1011 | logError("*** makeSimpleDialog failed: no current Activity!");
1012 | return null;
1013 | }
1014 | return makeSimpleDialog(mActivity, text);
1015 | }
1016 |
1017 | public Dialog makeSimpleDialog(String title, String text) {
1018 | if (mActivity == null) {
1019 | logError("*** makeSimpleDialog failed: no current Activity!");
1020 | return null;
1021 | }
1022 | return makeSimpleDialog(mActivity, title, text);
1023 | }
1024 |
1025 | void debugLog(String message) {
1026 | if (mDebugLog) {
1027 | Log.d(TAG, "GameHelper: " + message);
1028 | }
1029 | }
1030 |
1031 | void logWarn(String message) {
1032 | Log.w(TAG, "!!! GameHelper WARNING: " + message);
1033 | }
1034 |
1035 | void logError(String message) {
1036 | Log.e(TAG, "*** GameHelper ERROR: " + message);
1037 | }
1038 |
1039 | // Represents the reason for a sign-in failure
1040 | public static class SignInFailureReason {
1041 | public static final int NO_ACTIVITY_RESULT_CODE = -100;
1042 | int mServiceErrorCode = 0;
1043 | int mActivityResultCode = NO_ACTIVITY_RESULT_CODE;
1044 |
1045 | public int getServiceErrorCode() {
1046 | return mServiceErrorCode;
1047 | }
1048 |
1049 | public int getActivityResultCode() {
1050 | return mActivityResultCode;
1051 | }
1052 |
1053 | public SignInFailureReason(int serviceErrorCode, int activityResultCode) {
1054 | mServiceErrorCode = serviceErrorCode;
1055 | mActivityResultCode = activityResultCode;
1056 | }
1057 |
1058 | public SignInFailureReason(int serviceErrorCode) {
1059 | this(serviceErrorCode, NO_ACTIVITY_RESULT_CODE);
1060 | }
1061 |
1062 | @Override
1063 | public String toString() {
1064 | return "SignInFailureReason(serviceErrorCode:"
1065 | + GameHelperUtils.errorCodeToString(mServiceErrorCode)
1066 | + ((mActivityResultCode == NO_ACTIVITY_RESULT_CODE) ? ")"
1067 | : (",activityResultCode:"
1068 | + GameHelperUtils
1069 | .activityResponseCodeToString(mActivityResultCode) + ")"));
1070 | }
1071 | }
1072 |
1073 | // Not recommended for general use. This method forces the
1074 | // "connect on start" flag
1075 | // to a given state. This may be useful when using GameHelper in a
1076 | // non-standard
1077 | // sign-in flow.
1078 | public void setConnectOnStart(boolean connectOnStart) {
1079 | debugLog("Forcing mConnectOnStart=" + connectOnStart);
1080 | mConnectOnStart = connectOnStart;
1081 | }
1082 | }
1083 |
--------------------------------------------------------------------------------
/src/android/GameHelperUtils.java:
--------------------------------------------------------------------------------
1 | package com.google.example.games.basegameutils;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.content.pm.Signature;
7 | import android.content.res.Resources;
8 | import android.util.Log;
9 |
10 | import com.google.android.gms.common.ConnectionResult;
11 | import com.google.android.gms.games.GamesActivityResultCodes;
12 |
13 | import java.security.MessageDigest;
14 | import java.security.NoSuchAlgorithmException;
15 |
16 | /**
17 | * Created by btco on 2/10/14.
18 | */
19 | class GameHelperUtils {
20 | public static final int R_UNKNOWN_ERROR = 0;
21 | public static final int R_SIGN_IN_FAILED = 1;
22 | public static final int R_APP_MISCONFIGURED = 2;
23 | public static final int R_LICENSE_FAILED = 3;
24 |
25 | private final static String[] FALLBACK_STRINGS = {
26 | "*Unknown error.",
27 | "*Failed to sign in. Please check your network connection and try again.",
28 | "*The application is incorrectly configured. Check that the package name and signing certificate match the client ID created in Developer Console. Also, if the application is not yet published, check that the account you are trying to sign in with is listed as a tester account. See logs for more information.",
29 | "*License check failed."
30 | };
31 |
32 | private final static int[] RES_IDS = {
33 | //cranberrygame start
34 | //package r does not exist android
35 | //https://muut.com/appgyver#!/steroids#phonegap-plugin-package-r
36 | //I:\test\googleplaytest\platforms\android\ant-gen\com\cranberrygame\googleplaytest
37 | /*
38 | R.string.gamehelper_unknown_error, R.string.gamehelper_sign_in_failed,
39 | R.string.gamehelper_app_misconfigured, R.string.gamehelper_license_failed
40 | */
41 | //cranberrygame end
42 | };
43 |
44 | static String activityResponseCodeToString(int respCode) {
45 | switch (respCode) {
46 | case Activity.RESULT_OK:
47 | return "RESULT_OK";
48 | case Activity.RESULT_CANCELED:
49 | return "RESULT_CANCELED";
50 | case GamesActivityResultCodes.RESULT_APP_MISCONFIGURED:
51 | return "RESULT_APP_MISCONFIGURED";
52 | case GamesActivityResultCodes.RESULT_LEFT_ROOM:
53 | return "RESULT_LEFT_ROOM";
54 | case GamesActivityResultCodes.RESULT_LICENSE_FAILED:
55 | return "RESULT_LICENSE_FAILED";
56 | case GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED:
57 | return "RESULT_RECONNECT_REQUIRED";
58 | case GamesActivityResultCodes.RESULT_SIGN_IN_FAILED:
59 | return "SIGN_IN_FAILED";
60 | default:
61 | return String.valueOf(respCode);
62 | }
63 | }
64 |
65 | static String errorCodeToString(int errorCode) {
66 | switch (errorCode) {
67 | case ConnectionResult.DEVELOPER_ERROR:
68 | return "DEVELOPER_ERROR(" + errorCode + ")";
69 | case ConnectionResult.INTERNAL_ERROR:
70 | return "INTERNAL_ERROR(" + errorCode + ")";
71 | case ConnectionResult.INVALID_ACCOUNT:
72 | return "INVALID_ACCOUNT(" + errorCode + ")";
73 | case ConnectionResult.LICENSE_CHECK_FAILED:
74 | return "LICENSE_CHECK_FAILED(" + errorCode + ")";
75 | case ConnectionResult.NETWORK_ERROR:
76 | return "NETWORK_ERROR(" + errorCode + ")";
77 | case ConnectionResult.RESOLUTION_REQUIRED:
78 | return "RESOLUTION_REQUIRED(" + errorCode + ")";
79 | case ConnectionResult.SERVICE_DISABLED:
80 | return "SERVICE_DISABLED(" + errorCode + ")";
81 | case ConnectionResult.SERVICE_INVALID:
82 | return "SERVICE_INVALID(" + errorCode + ")";
83 | case ConnectionResult.SERVICE_MISSING:
84 | return "SERVICE_MISSING(" + errorCode + ")";
85 | case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
86 | return "SERVICE_VERSION_UPDATE_REQUIRED(" + errorCode + ")";
87 | case ConnectionResult.SIGN_IN_REQUIRED:
88 | return "SIGN_IN_REQUIRED(" + errorCode + ")";
89 | case ConnectionResult.SUCCESS:
90 | return "SUCCESS(" + errorCode + ")";
91 | default:
92 | return "Unknown error code " + errorCode;
93 | }
94 | }
95 |
96 | static void printMisconfiguredDebugInfo(Context ctx) {
97 | Log.w("GameHelper", "****");
98 | Log.w("GameHelper", "****");
99 | Log.w("GameHelper", "**** APP NOT CORRECTLY CONFIGURED TO USE GOOGLE PLAY GAME SERVICES");
100 | Log.w("GameHelper", "**** This is usually caused by one of these reasons:");
101 | Log.w("GameHelper", "**** (1) Your package name and certificate fingerprint do not match");
102 | Log.w("GameHelper", "**** the client ID you registered in Developer Console.");
103 | Log.w("GameHelper", "**** (2) Your App ID was incorrectly entered.");
104 | Log.w("GameHelper", "**** (3) Your game settings have not been published and you are ");
105 | Log.w("GameHelper", "**** trying to log in with an account that is not listed as");
106 | Log.w("GameHelper", "**** a test account.");
107 | Log.w("GameHelper", "****");
108 | if (ctx == null) {
109 | Log.w("GameHelper", "*** (no Context, so can't print more debug info)");
110 | return;
111 | }
112 |
113 | Log.w("GameHelper", "**** To help you debug, here is the information about this app");
114 | Log.w("GameHelper", "**** Package name : " + ctx.getPackageName());
115 | Log.w("GameHelper", "**** Cert SHA1 fingerprint: " + getSHA1CertFingerprint(ctx));
116 | Log.w("GameHelper", "**** App ID from : " + getAppIdFromResource(ctx));
117 | Log.w("GameHelper", "****");
118 | Log.w("GameHelper", "**** Check that the above information matches your setup in ");
119 | Log.w("GameHelper", "**** Developer Console. Also, check that you're logging in with the");
120 | Log.w("GameHelper", "**** right account (it should be listed in the Testers section if");
121 | Log.w("GameHelper", "**** your project is not yet published).");
122 | Log.w("GameHelper", "****");
123 | Log.w("GameHelper", "**** For more information, refer to the troubleshooting guide:");
124 | Log.w("GameHelper", "**** http://developers.google.com/games/services/android/troubleshooting");
125 | }
126 |
127 | static String getAppIdFromResource(Context ctx) {
128 | try {
129 | Resources res = ctx.getResources();
130 | String pkgName = ctx.getPackageName();
131 | int res_id = res.getIdentifier("app_id", "string", pkgName);
132 | return res.getString(res_id);
133 | } catch (Exception ex) {
134 | ex.printStackTrace();
135 | return "??? (failed to retrieve APP ID)";
136 | }
137 | }
138 |
139 | static String getSHA1CertFingerprint(Context ctx) {
140 | try {
141 | Signature[] sigs = ctx.getPackageManager().getPackageInfo(
142 | ctx.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
143 | if (sigs.length == 0) {
144 | return "ERROR: NO SIGNATURE.";
145 | } else if (sigs.length > 1) {
146 | return "ERROR: MULTIPLE SIGNATURES";
147 | }
148 | byte[] digest = MessageDigest.getInstance("SHA1").digest(sigs[0].toByteArray());
149 | StringBuilder hexString = new StringBuilder();
150 | for (int i = 0; i < digest.length; ++i) {
151 | if (i > 0) {
152 | hexString.append(":");
153 | }
154 | byteToString(hexString, digest[i]);
155 | }
156 | return hexString.toString();
157 |
158 | } catch (PackageManager.NameNotFoundException ex) {
159 | ex.printStackTrace();
160 | return "(ERROR: package not found)";
161 | } catch (NoSuchAlgorithmException ex) {
162 | ex.printStackTrace();
163 | return "(ERROR: SHA1 algorithm not found)";
164 | }
165 | }
166 |
167 | static void byteToString(StringBuilder sb, byte b) {
168 | int unsigned_byte = b < 0 ? b + 256 : b;
169 | int hi = unsigned_byte / 16;
170 | int lo = unsigned_byte % 16;
171 | sb.append("0123456789ABCDEF".substring(hi, hi + 1));
172 | sb.append("0123456789ABCDEF".substring(lo, lo + 1));
173 | }
174 |
175 | static String getString(Context ctx, int whichString) {
176 | //cranberrygame start
177 | /*
178 | whichString = whichString >= 0 && whichString < RES_IDS.length ? whichString : 0;
179 | int resId = RES_IDS[whichString];
180 | try {
181 | return ctx.getString(resId);
182 | } catch (Exception ex) {
183 | ex.printStackTrace();
184 | Log.w(GameHelper.TAG, "*** GameHelper could not found resource id #" + resId + ". " +
185 | "This probably happened because you included it as a stand-alone JAR. " +
186 | "BaseGameUtils should be compiled as a LIBRARY PROJECT, so that it can access " +
187 | "its resources. Using a fallback string.");
188 | return FALLBACK_STRINGS[whichString];
189 | }
190 | */
191 | try {
192 | whichString = whichString >= 0 && whichString < RES_IDS.length ? whichString : 0;
193 | int resId = RES_IDS[whichString];
194 |
195 | return ctx.getString(resId);
196 | } catch (Exception ex) {
197 | return FALLBACK_STRINGS[whichString];
198 | }
199 | //cranberrygame end
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/android/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/ios/Game.h:
--------------------------------------------------------------------------------
1 | //
2 | // Game.h
3 | // Detonate
4 | //
5 | // Created by Marco Piccardo on 04/02/11.
6 | // Copyright 2011 Eurotraining Engineering. All rights reserved.
7 | //
8 | /*
9 | * Modified and Updated
10 | *
11 | * Copyright 2014 Wizcorp Inc. http://www.wizcorp.jp
12 | * Author Ally Ogilvie
13 | */
14 | //Copyright (c) 2014 Sang Ki Kwon (Cranberrygame)
15 | //Email: cranberrygame@yahoo.com
16 | //Homepage: http://www.github.com/cranberrygame
17 | //License: MIT (http://opensource.org/licenses/MIT)
18 |
19 | #import
20 |
21 | #import
22 | #import
23 |
24 | @interface Game : CDVPlugin
25 |
26 | - (void)setUp:(CDVInvokedUrlCommand *)command;
27 | - (void)login:(CDVInvokedUrlCommand *)command;
28 | - (void)logout:(CDVInvokedUrlCommand *)command;
29 | - (void)getPlayerImage:(CDVInvokedUrlCommand *)command;
30 | - (void)getPlayerScore:(CDVInvokedUrlCommand *)command;
31 | - (void)submitScore:(CDVInvokedUrlCommand *)command;
32 | - (void)showLeaderboard:(CDVInvokedUrlCommand *)command;
33 | - (void)showLeaderboards:(CDVInvokedUrlCommand *)command;
34 | - (void)unlockAchievement:(CDVInvokedUrlCommand *)command;
35 | - (void)incrementAchievement:(CDVInvokedUrlCommand *)command;
36 | - (void)showAchievements:(CDVInvokedUrlCommand *)command;
37 | - (void)resetAchievements:(CDVInvokedUrlCommand *)command;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/src/ios/Game.m:
--------------------------------------------------------------------------------
1 | //
2 | // Game.m
3 | // Detonate
4 | //
5 | // Created by Marco Piccardo on 04/02/11.
6 | // Copyright 2011 Eurotraining Engineering. All rights reserved.
7 | //
8 | /*
9 | * Modified and Updated
10 | *
11 | * Copyright 2014 Wizcorp Inc. http://www.wizcorp.jp
12 | * Author Ally Ogilvie
13 | */
14 | //Copyright (c) 2014 Sang Ki Kwon (Cranberrygame)
15 | //Email: cranberrygame@yahoo.com
16 | //Homepage: http://www.github.com/cranberrygame
17 | //License: MIT (http://opensource.org/licenses/MIT)
18 |
19 | #import "Game.h"
20 | #import
21 |
22 | #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
23 |
24 | @interface Game ()
25 | @property (nonatomic, retain) GKLeaderboardViewController *leaderboardController;
26 | @property (nonatomic, retain) GKAchievementViewController *achievementsController;
27 | @end
28 |
29 | @implementation Game
30 |
31 | - (void)setUp:(CDVInvokedUrlCommand *)command {
32 |
33 | }
34 |
35 | - (void)login:(CDVInvokedUrlCommand *)command {
36 |
37 | //[self.commandDelegate runInBackground:^{//cranberrygame
38 | [[GKLocalPlayer localPlayer] setAuthenticateHandler: ^(UIViewController *viewcontroller, NSError *error) {
39 | /*
40 | //already logged in
41 | if ([GKLocalPlayer localPlayer].authenticated) {
42 |
43 | NSString *playerID = [GKLocalPlayer localPlayer].playerID;
44 | NSString *displayName = [GKLocalPlayer localPlayer].displayName;
45 |
46 | NSDictionary* playerDetail = @{
47 | @"playerId":playerID,
48 | @"playerDisplayName":displayName
49 | };
50 |
51 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:playerDetail];
52 | //[pr setKeepCallbackAsBool:YES];
53 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
54 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
55 | //[pr setKeepCallbackAsBool:YES];
56 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
57 | }
58 | else if (viewcontroller != nil) {
59 | CDVViewController *vc = (CDVViewController *)[super viewController];
60 | [vc presentViewController:viewcontroller animated:YES completion:^{
61 | }];
62 | }
63 | else {
64 | // Called the second time with result
65 | if (error != nil) {
66 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
67 | //[pr setKeepCallbackAsBool:YES];
68 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
69 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
70 | //[pr setKeepCallbackAsBool:YES];
71 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
72 | }
73 | else {
74 | NSString *playerID = [GKLocalPlayer localPlayer].playerID;
75 | NSString *displayName = [GKLocalPlayer localPlayer].displayName;
76 |
77 | NSDictionary* playerDetail = @{
78 | @"playerId":playerID,
79 | @"playerDisplayName":displayName
80 | };
81 |
82 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:playerDetail];
83 | //[pr setKeepCallbackAsBool:YES];
84 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
85 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
86 | //[pr setKeepCallbackAsBool:YES];
87 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
88 | }
89 | }
90 | */
91 | ///*
92 | //It turns out that you need to go to Settings>Game Center and manually enable Sandbox.
93 | //http://stackoverflow.com/questions/25916055/application-is-not-recognized-by-game-center-after-building-with-xcode-6-0-1
94 | if (viewcontroller != nil) {
95 |
96 | //UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"1" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
97 | //[alert1 show];
98 |
99 | CDVViewController *vc = (CDVViewController *)[super viewController];
100 | [vc presentViewController:viewcontroller animated:YES completion:^{
101 | }];
102 | }
103 | else {
104 | //already logged in
105 | if ([GKLocalPlayer localPlayer].authenticated) {
106 |
107 | //UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"2" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
108 | //[alert2 show];
109 |
110 | NSString *playerID = [GKLocalPlayer localPlayer].playerID;
111 | NSString *displayName = [GKLocalPlayer localPlayer].displayName;
112 |
113 | NSDictionary* playerDetail = @{
114 | @"playerId":playerID,
115 | @"playerDisplayName":displayName
116 | };
117 |
118 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:playerDetail];
119 | //[pr setKeepCallbackAsBool:YES];
120 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
121 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
122 | //[pr setKeepCallbackAsBool:YES];
123 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
124 | }
125 | else {
126 |
127 | //UIAlertView *alert3 = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"3" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
128 | //[alert3 show];
129 |
130 | // Called the second time with result
131 | if (error != nil) {
132 |
133 | UIAlertView *alert4 = [[UIAlertView alloc] initWithTitle:@"Alert" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
134 | [alert4 show];
135 |
136 |
137 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
138 | //[pr setKeepCallbackAsBool:YES];
139 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
140 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
141 | //[pr setKeepCallbackAsBool:YES];
142 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
143 | }
144 | else {
145 | NSString *playerID = [GKLocalPlayer localPlayer].playerID;
146 | NSString *displayName = [GKLocalPlayer localPlayer].displayName;
147 |
148 | NSDictionary* playerDetail = @{
149 | @"playerId":playerID,
150 | @"playerDisplayName":displayName
151 | };
152 |
153 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:playerDetail];
154 | //[pr setKeepCallbackAsBool:YES];
155 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
156 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
157 | //[pr setKeepCallbackAsBool:YES];
158 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
159 | }
160 | }
161 | }
162 | //*/
163 | }];
164 | //}];//cranberrygame
165 | }
166 |
167 | - (void)logout:(CDVInvokedUrlCommand *)command {
168 | //Unfortunately, this takes the user outside your app.
169 | //http://stackoverflow.com/questions/9995576/how-to-show-game-centers-player-profile-view
170 | //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"gamecenter:/me/account"]];
171 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"gamecenter:/me/signout"]];
172 | }
173 |
174 | - (void)getPlayerImage:(CDVInvokedUrlCommand *)command {
175 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
176 | NSString *documentsDirectory = [paths objectAtIndex:0];
177 | NSString *playerImageUrl = [documentsDirectory stringByAppendingPathComponent: @"user.jpg" ];
178 |
179 | BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:playerImageUrl];
180 | if(fileExists){
181 | NSLog(@"%@", playerImageUrl);
182 |
183 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:playerImageUrl];
184 | //[pr setKeepCallbackAsBool:YES];
185 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
186 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
187 | //[pr setKeepCallbackAsBool:YES];
188 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
189 | }
190 | else{
191 | GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
192 | [localPlayer loadPhotoForSize:GKPhotoSizeSmall withCompletionHandler: ^(UIImage *photo, NSError *error) {
193 | if (photo != nil)
194 | {
195 | NSData* data = UIImageJPEGRepresentation(photo, 0.8);
196 | [data writeToFile:playerImageUrl atomically:YES];
197 | NSLog(@"%@", playerImageUrl);
198 |
199 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:playerImageUrl];
200 | //[pr setKeepCallbackAsBool:YES];
201 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
202 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
203 | //[pr setKeepCallbackAsBool:YES];
204 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
205 | }
206 | else if (error != nil)
207 | {
208 | NSLog(@"%@", [error localizedDescription]);
209 |
210 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
211 | //[pr setKeepCallbackAsBool:YES];
212 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
213 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
214 | //[pr setKeepCallbackAsBool:YES];
215 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
216 | }
217 | }];
218 | }
219 | }
220 |
221 | - (void)getPlayerScore:(CDVInvokedUrlCommand *)command {
222 | NSString *leaderboardId = [command.arguments objectAtIndex:0];
223 |
224 | //http://stackoverflow.com/questions/21591123/how-to-get-local-player-score-from-game-center
225 | GKLeaderboard *leaderboard = [[GKLeaderboard alloc] init];
226 | leaderboard.identifier = leaderboardId;
227 | [leaderboard loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
228 | if (error) {
229 | NSLog(@"%@", error);
230 |
231 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
232 | //[pr setKeepCallbackAsBool:YES];
233 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
234 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
235 | //[pr setKeepCallbackAsBool:YES];
236 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
237 | }
238 | else if (scores) {
239 | GKScore *s = leaderboard.localPlayerScore;
240 | NSLog(@"Local player's score: %lld", s.value);
241 |
242 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[NSString stringWithFormat:@"%lld", s.value]];
243 | //[pr setKeepCallbackAsBool:YES];
244 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
245 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
246 | //[pr setKeepCallbackAsBool:YES];
247 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
248 | }
249 | }];
250 | }
251 |
252 | - (void)submitScore:(CDVInvokedUrlCommand *)command {
253 |
254 | //[self.commandDelegate runInBackground:^{//cranberrygame
255 | NSString *leaderboardId = [command.arguments objectAtIndex:0];
256 | int64_t score = [[command.arguments objectAtIndex:1] integerValue];
257 |
258 | GKScore *s = [[GKScore alloc] initWithLeaderboardIdentifier: leaderboardId];
259 | s.value = score;
260 | s.context = 0;
261 |
262 | [GKScore reportScores:@[s] withCompletionHandler: ^(NSError *error) {
263 | if (error != nil)
264 | {
265 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
266 | //[pr setKeepCallbackAsBool:YES];
267 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
268 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
269 | //[pr setKeepCallbackAsBool:YES];
270 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
271 | }
272 | else
273 | {
274 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
275 | //[pr setKeepCallbackAsBool:YES];
276 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
277 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
278 | //[pr setKeepCallbackAsBool:YES];
279 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
280 | }
281 | }];
282 | //}];//cranberrygame
283 | }
284 |
285 | - (void)showLeaderboard:(CDVInvokedUrlCommand *)command {
286 | /*
287 | //runtime error
288 | //[self.commandDelegate runInBackground:^{//cranberrygame
289 | if ( self.leaderboardController == nil ) {
290 | self.leaderboardController = [[GKLeaderboardViewController alloc] init];
291 | self.leaderboardController.leaderboardDelegate = self;//
292 | }
293 | self.leaderboardController.category = (NSString *) [command.arguments objectAtIndex:0];
294 | CDVViewController *vc = (CDVViewController *)[super viewController];
295 | [vc presentViewController:self.leaderboardController animated:YES completion: ^{
296 | }];
297 | //}];//cranberrygame
298 | */
299 | ///*
300 | //https://github.com/leecrossley/cordova-plugin-game-center/blob/master/src/ios/GameCenter.m
301 | NSString *leaderboardId = (NSString *) [command.arguments objectAtIndex:0];
302 |
303 | GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
304 | if (gameCenterController != nil)
305 | {
306 | gameCenterController.gameCenterDelegate = self;
307 |
308 | if (leaderboardId.length > 0)
309 | {
310 | gameCenterController.leaderboardIdentifier = leaderboardId;
311 | }
312 |
313 | gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
314 |
315 | [self.viewController presentViewController:gameCenterController animated:YES completion:nil];
316 | }
317 | else
318 | {
319 | }
320 | //*/
321 | }
322 |
323 | - (void)showLeaderboards:(CDVInvokedUrlCommand *)command {
324 | GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
325 | if (gameCenterController != nil)
326 | {
327 | gameCenterController.gameCenterDelegate = self;
328 |
329 | gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
330 |
331 | [self.viewController presentViewController:gameCenterController animated:YES completion:nil];
332 | }
333 | else
334 | {
335 | }
336 | }
337 |
338 | - (void)unlockAchievement:(CDVInvokedUrlCommand *)command {
339 | //[self.commandDelegate runInBackground:^{//cranberrygame
340 | NSString *achievementId = [command.arguments objectAtIndex:0];
341 |
342 | GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier: achievementId];
343 | if (achievement)
344 | {
345 | achievement.percentComplete = 100;
346 |
347 | [achievement reportAchievementWithCompletionHandler: ^(NSError *error)
348 | {
349 | if (error != nil)
350 | {
351 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
352 | //[pr setKeepCallbackAsBool:YES];
353 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
354 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
355 | //[pr setKeepCallbackAsBool:YES];
356 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
357 | }
358 | else {
359 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
360 | //[pr setKeepCallbackAsBool:YES];
361 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
362 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
363 | //[pr setKeepCallbackAsBool:YES];
364 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
365 |
366 |
367 | }
368 | }];
369 | }
370 | //}];//cranberrygame
371 | }
372 |
373 | - (void)incrementAchievement:(CDVInvokedUrlCommand *)command {
374 | //[self.commandDelegate runInBackground:^{//cranberrygame
375 | NSString *achievementId = [command.arguments objectAtIndex:0];
376 | NSString *stepsOrPercent = [command.arguments objectAtIndex:1];
377 | float stepsOrPercentFloat = [stepsOrPercent floatValue];
378 |
379 | GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier: achievementId];
380 | if (achievement)
381 | {
382 | achievement.percentComplete = stepsOrPercentFloat;
383 |
384 | [achievement reportAchievementWithCompletionHandler: ^(NSError *error)
385 | {
386 | if (error != nil)
387 | {
388 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
389 | //[pr setKeepCallbackAsBool:YES];
390 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
391 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
392 | //[pr setKeepCallbackAsBool:YES];
393 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
394 | }
395 | else {
396 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
397 | //[pr setKeepCallbackAsBool:YES];
398 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
399 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
400 | //[pr setKeepCallbackAsBool:YES];
401 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
402 |
403 |
404 | }
405 | }];
406 | }
407 | //}];//cranberrygame
408 | }
409 |
410 | - (void)showAchievements:(CDVInvokedUrlCommand *)command {//cranberrygame
411 | //[self.commandDelegate runInBackground:^{
412 | if ( self.achievementsController == nil ) {
413 | self.achievementsController = [[GKAchievementViewController alloc] init];
414 | self.achievementsController.achievementDelegate = self;//
415 | }
416 | CDVViewController *vc = (CDVViewController *)[super viewController];
417 | [vc presentViewController:self.achievementsController animated:YES completion: ^{
418 | }];
419 | //}];//cranberrygame
420 | }
421 |
422 | - (void) resetAchievements:(CDVInvokedUrlCommand*)command;
423 | {
424 | [GKAchievement resetAchievementsWithCompletionHandler: ^(NSError *error)
425 | {
426 | if (error != nil)
427 | {
428 |
429 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
430 | //[pr setKeepCallbackAsBool:YES];
431 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
432 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]];
433 | //[pr setKeepCallbackAsBool:YES];
434 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
435 | } else {
436 | CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
437 | //[pr setKeepCallbackAsBool:YES];
438 | [self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
439 | //CDVPluginResult* pr = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
440 | //[pr setKeepCallbackAsBool:YES];
441 | //[self.commandDelegate sendPluginResult:pr callbackId:command.callbackId];
442 | }
443 | }];
444 | }
445 |
446 | //GKLeaderboardViewControllerDelegate
447 | - (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController {
448 | /*
449 | CDVViewController *vc = (CDVViewController *)[super viewController];
450 | [vc dismissViewControllerAnimated:YES completion:nil];
451 | */
452 | [viewController dismissViewControllerAnimated:YES completion:nil];
453 | }
454 |
455 | //GKAchievementViewControllerDelegate
456 | - (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController {
457 | /*
458 | CDVViewController* vc = (CDVViewController *)[super viewController];
459 | [vc dismissViewControllerAnimated:YES completion:nil];
460 | */
461 | [viewController dismissViewControllerAnimated:YES completion:nil];
462 | }
463 |
464 | //GKGameCenterControllerDelegate
465 | - (void) gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController
466 | {
467 | [gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
468 | }
469 |
470 | - (void)dealloc {
471 | self.leaderboardController = nil;
472 | self.achievementsController = nil;
473 |
474 | [super dealloc];
475 | }
476 |
477 | @end
478 |
--------------------------------------------------------------------------------
/www/game.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 | _loggedin: false,
4 | tag: '',
5 | setUp: function () {
6 | cordova.exec(
7 | function (result) {
8 | },
9 | function (error) {
10 | }, "Game", "setUp", []);
11 | },
12 | login: function (tag) {
13 | var self = this;
14 | cordova.exec(function (result) {
15 | var playerDetail = result;
16 | self._loggedin = true;
17 | self.tag = tag;
18 | if (self.onLoginSucceeded)
19 | self.onLoginSucceeded(playerDetail);
20 | },
21 | function (error) {
22 | self.tag = tag;
23 | if (self.onLoginFailed)
24 | self.onLoginFailed();
25 | }, "Game", "login", []);
26 | },
27 | logout: function () {
28 | var self = this;
29 | cordova.exec(function (result) {
30 | self._loggedin = false;
31 | },
32 | function (error) {
33 | }, "Game", "logout", []);
34 | },
35 | isLoggedIn: function () {
36 | return this._loggedin;
37 | },
38 | submitScore: function (leaderboardId, score, tag) {
39 | var self = this;
40 | cordova.exec(function (result) {
41 | self.tag = tag;
42 | if (self.onSubmitScoreSucceeded)
43 | self.onSubmitScoreSucceeded();
44 | },
45 | function (error) {
46 | self.tag = tag;
47 | if (self.onSubmitScoreFailed)
48 | self.onSubmitScoreFailed();
49 | }, "Game", "submitScore", [leaderboardId, score]);
50 | },
51 | showLeaderboard: function (leaderboardId) {
52 | cordova.exec(
53 | function (result) {
54 | },
55 | function (error) {
56 | }, "Game", "showLeaderboard", [leaderboardId]);
57 | },
58 | showLeaderboards: function () {
59 | cordova.exec(
60 | function (result) {
61 | },
62 | function (error) {
63 | }, "Game", "showLeaderboards", []);
64 | },
65 | getPlayerScore: function (leaderboardId, tag) {
66 | var self = this;
67 | cordova.exec(function (result) {
68 | var playerScore = result;
69 | self.tag = tag;
70 | if (self.onGetPlayerScoreSucceeded)
71 | self.onGetPlayerScoreSucceeded(playerScore);
72 | },
73 | function (error) {
74 | self.tag = tag;
75 | if (self.onGetPlayerScoreFailed)
76 | self.onGetPlayerScoreFailed();
77 | }, "Game", "getPlayerScore", [leaderboardId]);
78 | },
79 | unlockAchievement: function (achievementId, tag) {
80 | var self = this;
81 | cordova.exec(function (result) {
82 | self.tag = tag;
83 | if (self.onUnlockAchievementSucceeded)
84 | self.onUnlockAchievementSucceeded();
85 | },
86 | function (error) {
87 | self.tag = tag;
88 | if (self.onUnlockAchievementFailed)
89 | self.onUnlockAchievementFailed();
90 | }, "Game", "unlockAchievement", [achievementId]);
91 | },
92 | incrementAchievement: function (achievementId, incrementalStepOrCurrentPercent, tag) {
93 | var self = this;
94 | cordova.exec(function (result) {
95 | self.tag = tag;
96 | if (self.onIncrementAchievementSucceeded)
97 | self.onIncrementAchievementSucceeded();
98 | },
99 | function (error) {
100 | self.tag = tag;
101 | if (self.onIncrementAchievementFailed)
102 | self.onIncrementAchievementFailed();
103 | }, "Game", "incrementAchievement", [achievementId, incrementalStepOrCurrentPercent]);
104 | },
105 | showAchievements: function () {
106 | cordova.exec(
107 | function (result) {
108 | },
109 | function (error) {
110 | }, "Game", "showAchievements", []);
111 | },
112 | resetAchievements: function () {
113 | var self = this;
114 | cordova.exec(function (result) {
115 | if (self.onResetAchievementsSucceeded)
116 | self.onResetAchievementsSucceeded();
117 | },
118 | function (error) {
119 | if (self.onResetAchievementsFailed)
120 | self.onResetAchievementsFailed();
121 | }, "Game", "resetAchievements", []);
122 | },
123 | getPlayerImage: function () {
124 | var self = this;
125 | cordova.exec(function (result) {
126 | var playerImageUrl = result;
127 | if (self.onGetPlayerImageSucceeded)
128 | self.onGetPlayerImageSucceeded(playerImageUrl);
129 | },
130 | function (error) {
131 | if (self.onGetPlayerImageFailed)
132 | self.onGetPlayerImageFailed();
133 | }, "Game", "getPlayerImage", []);
134 | },
135 | onLoginSucceeded: null,
136 | onLoginFailed: null,
137 | onSubmitScoreSucceeded: null,
138 | onSubmitScoreFailed: null,
139 | onGetPlayerScoreSucceeded: null,
140 | onGetPlayerScoreFailed: null,
141 | onUnlockAchievementSucceeded: null,
142 | onUnlockAchievementFailed: null,
143 | onIncrementAchievementSucceeded: null,
144 | onIncrementAchievementFailed: null,
145 | onResetAchievementsSucceeded: null,
146 | onResetAchievementsFailed: null,
147 | onGetPlayerImageSucceeded: null,
148 | onGetPlayerImageFailed: null
149 | };
150 |
--------------------------------------------------------------------------------