PlayGamesPlatform.cs 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679
  1. // <copyright file="PlayGamesPlatform.cs" company="Google Inc.">
  2. // Copyright (C) 2014 Google Inc. All Rights Reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. // </copyright>
  16. #if UNITY_ANDROID
  17. namespace GooglePlayGames
  18. {
  19. using System;
  20. using System.Collections.Generic;
  21. using GooglePlayGames.BasicApi;
  22. using GooglePlayGames.BasicApi.Events;
  23. using GooglePlayGames.BasicApi.Nearby;
  24. using GooglePlayGames.BasicApi.SavedGame;
  25. using GooglePlayGames.BasicApi.Video;
  26. using GooglePlayGames.OurUtils;
  27. using UnityEngine;
  28. using UnityEngine.SocialPlatforms;
  29. /// <summary>
  30. /// Provides access to the Google Play Games platform. This is an implementation of
  31. /// UnityEngine.SocialPlatforms.ISocialPlatform. Activate this platform by calling
  32. /// the <see cref="Activate" /> method, then authenticate by calling
  33. /// the <see cref="Authenticate" /> method. After authentication
  34. /// completes, you may call the other methods of this class. This is not a complete
  35. /// implementation of the ISocialPlatform interface. Methods lacking an implementation
  36. /// or whose behavior is at variance with the standard are noted as such.
  37. /// </summary>
  38. public class PlayGamesPlatform : ISocialPlatform
  39. {
  40. /// <summary>Singleton instance</summary>
  41. private static volatile PlayGamesPlatform sInstance = null;
  42. /// <summary>status of nearby connection initialization.</summary>
  43. private static volatile bool sNearbyInitializePending;
  44. /// <summary>Reference to the nearby client.</summary>
  45. /// <remarks>This is static since it can be used without using play game services.</remarks>
  46. private static volatile INearbyConnectionClient sNearbyConnectionClient;
  47. /// <summary>Configuration used to create this instance.</summary>
  48. private readonly PlayGamesClientConfiguration mConfiguration;
  49. /// <summary>The local user.</summary>
  50. private PlayGamesLocalUser mLocalUser = null;
  51. /// <summary>Reference to the platform specific implementation.</summary>
  52. private IPlayGamesClient mClient = null;
  53. /// <summary>the default leaderboard we show on ShowLeaderboardUI</summary>
  54. private string mDefaultLbUi = null;
  55. /// <summary>the mapping table from alias to leaderboard/achievement id.</summary>
  56. private Dictionary<string, string> mIdMap = new Dictionary<string, string>();
  57. /// <summary>
  58. /// Initializes a new instance of the <see cref="GooglePlayGames.PlayGamesPlatform"/> class.
  59. /// </summary>
  60. /// <param name="client">Implementation client to use for this instance.</param>
  61. internal PlayGamesPlatform(IPlayGamesClient client)
  62. {
  63. this.mClient = Misc.CheckNotNull(client);
  64. this.mLocalUser = new PlayGamesLocalUser(this);
  65. this.mConfiguration = PlayGamesClientConfiguration.DefaultConfiguration;
  66. }
  67. /// <summary>
  68. /// Initializes a new instance of the <see cref="GooglePlayGames.PlayGamesPlatform"/> class.
  69. /// </summary>
  70. /// <param name="configuration">Configuration object to use.</param>
  71. private PlayGamesPlatform(PlayGamesClientConfiguration configuration)
  72. {
  73. GooglePlayGames.OurUtils.Logger.d("Creating new PlayGamesPlatform");
  74. this.mLocalUser = new PlayGamesLocalUser(this);
  75. this.mConfiguration = configuration;
  76. }
  77. /// <summary>
  78. /// Gets or sets a value indicating whether debug logs are enabled. This property
  79. /// may be set before calling <see cref="Activate" /> method.
  80. /// </summary>
  81. /// <returns>
  82. /// <c>true</c> if debug log enabled; otherwise, <c>false</c>.
  83. /// </returns>
  84. public static bool DebugLogEnabled
  85. {
  86. get { return GooglePlayGames.OurUtils.Logger.DebugLogEnabled; }
  87. set { GooglePlayGames.OurUtils.Logger.DebugLogEnabled = value; }
  88. }
  89. /// <summary>
  90. /// Gets the singleton instance of the Play Games platform.
  91. /// </summary>
  92. /// <returns>
  93. /// The instance.
  94. /// </returns>
  95. public static PlayGamesPlatform Instance
  96. {
  97. get
  98. {
  99. if (sInstance == null)
  100. {
  101. GooglePlayGames.OurUtils.Logger.d(
  102. "Instance was not initialized, using default configuration.");
  103. InitializeInstance(PlayGamesClientConfiguration.DefaultConfiguration);
  104. }
  105. return sInstance;
  106. }
  107. }
  108. /// <summary>
  109. /// Gets the nearby connection client. NOTE: Can be null until the nearby client
  110. /// is initialized. Call InitializeNearby to use callback to be notified when initialization
  111. /// is complete.
  112. /// </summary>
  113. /// <value>The nearby.</value>
  114. public static INearbyConnectionClient Nearby
  115. {
  116. get
  117. {
  118. if (sNearbyConnectionClient == null && !sNearbyInitializePending)
  119. {
  120. sNearbyInitializePending = true;
  121. InitializeNearby(null);
  122. }
  123. return sNearbyConnectionClient;
  124. }
  125. }
  126. /// <summary>Gets the saved game client object.</summary>
  127. /// <value>The saved game client.</value>
  128. public ISavedGameClient SavedGame
  129. {
  130. get { return mClient.GetSavedGameClient(); }
  131. }
  132. /// <summary>Gets the events client object.</summary>
  133. /// <value>The events client.</value>
  134. public IEventsClient Events
  135. {
  136. get { return mClient.GetEventsClient(); }
  137. }
  138. /// <summary>Gets the video client object.</summary>
  139. /// <value>The video client.</value>
  140. public IVideoClient Video
  141. {
  142. get { return mClient.GetVideoClient(); }
  143. }
  144. /// <summary>
  145. /// Gets the local user.
  146. /// </summary>
  147. /// <returns>
  148. /// The local user.
  149. /// </returns>
  150. public ILocalUser localUser
  151. {
  152. get { return mLocalUser; }
  153. }
  154. /// <summary>
  155. /// Initializes the instance of Play Game Services platform.
  156. /// </summary>
  157. /// <remarks>This creates the singleton instance of the platform.
  158. /// Multiple calls to this method are ignored.
  159. /// </remarks>
  160. /// <param name="configuration">Configuration to use when initializing.</param>
  161. public static void InitializeInstance(PlayGamesClientConfiguration configuration)
  162. {
  163. if (sInstance == null || sInstance.mConfiguration != configuration)
  164. {
  165. sInstance = new PlayGamesPlatform(configuration);
  166. return;
  167. }
  168. GooglePlayGames.OurUtils.Logger.w(
  169. "PlayGamesPlatform already initialized. Ignoring this call.");
  170. }
  171. /// <summary>
  172. /// Initializes the nearby connection platform.
  173. /// </summary>
  174. /// <remarks>This call initializes the nearby connection platform. This
  175. /// is independent of the Play Game Services initialization. Multiple
  176. /// calls to this method are ignored.
  177. /// </remarks>
  178. /// <param name="callback">Callback invoked when complete.</param>
  179. public static void InitializeNearby(Action<INearbyConnectionClient> callback)
  180. {
  181. Debug.Log("Calling InitializeNearby!");
  182. if (sNearbyConnectionClient == null)
  183. {
  184. #if UNITY_ANDROID && !UNITY_EDITOR
  185. NearbyConnectionClientFactory.Create(client => {
  186. Debug.Log("Nearby Client Created!!");
  187. sNearbyConnectionClient = client;
  188. if (callback != null) {
  189. callback.Invoke(client);
  190. }
  191. else {
  192. Debug.Log("Initialize Nearby callback is null");
  193. }
  194. });
  195. #else
  196. sNearbyConnectionClient = new DummyNearbyConnectionClient();
  197. if (callback != null)
  198. {
  199. callback.Invoke(sNearbyConnectionClient);
  200. }
  201. #endif
  202. }
  203. else if (callback != null)
  204. {
  205. Debug.Log("Nearby Already initialized: calling callback directly");
  206. callback.Invoke(sNearbyConnectionClient);
  207. }
  208. else
  209. {
  210. Debug.Log("Nearby Already initialized");
  211. }
  212. }
  213. /// <summary>
  214. /// Activates the Play Games platform as the implementation of Social.Active.
  215. /// After calling this method, you can call methods on Social.Active. For
  216. /// example, <c>Social.Active.Authenticate()</c>.
  217. /// </summary>
  218. /// <returns>The singleton <see cref="PlayGamesPlatform" /> instance.</returns>
  219. public static PlayGamesPlatform Activate()
  220. {
  221. GooglePlayGames.OurUtils.Logger.d("Activating PlayGamesPlatform.");
  222. Social.Active = PlayGamesPlatform.Instance;
  223. GooglePlayGames.OurUtils.Logger.d(
  224. "PlayGamesPlatform activated: " + Social.Active);
  225. return PlayGamesPlatform.Instance;
  226. }
  227. /// <summary>
  228. /// Sets the gravity for popups (Android only).
  229. /// </summary>
  230. /// <remarks>This can only be called after authentication. It affects
  231. /// popups for achievements and other game services elements.</remarks>
  232. /// <param name="gravity">Gravity for the popup.</param>
  233. public void SetGravityForPopups(Gravity gravity)
  234. {
  235. mClient.SetGravityForPopups(gravity);
  236. }
  237. /// <summary>
  238. /// Specifies that the ID <c>fromId</c> should be implicitly replaced by <c>toId</c>
  239. /// on any calls that take a leaderboard or achievement ID.
  240. /// </summary>
  241. /// <remarks> After a mapping is
  242. /// registered, you can use <c>fromId</c> instead of <c>toId</c> when making a call.
  243. /// For example, the following two snippets are equivalent:
  244. /// <code>
  245. /// ReportProgress("Cfiwjew894_AQ", 100.0, callback);
  246. /// </code>
  247. /// ...is equivalent to:
  248. /// <code>
  249. /// AddIdMapping("super-combo", "Cfiwjew894_AQ");
  250. /// ReportProgress("super-combo", 100.0, callback);
  251. /// </code>
  252. /// </remarks>
  253. /// <param name='fromId'>
  254. /// The identifier to map.
  255. /// </param>
  256. /// <param name='toId'>
  257. /// The identifier that <c>fromId</c> will be mapped to.
  258. /// </param>
  259. public void AddIdMapping(string fromId, string toId)
  260. {
  261. mIdMap[fromId] = toId;
  262. }
  263. /// <summary>
  264. /// Authenticate the local user with the Google Play Games service.
  265. /// </summary>
  266. /// <param name='callback'>
  267. /// The callback to call when authentication finishes. It will be called
  268. /// with <c>true</c> if authentication was successful, <c>false</c>
  269. /// otherwise.
  270. /// </param>
  271. public void Authenticate(Action<bool> callback)
  272. {
  273. Authenticate(callback, false);
  274. }
  275. /// <summary>
  276. /// Authenticate the local user with the Google Play Games service.
  277. /// </summary>
  278. /// <param name='callback'>
  279. /// The callback to call when authentication finishes. It will be called
  280. /// with <c>true</c> if authentication was successful, <c>false</c>
  281. /// otherwise.
  282. /// </param>
  283. public void Authenticate(Action<bool, string> callback)
  284. {
  285. Authenticate(callback, false);
  286. }
  287. /// <summary>
  288. /// Authenticate the local user with the Google Play Games service.
  289. /// </summary>
  290. /// <param name='callback'>
  291. /// The callback to call when authentication finishes. It will be called
  292. /// with <c>true</c> if authentication was successful, <c>false</c>
  293. /// otherwise.
  294. /// </param>
  295. /// <param name='silent'>
  296. /// Indicates whether authentication should be silent. If <c>false</c>,
  297. /// authentication may show popups and interact with the user to obtain
  298. /// authorization. If <c>true</c>, there will be no popups or interaction with
  299. /// the user, and the authentication will fail instead if such interaction
  300. /// is required. A typical pattern is to try silent authentication on startup
  301. /// and, if that fails, present the user with a "Sign in" button that then
  302. /// triggers normal (not silent) authentication.
  303. /// </param>
  304. public void Authenticate(Action<bool> callback, bool silent)
  305. {
  306. Authenticate((bool success, string msg) => callback(success), silent);
  307. }
  308. /// <summary>
  309. /// Authenticate the local user with the Google Play Games service.
  310. /// </summary>
  311. /// <param name='callback'>
  312. /// The callback to call when authentication finishes. It will be called
  313. /// with <c>true</c> if authentication was successful, <c>false</c>
  314. /// otherwise.
  315. /// </param>
  316. /// <param name='silent'>
  317. /// Indicates whether authentication should be silent. If <c>false</c>,
  318. /// authentication may show popups and interact with the user to obtain
  319. /// authorization. If <c>true</c>, there will be no popups or interaction with
  320. /// the user, and the authentication will fail instead if such interaction
  321. /// is required. A typical pattern is to try silent authentication on startup
  322. /// and, if that fails, present the user with a "Sign in" button that then
  323. /// triggers normal (not silent) authentication.
  324. /// </param>
  325. public void Authenticate(Action<bool, string> callback, bool silent)
  326. {
  327. Authenticate(silent ? SignInInteractivity.NoPrompt : SignInInteractivity.CanPromptAlways, status =>
  328. {
  329. if (status == SignInStatus.Success)
  330. {
  331. callback(true, "Authentication succeeded");
  332. }
  333. else if (status == SignInStatus.Canceled)
  334. {
  335. callback(false, "Authentication canceled");
  336. GooglePlayGames.OurUtils.Logger.d("Authentication canceled");
  337. }
  338. else if (status == SignInStatus.DeveloperError)
  339. {
  340. callback(false, "Authentication failed - developer error");
  341. GooglePlayGames.OurUtils.Logger.d("Authentication failed - developer error");
  342. }
  343. else
  344. {
  345. callback(false, "Authentication failed");
  346. GooglePlayGames.OurUtils.Logger.d("Authentication failed");
  347. }
  348. });
  349. }
  350. /// <summary>
  351. /// Authenticate the local user with the Google Play Games service.
  352. /// </summary>
  353. /// <param name="callback">The callback to call when authentication finishes.</param>
  354. /// <param name="signInInteractivity"><see cref="SignInInteractivity"/></param>
  355. public void Authenticate(SignInInteractivity signInInteractivity, Action<SignInStatus> callback)
  356. {
  357. // make a platform-specific Play Games client
  358. if (mClient == null)
  359. {
  360. GooglePlayGames.OurUtils.Logger.d(
  361. "Creating platform-specific Play Games client.");
  362. mClient = PlayGamesClientFactory.GetPlatformPlayGamesClient(mConfiguration);
  363. }
  364. if (callback == null)
  365. {
  366. callback = status => { };
  367. }
  368. switch (signInInteractivity)
  369. {
  370. case SignInInteractivity.NoPrompt:
  371. mClient.Authenticate( /* silent= */ true, code =>
  372. {
  373. // SignInStatus.UiSignInRequired is returned when silent sign in fails or when there is no
  374. // internet connection.
  375. if (code == SignInStatus.UiSignInRequired &&
  376. Application.internetReachability == NetworkReachability.NotReachable)
  377. {
  378. callback(SignInStatus.NetworkError);
  379. }
  380. else
  381. {
  382. callback(code);
  383. }
  384. });
  385. break;
  386. case SignInInteractivity.CanPromptAlways:
  387. mClient.Authenticate( /* silent= */ false, code =>
  388. {
  389. // SignInStatus.Canceled is returned when interactive sign in fails or when there is no internet connection.
  390. if (code == SignInStatus.Canceled &&
  391. Application.internetReachability == NetworkReachability.NotReachable)
  392. {
  393. callback(SignInStatus.NetworkError);
  394. }
  395. else
  396. {
  397. callback(code);
  398. }
  399. });
  400. break;
  401. case SignInInteractivity.CanPromptOnce:
  402. // 1. Silent sign in first
  403. mClient.Authenticate( /* silent= */ true, silentSignInResultCode =>
  404. {
  405. if (silentSignInResultCode == SignInStatus.Success)
  406. {
  407. OurUtils.Logger.d("Successful, triggering callback");
  408. callback(silentSignInResultCode);
  409. return;
  410. }
  411. // 2. Check the shared pref and bail out if it's true.
  412. if (!SignInHelper.ShouldPromptUiSignIn())
  413. {
  414. OurUtils.Logger.d(
  415. "User cancelled sign in attempt in the previous attempt. Triggering callback with silentSignInResultCode");
  416. callback(silentSignInResultCode);
  417. return;
  418. }
  419. // 3. Check internet connection
  420. if (Application.internetReachability == NetworkReachability.NotReachable)
  421. {
  422. OurUtils.Logger.d("No internet connection");
  423. callback(SignInStatus.NetworkError);
  424. return;
  425. }
  426. // 4. Interactive sign in
  427. mClient.Authenticate( /* silent= */ false, interactiveSignInResultCode =>
  428. {
  429. // 5. Save that the user has cancelled the interactive sign in.
  430. if (interactiveSignInResultCode == SignInStatus.Canceled)
  431. {
  432. OurUtils.Logger.d("Cancelled, saving this to a shared pref");
  433. SignInHelper.SetPromptUiSignIn(false);
  434. }
  435. callback(interactiveSignInResultCode);
  436. });
  437. });
  438. break;
  439. default:
  440. PlayGamesHelperObject.RunOnGameThread(() => callback(SignInStatus.Failed));
  441. break;
  442. }
  443. }
  444. /// <summary>
  445. /// Provided for compatibility with ISocialPlatform.
  446. /// </summary>
  447. /// <seealso cref="Authenticate(Action&lt;bool&gt;,bool)"/>
  448. /// <param name="unused">Unused parameter for this implementation.</param>
  449. /// <param name="callback">Callback invoked when complete.</param>
  450. public void Authenticate(ILocalUser unused, Action<bool> callback)
  451. {
  452. Authenticate(callback, false);
  453. }
  454. /// <summary>
  455. /// Provided for compatibility with ISocialPlatform.
  456. /// </summary>
  457. /// <seealso cref="Authenticate(Action&lt;bool&gt;,bool)"/>
  458. /// <param name="unused">Unused parameter for this implementation.</param>
  459. /// <param name="callback">Callback invoked when complete.</param>
  460. public void Authenticate(ILocalUser unused, Action<bool, string> callback)
  461. {
  462. Authenticate(callback, false);
  463. }
  464. /// <summary>
  465. /// Determines whether the user is authenticated.
  466. /// </summary>
  467. /// <returns>
  468. /// <c>true</c> if the user is authenticated; otherwise, <c>false</c>.
  469. /// </returns>
  470. public bool IsAuthenticated()
  471. {
  472. return mClient != null && mClient.IsAuthenticated();
  473. }
  474. /// <summary>Sign out. After signing out,
  475. /// Authenticate must be called again to sign back in.
  476. /// </summary>
  477. public void SignOut()
  478. {
  479. if (mClient != null)
  480. {
  481. mClient.SignOut();
  482. }
  483. mLocalUser = new PlayGamesLocalUser(this);
  484. }
  485. /// <summary>
  486. /// Loads the users.
  487. /// </summary>
  488. /// <param name="userIds">User identifiers.</param>
  489. /// <param name="callback">Callback invoked when complete.</param>
  490. public void LoadUsers(string[] userIds, Action<IUserProfile[]> callback)
  491. {
  492. if (!IsAuthenticated())
  493. {
  494. GooglePlayGames.OurUtils.Logger.e(
  495. "GetUserId() can only be called after authentication.");
  496. callback(new IUserProfile[0]);
  497. return;
  498. }
  499. mClient.LoadUsers(userIds, callback);
  500. }
  501. /// <summary>
  502. /// Returns the user's Google ID.
  503. /// </summary>
  504. /// <returns>
  505. /// The user's Google ID. No guarantees are made as to the meaning or format of
  506. /// this identifier except that it is unique to the user who is signed in.
  507. /// </returns>
  508. public string GetUserId()
  509. {
  510. if (!IsAuthenticated())
  511. {
  512. GooglePlayGames.OurUtils.Logger.e(
  513. "GetUserId() can only be called after authentication.");
  514. return "0";
  515. }
  516. return mClient.GetUserId();
  517. }
  518. /// <summary>
  519. /// Get an id token for the user.
  520. /// </summary>
  521. public string GetIdToken()
  522. {
  523. if (mClient != null)
  524. {
  525. return mClient.GetIdToken();
  526. }
  527. OurUtils.Logger.e("No client available, returning null.");
  528. return null;
  529. }
  530. /// <summary>
  531. /// Gets the server auth code.
  532. /// </summary>
  533. /// <remarks>This code is used by the server application in order to get
  534. /// an oauth token. For how to use this acccess token please see:
  535. /// https://developers.google.com/drive/v2/web/auth/web-server.
  536. /// To get another server auth code after the initial one returned, call
  537. /// GetAnotherServerAuthCode().
  538. /// </remarks>
  539. public string GetServerAuthCode()
  540. {
  541. if (mClient != null && mClient.IsAuthenticated())
  542. {
  543. return mClient.GetServerAuthCode();
  544. }
  545. return null;
  546. }
  547. /// <summary>
  548. /// Gets another server auth code.
  549. /// </summary>
  550. /// <remarks>This method should be called after authenticating, and exchanging
  551. /// the initial server auth code for a token. This is implemented by signing in
  552. /// silently, which if successful returns almost immediately and with a new
  553. /// server auth code.
  554. /// </remarks>
  555. /// <param name="reAuthenticateIfNeeded">Calls Authenticate if needed when
  556. /// retrieving another auth code. </param>
  557. /// <param name="callback">Callback returning the auth code or null
  558. /// if there was an error. NOTE: This callback can return immediately.</param>
  559. public void GetAnotherServerAuthCode(bool reAuthenticateIfNeeded,
  560. Action<string> callback)
  561. {
  562. if (mClient != null && mClient.IsAuthenticated())
  563. {
  564. mClient.GetAnotherServerAuthCode(reAuthenticateIfNeeded, callback);
  565. }
  566. else if (mClient != null && reAuthenticateIfNeeded)
  567. {
  568. mClient.Authenticate(false, (status) =>
  569. {
  570. if (status == SignInStatus.Success)
  571. {
  572. callback(mClient.GetServerAuthCode());
  573. }
  574. else
  575. {
  576. OurUtils.Logger.e("Re-authentication failed: " + status);
  577. callback(null);
  578. }
  579. });
  580. }
  581. else
  582. {
  583. OurUtils.Logger.e("Cannot call GetAnotherServerAuthCode: not authenticated");
  584. callback(null);
  585. }
  586. }
  587. /// <summary>
  588. /// Gets the user's email.
  589. /// </summary>
  590. public string GetUserEmail()
  591. {
  592. return mClient.GetUserEmail();
  593. }
  594. /// <summary>
  595. /// Gets the player stats.
  596. /// </summary>
  597. /// <param name="callback">Callback invoked when completed.</param>
  598. public void GetPlayerStats(Action<CommonStatusCodes, PlayerStats> callback)
  599. {
  600. if (mClient != null && mClient.IsAuthenticated())
  601. {
  602. mClient.GetPlayerStats(callback);
  603. }
  604. else
  605. {
  606. GooglePlayGames.OurUtils.Logger.e(
  607. "GetPlayerStats can only be called after authentication.");
  608. callback(CommonStatusCodes.SignInRequired, new PlayerStats());
  609. }
  610. }
  611. /// <summary>
  612. /// Returns the user's display name.
  613. /// </summary>
  614. /// <returns>
  615. /// The user display name (e.g. "Bruno Oliveira")
  616. /// </returns>
  617. public string GetUserDisplayName()
  618. {
  619. if (!IsAuthenticated())
  620. {
  621. GooglePlayGames.OurUtils.Logger.e(
  622. "GetUserDisplayName can only be called after authentication.");
  623. return string.Empty;
  624. }
  625. return mClient.GetUserDisplayName();
  626. }
  627. /// <summary>
  628. /// Returns the user's avatar URL if they have one.
  629. /// </summary>
  630. /// <returns>
  631. /// The URL, or <code>null</code> if the user is not authenticated or does not have
  632. /// an avatar.
  633. /// </returns>
  634. public string GetUserImageUrl()
  635. {
  636. if (!IsAuthenticated())
  637. {
  638. GooglePlayGames.OurUtils.Logger.e(
  639. "GetUserImageUrl can only be called after authentication.");
  640. return null;
  641. }
  642. return mClient.GetUserImageUrl();
  643. }
  644. /// <summary>
  645. /// Reports the progress of an achievement (reveal, unlock or increment). This method attempts
  646. /// to implement the expected behavior of ISocialPlatform.ReportProgress as closely as possible,
  647. /// as described below. Although this method works with incremental achievements for compatibility
  648. /// purposes, calling this method for incremental achievements is not recommended,
  649. /// since the Play Games API exposes incremental achievements in a very different way
  650. /// than the interface presented by ISocialPlatform.ReportProgress. The implementation of this
  651. /// method for incremental achievements attempts to produce the correct result, but may be
  652. /// imprecise. If possible, call <see cref="IncrementAchievement" /> instead.
  653. /// </summary>
  654. /// <param name='achievementID'>
  655. /// The ID of the achievement to unlock, reveal or increment. This can be a raw Google Play
  656. /// Games achievement ID (alphanumeric string), or an alias that was previously configured
  657. /// by a call to <see cref="AddIdMapping" />.
  658. /// </param>
  659. /// <param name='progress'>
  660. /// Progress of the achievement. If the achievement is standard (not incremental), then
  661. /// a progress of 0.0 will reveal the achievement and 100.0 will unlock it. Behavior of other
  662. /// values is undefined. If the achievement is incremental, then this value is interpreted
  663. /// as the total percentage of the achievement's progress that the player should have
  664. /// as a result of this call (regardless of the progress they had before). So if the
  665. /// player's previous progress was 30% and this call specifies 50.0, the new progress will
  666. /// be 50% (not 80%).
  667. /// </param>
  668. /// <param name='callback'>
  669. /// Callback that will be called to report the result of the operation: <c>true</c> on
  670. /// success, <c>false</c> otherwise.
  671. /// </param>
  672. public void ReportProgress(string achievementID, double progress, Action<bool> callback)
  673. {
  674. callback = ToOnGameThread(callback);
  675. if (!IsAuthenticated())
  676. {
  677. GooglePlayGames.OurUtils.Logger.e(
  678. "ReportProgress can only be called after authentication.");
  679. callback.Invoke(false);
  680. return;
  681. }
  682. // map ID, if it's in the dictionary
  683. GooglePlayGames.OurUtils.Logger.d("ReportProgress, " + achievementID + ", " + progress);
  684. achievementID = MapId(achievementID);
  685. // if progress is 0.0, we just want to reveal it
  686. if (progress < 0.000001)
  687. {
  688. GooglePlayGames.OurUtils.Logger.d(
  689. "Progress 0.00 interpreted as request to reveal.");
  690. mClient.RevealAchievement(achievementID, callback);
  691. return;
  692. }
  693. mClient.LoadAchievements(ach =>
  694. {
  695. for (int i = 0; i < ach.Length; i++)
  696. {
  697. if (ach[i].Id == achievementID)
  698. {
  699. if (ach[i].IsIncremental)
  700. {
  701. GooglePlayGames.OurUtils.Logger.d("Progress " + progress +
  702. " interpreted as incremental target (approximate).");
  703. if (progress >= 0.0 && progress <= 1.0)
  704. {
  705. // in a previous version, incremental progress was reported by using the range [0-1]
  706. GooglePlayGames.OurUtils.Logger.w(
  707. "Progress " + progress +
  708. " is less than or equal to 1. You might be trying to use values in the range of [0,1], while values are expected to be within the range [0,100]. If you are using the latter, you can safely ignore this message.");
  709. }
  710. mClient.SetStepsAtLeast(achievementID, progressToSteps(progress, ach[i].TotalSteps), callback);
  711. }
  712. else
  713. {
  714. if (progress >= 100)
  715. {
  716. // unlock it!
  717. GooglePlayGames.OurUtils.Logger.d("Progress " + progress + " interpreted as UNLOCK.");
  718. mClient.UnlockAchievement(achievementID, callback);
  719. }
  720. else
  721. {
  722. // not enough to unlock
  723. GooglePlayGames.OurUtils.Logger.d(
  724. "Progress " + progress + " not enough to unlock non-incremental achievement.");
  725. callback.Invoke(false);
  726. }
  727. }
  728. return;
  729. }
  730. }
  731. // Achievement not found
  732. GooglePlayGames.OurUtils.Logger.e("Unable to locate achievement " + achievementID);
  733. callback.Invoke(false);
  734. });
  735. }
  736. internal static int progressToSteps(double progress, int totalSteps) {
  737. return (progress >= 100.0) ? totalSteps : (int) (progress * totalSteps / 100.0);
  738. }
  739. /// <summary>
  740. /// Reveals the achievement with the passed identifier. This is a Play Games extension of the ISocialPlatform API.
  741. /// </summary>
  742. /// <remarks>If the operation succeeds, the callback
  743. /// will be invoked on the game thread with <code>true</code>. If the operation fails, the
  744. /// callback will be invoked with <code>false</code>. This operation will immediately fail if
  745. /// the user is not authenticated (i.e. the callback will immediately be invoked with
  746. /// <code>false</code>). If the achievement is already in a revealed state, this call will
  747. /// succeed immediately.
  748. /// </remarks>
  749. /// <param name='achievementID'>
  750. /// The ID of the achievement to increment. This can be a raw Google Play
  751. /// Games achievement ID (alphanumeric string), or an alias that was previously configured
  752. /// by a call to <see cref="AddIdMapping" />.
  753. /// </param>
  754. /// <param name='callback'>
  755. /// The callback to call to report the success or failure of the operation. The callback
  756. /// will be called with <c>true</c> to indicate success or <c>false</c> for failure.
  757. /// </param>
  758. public void RevealAchievement(string achievementID, Action<bool> callback = null)
  759. {
  760. if (!IsAuthenticated())
  761. {
  762. GooglePlayGames.OurUtils.Logger.e(
  763. "RevealAchievement can only be called after authentication.");
  764. if (callback != null)
  765. {
  766. callback.Invoke(false);
  767. }
  768. return;
  769. }
  770. // map ID, if it's in the dictionary
  771. GooglePlayGames.OurUtils.Logger.d(
  772. "RevealAchievement: " + achievementID);
  773. achievementID = MapId(achievementID);
  774. mClient.RevealAchievement(achievementID, callback);
  775. }
  776. /// <summary>
  777. /// Unlocks the achievement with the passed identifier. This is a Play Games extension of the ISocialPlatform API.
  778. /// </summary>
  779. /// <remarks>If the operation succeeds, the callback
  780. /// will be invoked on the game thread with <code>true</code>. If the operation fails, the
  781. /// callback will be invoked with <code>false</code>. This operation will immediately fail if
  782. /// the user is not authenticated (i.e. the callback will immediately be invoked with
  783. /// <code>false</code>). If the achievement is already unlocked, this call will
  784. /// succeed immediately.
  785. /// </remarks>
  786. /// <param name='achievementID'>
  787. /// The ID of the achievement to increment. This can be a raw Google Play
  788. /// Games achievement ID (alphanumeric string), or an alias that was previously configured
  789. /// by a call to <see cref="AddIdMapping" />.
  790. /// </param>
  791. /// <param name='callback'>
  792. /// The callback to call to report the success or failure of the operation. The callback
  793. /// will be called with <c>true</c> to indicate success or <c>false</c> for failure.
  794. /// </param>
  795. public void UnlockAchievement(string achievementID, Action<bool> callback = null)
  796. {
  797. if (!IsAuthenticated())
  798. {
  799. GooglePlayGames.OurUtils.Logger.e(
  800. "UnlockAchievement can only be called after authentication.");
  801. if (callback != null)
  802. {
  803. callback.Invoke(false);
  804. }
  805. return;
  806. }
  807. // map ID, if it's in the dictionary
  808. GooglePlayGames.OurUtils.Logger.d(
  809. "UnlockAchievement: " + achievementID);
  810. achievementID = MapId(achievementID);
  811. mClient.UnlockAchievement(achievementID, callback);
  812. }
  813. /// <summary>
  814. /// Increments an achievement. This is a Play Games extension of the ISocialPlatform API.
  815. /// </summary>
  816. /// <param name='achievementID'>
  817. /// The ID of the achievement to increment. This can be a raw Google Play
  818. /// Games achievement ID (alphanumeric string), or an alias that was previously configured
  819. /// by a call to <see cref="AddIdMapping" />.
  820. /// </param>
  821. /// <param name='steps'>
  822. /// The number of steps to increment the achievement by.
  823. /// </param>
  824. /// <param name='callback'>
  825. /// The callback to call to report the success or failure of the operation. The callback
  826. /// will be called with <c>true</c> to indicate success or <c>false</c> for failure.
  827. /// </param>
  828. public void IncrementAchievement(string achievementID, int steps, Action<bool> callback)
  829. {
  830. if (!IsAuthenticated())
  831. {
  832. GooglePlayGames.OurUtils.Logger.e(
  833. "IncrementAchievement can only be called after authentication.");
  834. if (callback != null)
  835. {
  836. callback.Invoke(false);
  837. }
  838. return;
  839. }
  840. // map ID, if it's in the dictionary
  841. GooglePlayGames.OurUtils.Logger.d(
  842. "IncrementAchievement: " + achievementID + ", steps " + steps);
  843. achievementID = MapId(achievementID);
  844. mClient.IncrementAchievement(achievementID, steps, callback);
  845. }
  846. /// <summary>
  847. /// Set an achievement to have at least the given number of steps completed.
  848. /// Calling this method while the achievement already has more steps than
  849. /// the provided value is a no-op. Once the achievement reaches the
  850. /// maximum number of steps, the achievement is automatically unlocked,
  851. /// and any further mutation operations are ignored.
  852. /// </summary>
  853. /// <param name='achievementID'>
  854. /// The ID of the achievement to increment. This can be a raw Google Play
  855. /// Games achievement ID (alphanumeric string), or an alias that was previously configured
  856. /// by a call to <see cref="AddIdMapping" />.
  857. /// </param>
  858. /// <param name='steps'>
  859. /// The number of steps to increment the achievement by.
  860. /// </param>
  861. /// <param name='callback'>
  862. /// The callback to call to report the success or failure of the operation. The callback
  863. /// will be called with <c>true</c> to indicate success or <c>false</c> for failure.
  864. /// </param>
  865. public void SetStepsAtLeast(string achievementID, int steps, Action<bool> callback)
  866. {
  867. if (!IsAuthenticated())
  868. {
  869. GooglePlayGames.OurUtils.Logger.e(
  870. "SetStepsAtLeast can only be called after authentication.");
  871. if (callback != null)
  872. {
  873. callback.Invoke(false);
  874. }
  875. return;
  876. }
  877. // map ID, if it's in the dictionary
  878. GooglePlayGames.OurUtils.Logger.d(
  879. "SetStepsAtLeast: " + achievementID + ", steps " + steps);
  880. achievementID = MapId(achievementID);
  881. mClient.SetStepsAtLeast(achievementID, steps, callback);
  882. }
  883. /// <summary>
  884. /// Loads the Achievement descriptions.
  885. /// </summary>
  886. /// <param name="callback">The callback to receive the descriptions</param>
  887. public void LoadAchievementDescriptions(Action<IAchievementDescription[]> callback)
  888. {
  889. if (!IsAuthenticated())
  890. {
  891. GooglePlayGames.OurUtils.Logger.e(
  892. "LoadAchievementDescriptions can only be called after authentication.");
  893. if (callback != null)
  894. {
  895. callback.Invoke(null);
  896. }
  897. return;
  898. }
  899. mClient.LoadAchievements(ach =>
  900. {
  901. IAchievementDescription[] data = new IAchievementDescription[ach.Length];
  902. for (int i = 0; i < data.Length; i++)
  903. {
  904. data[i] = new PlayGamesAchievement(ach[i]);
  905. }
  906. callback.Invoke(data);
  907. });
  908. }
  909. /// <summary>
  910. /// Loads the achievement state for the current user.
  911. /// </summary>
  912. /// <param name="callback">The callback to receive the achievements</param>
  913. public void LoadAchievements(Action<IAchievement[]> callback)
  914. {
  915. if (!IsAuthenticated())
  916. {
  917. GooglePlayGames.OurUtils.Logger.e("LoadAchievements can only be called after authentication.");
  918. callback.Invoke(null);
  919. return;
  920. }
  921. mClient.LoadAchievements(ach =>
  922. {
  923. IAchievement[] data = new IAchievement[ach.Length];
  924. for (int i = 0; i < data.Length; i++)
  925. {
  926. data[i] = new PlayGamesAchievement(ach[i]);
  927. }
  928. callback.Invoke(data);
  929. });
  930. }
  931. /// <summary>
  932. /// Creates an achievement object which may be subsequently used to report an
  933. /// achievement.
  934. /// </summary>
  935. /// <returns>
  936. /// The achievement object.
  937. /// </returns>
  938. public IAchievement CreateAchievement()
  939. {
  940. return new PlayGamesAchievement();
  941. }
  942. /// <summary>
  943. /// Reports a score to a leaderboard.
  944. /// </summary>
  945. /// <param name='score'>
  946. /// The score to report.
  947. /// </param>
  948. /// <param name='board'>
  949. /// The ID of the leaderboard on which the score is to be posted. This may be a raw
  950. /// Google Play Games leaderboard ID or an alias configured through a call to
  951. /// <see cref="AddIdMapping" />.
  952. /// </param>
  953. /// <param name='callback'>
  954. /// The callback to call to report the success or failure of the operation. The callback
  955. /// will be called with <c>true</c> to indicate success or <c>false</c> for failure.
  956. /// </param>
  957. public void ReportScore(long score, string board, Action<bool> callback)
  958. {
  959. if (!IsAuthenticated())
  960. {
  961. GooglePlayGames.OurUtils.Logger.e("ReportScore can only be called after authentication.");
  962. if (callback != null)
  963. {
  964. callback.Invoke(false);
  965. }
  966. return;
  967. }
  968. GooglePlayGames.OurUtils.Logger.d("ReportScore: score=" + score + ", board=" + board);
  969. string leaderboardId = MapId(board);
  970. mClient.SubmitScore(leaderboardId, score, callback);
  971. }
  972. /// <summary>
  973. /// Submits the score for the currently signed-in player
  974. /// to the leaderboard associated with a specific id
  975. /// and metadata (such as something the player did to earn the score).
  976. /// </summary>
  977. /// <param name="score">Score to report.</param>
  978. /// <param name="board">leaderboard id.</param>
  979. /// <param name="metadata">metadata about the score.</param>
  980. /// <param name="callback">Callback invoked upon completion.</param>
  981. public void ReportScore(long score, string board, string metadata, Action<bool> callback)
  982. {
  983. if (!IsAuthenticated())
  984. {
  985. GooglePlayGames.OurUtils.Logger.e("ReportScore can only be called after authentication.");
  986. if (callback != null)
  987. {
  988. callback.Invoke(false);
  989. }
  990. return;
  991. }
  992. GooglePlayGames.OurUtils.Logger.d("ReportScore: score=" + score +
  993. ", board=" + board +
  994. " metadata=" + metadata);
  995. string leaderboardId = MapId(board);
  996. mClient.SubmitScore(leaderboardId, score, metadata, callback);
  997. }
  998. /// <summary>
  999. /// Loads the scores relative the player.
  1000. /// </summary>
  1001. /// <remarks>This returns the 25
  1002. /// (which is the max results returned by the SDK per call) scores
  1003. /// that are around the player's score on the Public, all time leaderboard.
  1004. /// Use the overloaded methods which are specific to GPGS to modify these
  1005. /// parameters.
  1006. /// </remarks>
  1007. /// <param name="leaderboardId">Leaderboard Id</param>
  1008. /// <param name="callback">Callback to invoke when completed.</param>
  1009. public void LoadScores(string leaderboardId, Action<IScore[]> callback)
  1010. {
  1011. LoadScores(
  1012. leaderboardId,
  1013. LeaderboardStart.PlayerCentered,
  1014. mClient.LeaderboardMaxResults(),
  1015. LeaderboardCollection.Public,
  1016. LeaderboardTimeSpan.AllTime,
  1017. (scoreData) => callback(scoreData.Scores));
  1018. }
  1019. /// <summary>
  1020. /// Loads the scores using the provided parameters. This call may fail when trying to load friends with
  1021. /// ResponseCode.ResolutionRequired if the user has not share the friends list with the game. In this case, use
  1022. /// AskForLoadFriendsResolution to request access.
  1023. /// </summary>
  1024. /// <param name="leaderboardId">Leaderboard identifier.</param>
  1025. /// <param name="start">Start either top scores, or player centered.</param>
  1026. /// <param name="rowCount">Row count. the number of rows to return.</param>
  1027. /// <param name="collection">Collection. social or public</param>
  1028. /// <param name="timeSpan">Time span. daily, weekly, all-time</param>
  1029. /// <param name="callback">Callback to invoke when completed.</param>
  1030. public void LoadScores(
  1031. string leaderboardId,
  1032. LeaderboardStart start,
  1033. int rowCount,
  1034. LeaderboardCollection collection,
  1035. LeaderboardTimeSpan timeSpan,
  1036. Action<LeaderboardScoreData> callback)
  1037. {
  1038. if (!IsAuthenticated())
  1039. {
  1040. GooglePlayGames.OurUtils.Logger.e("LoadScores can only be called after authentication.");
  1041. callback(new LeaderboardScoreData(
  1042. leaderboardId,
  1043. ResponseStatus.NotAuthorized));
  1044. return;
  1045. }
  1046. mClient.LoadScores(
  1047. leaderboardId,
  1048. start,
  1049. rowCount,
  1050. collection,
  1051. timeSpan,
  1052. callback);
  1053. }
  1054. /// <summary>
  1055. /// Loads more scores. This call may fail when trying to load friends with
  1056. /// ResponseCode.ResolutionRequired if the user has not share the friends list with the game. In this case, use
  1057. /// AskForLoadFriendsResolution to request access.
  1058. /// </summary>
  1059. /// <remarks>This is used to load the next "page" of scores. </remarks>
  1060. /// <param name="token">Token used to recording the loading.</param>
  1061. /// <param name="rowCount">Row count.</param>
  1062. /// <param name="callback">Callback invoked when complete.</param>
  1063. public void LoadMoreScores(
  1064. ScorePageToken token,
  1065. int rowCount,
  1066. Action<LeaderboardScoreData> callback)
  1067. {
  1068. if (!IsAuthenticated())
  1069. {
  1070. GooglePlayGames.OurUtils.Logger.e("LoadMoreScores can only be called after authentication.");
  1071. callback(
  1072. new LeaderboardScoreData(
  1073. token.LeaderboardId,
  1074. ResponseStatus.NotAuthorized));
  1075. return;
  1076. }
  1077. mClient.LoadMoreScores(token, rowCount, callback);
  1078. }
  1079. /// <summary>
  1080. /// Returns a leaderboard object that can be configured to
  1081. /// load scores.
  1082. /// </summary>
  1083. /// <returns>The leaderboard object.</returns>
  1084. public ILeaderboard CreateLeaderboard()
  1085. {
  1086. return new PlayGamesLeaderboard(mDefaultLbUi);
  1087. }
  1088. /// <summary>
  1089. /// Shows the standard Google Play Games achievements user interface,
  1090. /// which allows the player to browse their achievements.
  1091. /// </summary>
  1092. public void ShowAchievementsUI()
  1093. {
  1094. ShowAchievementsUI(null);
  1095. }
  1096. /// <summary>
  1097. /// Shows the standard Google Play Games achievements user interface,
  1098. /// which allows the player to browse their achievements.
  1099. /// </summary>
  1100. /// <param name="callback">If non-null, the callback is invoked when
  1101. /// the achievement UI is dismissed</param>
  1102. public void ShowAchievementsUI(Action<UIStatus> callback)
  1103. {
  1104. if (!IsAuthenticated())
  1105. {
  1106. GooglePlayGames.OurUtils.Logger.e("ShowAchievementsUI can only be called after authentication.");
  1107. return;
  1108. }
  1109. GooglePlayGames.OurUtils.Logger.d("ShowAchievementsUI callback is " + callback);
  1110. mClient.ShowAchievementsUI(callback);
  1111. }
  1112. /// <summary>
  1113. /// Shows the standard Google Play Games leaderboards user interface,
  1114. /// which allows the player to browse their leaderboards. If you have
  1115. /// configured a specific leaderboard as the default through a call to
  1116. /// <see cref="SetDefaultLeaderboardForUI" />, the UI will show that
  1117. /// specific leaderboard only. Otherwise, a list of all the leaderboards
  1118. /// will be shown.
  1119. /// </summary>
  1120. public void ShowLeaderboardUI()
  1121. {
  1122. GooglePlayGames.OurUtils.Logger.d("ShowLeaderboardUI with default ID");
  1123. ShowLeaderboardUI(MapId(mDefaultLbUi), null);
  1124. }
  1125. /// <summary>
  1126. /// Shows the standard Google Play Games leaderboard UI for the given
  1127. /// leaderboard.
  1128. /// </summary>
  1129. /// <param name='leaderboardId'>
  1130. /// The ID of the leaderboard to display. This may be a raw
  1131. /// Google Play Games leaderboard ID or an alias configured through a call to
  1132. /// <see cref="AddIdMapping" />.
  1133. /// </param>
  1134. public void ShowLeaderboardUI(string leaderboardId)
  1135. {
  1136. if (leaderboardId != null)
  1137. {
  1138. leaderboardId = MapId(leaderboardId);
  1139. }
  1140. ShowLeaderboardUI(leaderboardId, LeaderboardTimeSpan.AllTime, null);
  1141. }
  1142. /// <summary>
  1143. /// Shows the leaderboard UI and calls the specified callback upon
  1144. /// completion.
  1145. /// </summary>
  1146. /// <param name="leaderboardId">leaderboard ID, can be null meaning all leaderboards.</param>
  1147. /// <param name="callback">Callback to call. If null, nothing is called.</param>
  1148. public void ShowLeaderboardUI(string leaderboardId, Action<UIStatus> callback)
  1149. {
  1150. ShowLeaderboardUI(leaderboardId, LeaderboardTimeSpan.AllTime, callback);
  1151. }
  1152. /// <summary>
  1153. /// Shows the leaderboard UI and calls the specified callback upon
  1154. /// completion.
  1155. /// </summary>
  1156. /// <param name="leaderboardId">leaderboard ID, can be null meaning all leaderboards.</param>
  1157. /// <param name="span">Timespan to display scores in the leaderboard.</param>
  1158. /// <param name="callback">Callback to call. If null, nothing is called.</param>
  1159. public void ShowLeaderboardUI(
  1160. string leaderboardId,
  1161. LeaderboardTimeSpan span,
  1162. Action<UIStatus> callback)
  1163. {
  1164. if (!IsAuthenticated())
  1165. {
  1166. GooglePlayGames.OurUtils.Logger.e("ShowLeaderboardUI can only be called after authentication.");
  1167. if (callback != null)
  1168. {
  1169. callback(UIStatus.NotAuthorized);
  1170. }
  1171. return;
  1172. }
  1173. GooglePlayGames.OurUtils.Logger.d("ShowLeaderboardUI, lbId=" +
  1174. leaderboardId + " callback is " + callback);
  1175. mClient.ShowLeaderboardUI(leaderboardId, span, callback);
  1176. }
  1177. /// <summary>
  1178. /// Sets the default leaderboard for the leaderboard UI. After calling this
  1179. /// method, a call to <see cref="ShowLeaderboardUI" /> will show only the specified
  1180. /// leaderboard instead of showing the list of all leaderboards.
  1181. /// </summary>
  1182. /// <param name='lbid'>
  1183. /// The ID of the leaderboard to display on the default UI. This may be a raw
  1184. /// Google Play Games leaderboard ID or an alias configured through a call to
  1185. /// <see cref="AddIdMapping" />.
  1186. /// </param>
  1187. public void SetDefaultLeaderboardForUI(string lbid)
  1188. {
  1189. GooglePlayGames.OurUtils.Logger.d("SetDefaultLeaderboardForUI: " + lbid);
  1190. if (lbid != null)
  1191. {
  1192. lbid = MapId(lbid);
  1193. }
  1194. mDefaultLbUi = lbid;
  1195. }
  1196. /// <summary>
  1197. /// Loads the friends that also play this game. See loadConnectedPlayers.
  1198. /// </summary>
  1199. /// <remarks>This is a callback variant of LoadFriends. When completed,
  1200. /// the friends list set in the user object, so they can accessed via the
  1201. /// friends property as needed.
  1202. /// </remarks>
  1203. /// <param name="user">The current local user</param>
  1204. /// <param name="callback">Callback invoked when complete.</param>
  1205. public void LoadFriends(ILocalUser user, Action<bool> callback)
  1206. {
  1207. if (!IsAuthenticated())
  1208. {
  1209. GooglePlayGames.OurUtils.Logger.e(
  1210. "LoadScores can only be called after authentication.");
  1211. if (callback != null)
  1212. {
  1213. callback(false);
  1214. }
  1215. return;
  1216. }
  1217. mClient.LoadFriends(callback);
  1218. }
  1219. /// <summary>
  1220. /// Loads the leaderboard based on the constraints in the leaderboard
  1221. /// object.
  1222. /// </summary>
  1223. /// <param name="board">The leaderboard object. This is created by
  1224. /// calling CreateLeaderboard(), and then initialized appropriately.</param>
  1225. /// <param name="callback">Callback invoked when complete.</param>
  1226. public void LoadScores(ILeaderboard board, Action<bool> callback)
  1227. {
  1228. if (!IsAuthenticated())
  1229. {
  1230. GooglePlayGames.OurUtils.Logger.e("LoadScores can only be called after authentication.");
  1231. if (callback != null)
  1232. {
  1233. callback(false);
  1234. }
  1235. return;
  1236. }
  1237. LeaderboardTimeSpan timeSpan;
  1238. switch (board.timeScope)
  1239. {
  1240. case TimeScope.AllTime:
  1241. timeSpan = LeaderboardTimeSpan.AllTime;
  1242. break;
  1243. case TimeScope.Week:
  1244. timeSpan = LeaderboardTimeSpan.Weekly;
  1245. break;
  1246. case TimeScope.Today:
  1247. timeSpan = LeaderboardTimeSpan.Daily;
  1248. break;
  1249. default:
  1250. timeSpan = LeaderboardTimeSpan.AllTime;
  1251. break;
  1252. }
  1253. ((PlayGamesLeaderboard) board).loading = true;
  1254. GooglePlayGames.OurUtils.Logger.d("LoadScores, board=" + board +
  1255. " callback is " + callback);
  1256. mClient.LoadScores(
  1257. board.id,
  1258. LeaderboardStart.PlayerCentered,
  1259. board.range.count > 0 ? board.range.count : mClient.LeaderboardMaxResults(),
  1260. board.userScope == UserScope.FriendsOnly ? LeaderboardCollection.Social : LeaderboardCollection.Public,
  1261. timeSpan,
  1262. (scoreData) => HandleLoadingScores(
  1263. (PlayGamesLeaderboard) board, scoreData, callback));
  1264. }
  1265. /// <summary>Asks user to give permissions for the given scopes.</summary>
  1266. /// <param name="scopes">Scope to ask permission for</param>
  1267. /// <param name="callback">Callback used to indicate the outcome of the operation.</param>
  1268. public void RequestPermission(string scope, Action<SignInStatus> callback)
  1269. {
  1270. RequestPermissions(new string[] {scope}, callback);
  1271. }
  1272. /// <summary>Asks user to give permissions for the given scopes.</summary>
  1273. /// <param name="scopes">List of scopes to ask permission for</param>
  1274. /// <param name="callback">Callback used to indicate the outcome of the operation.</param>
  1275. public void RequestPermissions(string[] scopes, Action<SignInStatus> callback)
  1276. {
  1277. if (!IsAuthenticated())
  1278. {
  1279. GooglePlayGames.OurUtils.Logger.e(
  1280. "HasPermissions can only be called after authentication.");
  1281. callback(SignInStatus.NotAuthenticated);
  1282. return;
  1283. }
  1284. mClient.RequestPermissions(scopes, callback);
  1285. }
  1286. /// <summary>Returns whether or not user has given permissions for given scopes.</summary>
  1287. /// <param name="scope">scope</param>
  1288. /// <returns><c>true</c>, if given, <c>false</c> otherwise.</returns>
  1289. public bool HasPermission(string scope)
  1290. {
  1291. return HasPermissions(new string[] {scope});
  1292. }
  1293. /// <summary>Returns whether or not user has given permissions for given scopes.</summary>
  1294. /// <param name="scopes">array of scopes</param>
  1295. /// <returns><c>true</c>, if given, <c>false</c> otherwise.</returns>
  1296. public bool HasPermissions(string[] scopes)
  1297. {
  1298. if (!IsAuthenticated())
  1299. {
  1300. GooglePlayGames.OurUtils.Logger.e(
  1301. "HasPermissions can only be called after authentication.");
  1302. return false;
  1303. }
  1304. return mClient.HasPermissions(scopes);
  1305. }
  1306. /// <summary>
  1307. /// Check if the leaderboard is currently loading.
  1308. /// </summary>
  1309. /// <returns><c>true</c>, if loading was gotten, <c>false</c> otherwise.</returns>
  1310. /// <param name="board">The leaderboard to check for loading in progress</param>
  1311. public bool GetLoading(ILeaderboard board)
  1312. {
  1313. return board != null && board.loading;
  1314. }
  1315. /// <summary>
  1316. /// Shows the Player Profile UI for the given user identifier.
  1317. /// </summary>
  1318. /// <param name="userId">User Identifier.</param>
  1319. /// <param name="otherPlayerInGameName">
  1320. /// The game's own display name of the player referred to by userId.
  1321. /// </param>
  1322. /// <param name="currentPlayerInGameName">
  1323. /// The game's own display name of the current player.
  1324. /// </param>
  1325. /// <param name="callback">Callback invoked upon completion.</param>
  1326. public void ShowCompareProfileWithAlternativeNameHintsUI(string userId,
  1327. string otherPlayerInGameName,
  1328. string currentPlayerInGameName,
  1329. Action<UIStatus> callback)
  1330. {
  1331. if (!IsAuthenticated())
  1332. {
  1333. GooglePlayGames.OurUtils.Logger.e(
  1334. "ShowCompareProfileWithAlternativeNameHintsUI can only be called after authentication.");
  1335. InvokeCallbackOnGameThread(callback, UIStatus.NotAuthorized);
  1336. return;
  1337. }
  1338. GooglePlayGames.OurUtils.Logger.d(
  1339. "ShowCompareProfileWithAlternativeNameHintsUI, userId=" + userId + " callback is " +
  1340. callback);
  1341. mClient.ShowCompareProfileWithAlternativeNameHintsUI(userId, otherPlayerInGameName,
  1342. currentPlayerInGameName, callback);
  1343. }
  1344. /// <summary>
  1345. /// Returns if the user has allowed permission for the game to access the friends list.
  1346. /// </summary>
  1347. /// <param name="forceReload">If true, this call will clear any locally cached data and
  1348. /// attempt to fetch the latest data from the server. Normally, this should be set to {@code
  1349. /// false} to gain advantages of data caching.</param>
  1350. /// <param name="callback">Callback invoked upon completion.</param>
  1351. public void GetFriendsListVisibility(bool forceReload,
  1352. Action<FriendsListVisibilityStatus> callback)
  1353. {
  1354. if (!IsAuthenticated())
  1355. {
  1356. GooglePlayGames.OurUtils.Logger.e(
  1357. "GetFriendsListVisibility can only be called after authentication.");
  1358. InvokeCallbackOnGameThread(callback, FriendsListVisibilityStatus.NotAuthorized);
  1359. return;
  1360. }
  1361. GooglePlayGames.OurUtils.Logger.d("GetFriendsListVisibility, callback is " + callback);
  1362. mClient.GetFriendsListVisibility(forceReload, callback);
  1363. }
  1364. /// <summary>
  1365. /// Shows the appropriate platform-specific friends sharing UI.
  1366. /// <param name="callback">The callback to invoke when complete. If null,
  1367. /// no callback is called. </param>
  1368. /// </summary>
  1369. public void AskForLoadFriendsResolution(Action<UIStatus> callback)
  1370. {
  1371. if (!IsAuthenticated())
  1372. {
  1373. GooglePlayGames.OurUtils.Logger.e(
  1374. "AskForLoadFriendsResolution can only be called after authentication.");
  1375. InvokeCallbackOnGameThread(callback, UIStatus.NotAuthorized);
  1376. return;
  1377. }
  1378. GooglePlayGames.OurUtils.Logger.d("AskForLoadFriendsResolution callback is " + callback);
  1379. mClient.AskForLoadFriendsResolution(callback);
  1380. }
  1381. /// <summary>
  1382. /// Gets status of the last call to load friends.
  1383. /// </summary>
  1384. public LoadFriendsStatus GetLastLoadFriendsStatus()
  1385. {
  1386. if (!IsAuthenticated())
  1387. {
  1388. GooglePlayGames.OurUtils.Logger.e(
  1389. "GetLastLoadFriendsStatus can only be called after authentication.");
  1390. return LoadFriendsStatus.NotAuthorized;
  1391. }
  1392. return mClient.GetLastLoadFriendsStatus();
  1393. }
  1394. /// <summary>
  1395. /// Loads the first page of the user's friends
  1396. /// </summary>
  1397. /// <param name="pageSize">
  1398. /// The number of entries to request for this initial page. Note that if cached
  1399. /// data already exists, the returned buffer may contain more than this size, but it is
  1400. /// guaranteed to contain at least this many if the collection contains enough records.
  1401. /// </param>
  1402. /// <param name="forceReload">
  1403. /// If true, this call will clear any locally cached data and attempt to
  1404. /// fetch the latest data from the server. This would commonly be used for something like a
  1405. /// user-initiated refresh. Normally, this should be set to {@code false} to gain advantages
  1406. /// of data caching.</param> <param name="callback">Callback invoked upon
  1407. /// completion.</param>
  1408. public void LoadFriends(int pageSize, bool forceReload,
  1409. Action<LoadFriendsStatus> callback)
  1410. {
  1411. if (!IsAuthenticated())
  1412. {
  1413. GooglePlayGames.OurUtils.Logger.e(
  1414. "LoadFriends can only be called after authentication.");
  1415. InvokeCallbackOnGameThread(callback, LoadFriendsStatus.NotAuthorized);
  1416. return;
  1417. }
  1418. mClient.LoadFriends(pageSize, forceReload, callback);
  1419. }
  1420. /// <summary>
  1421. /// Loads the friends list page
  1422. /// </summary>
  1423. /// <param name="pageSize">
  1424. /// The number of entries to request for this initial page. Note that if cached
  1425. /// data already exists, the returned buffer may contain more than this size, but it is
  1426. /// guaranteed to contain at least this many if the collection contains enough records.
  1427. /// </param>
  1428. /// <param name="callback"></param>
  1429. public void LoadMoreFriends(int pageSize, Action<LoadFriendsStatus> callback)
  1430. {
  1431. if (!IsAuthenticated())
  1432. {
  1433. GooglePlayGames.OurUtils.Logger.e(
  1434. "LoadMoreFriends can only be called after authentication.");
  1435. InvokeCallbackOnGameThread(callback, LoadFriendsStatus.NotAuthorized);
  1436. return;
  1437. }
  1438. mClient.LoadMoreFriends(pageSize, callback);
  1439. }
  1440. /// <summary>
  1441. /// Handles the processing of scores during loading.
  1442. /// </summary>
  1443. /// <param name="board">leaderboard being loaded</param>
  1444. /// <param name="scoreData">Score data.</param>
  1445. /// <param name="callback">Callback invoked when complete.</param>
  1446. internal void HandleLoadingScores(
  1447. PlayGamesLeaderboard board,
  1448. LeaderboardScoreData scoreData,
  1449. Action<bool> callback)
  1450. {
  1451. bool ok = board.SetFromData(scoreData);
  1452. if (ok && !board.HasAllScores() && scoreData.NextPageToken != null)
  1453. {
  1454. int rowCount = board.range.count - board.ScoreCount;
  1455. // need to load more scores
  1456. mClient.LoadMoreScores(
  1457. scoreData.NextPageToken,
  1458. rowCount,
  1459. (nextScoreData) =>
  1460. HandleLoadingScores(board, nextScoreData, callback));
  1461. }
  1462. else
  1463. {
  1464. callback(ok);
  1465. }
  1466. }
  1467. /// <summary>
  1468. /// Internal implmentation of getFriends.Gets the friends.
  1469. /// </summary>
  1470. /// <returns>The friends.</returns>
  1471. internal IUserProfile[] GetFriends()
  1472. {
  1473. if (!IsAuthenticated())
  1474. {
  1475. GooglePlayGames.OurUtils.Logger.d("Cannot get friends when not authenticated!");
  1476. return new IUserProfile[0];
  1477. }
  1478. return mClient.GetFriends();
  1479. }
  1480. /// <summary>
  1481. /// Maps the alias to the identifier.
  1482. /// </summary>
  1483. /// <remarks>This maps an aliased ID to the actual id. The intent of
  1484. /// this method is to allow easy to read constants to be used instead of
  1485. /// the generated ids.
  1486. /// </remarks>
  1487. /// <returns>The identifier, or null if not found.</returns>
  1488. /// <param name="id">Alias to map</param>
  1489. private string MapId(string id)
  1490. {
  1491. if (id == null)
  1492. {
  1493. return null;
  1494. }
  1495. if (mIdMap.ContainsKey(id))
  1496. {
  1497. string result = mIdMap[id];
  1498. GooglePlayGames.OurUtils.Logger.d("Mapping alias " + id + " to ID " + result);
  1499. return result;
  1500. }
  1501. return id;
  1502. }
  1503. private static void InvokeCallbackOnGameThread<T>(Action<T> callback, T data)
  1504. {
  1505. if (callback == null)
  1506. {
  1507. return;
  1508. }
  1509. PlayGamesHelperObject.RunOnGameThread(() => { callback(data); });
  1510. }
  1511. private static Action<T> ToOnGameThread<T>(Action<T> toConvert)
  1512. {
  1513. if (toConvert == null)
  1514. {
  1515. return delegate { };
  1516. }
  1517. return (val) => PlayGamesHelperObject.RunOnGameThread(() => toConvert(val));
  1518. }
  1519. }
  1520. }
  1521. #endif