AndroidClient.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // <copyright file="AndroidClient.cs" company="Google Inc.">
  2. // Copyright (C) 2015 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.Android
  18. {
  19. using System;
  20. using Com.Google.Android.Gms.Common.Api;
  21. using Com.Google.Android.Gms.Games.Stats;
  22. using Com.Google.Android.Gms.Games;
  23. using UnityEngine;
  24. using GooglePlayGames.BasicApi;
  25. using GooglePlayGames.OurUtils;
  26. using C = GooglePlayGames.Native.Cwrapper.InternalHooks;
  27. using GooglePlayGames.Native.PInvoke;
  28. internal class AndroidClient : IClientImpl
  29. {
  30. internal const string BridgeActivityClass = "com.google.games.bridge.NativeBridgeActivity";
  31. private const string LaunchBridgeMethod = "launchBridgeIntent";
  32. private const string LaunchBridgeSignature =
  33. "(Landroid/app/Activity;Landroid/content/Intent;)V";
  34. private TokenClient tokenClient;
  35. private static AndroidJavaObject invisible;
  36. public PlatformConfiguration CreatePlatformConfiguration(PlayGamesClientConfiguration clientConfig)
  37. {
  38. var config = AndroidPlatformConfiguration.Create();
  39. using (var activity = AndroidTokenClient.GetActivity())
  40. {
  41. config.SetActivity(activity.GetRawObject());
  42. config.SetOptionalIntentHandlerForUI((intent) =>
  43. {
  44. // Capture a global reference to the intent we are to show. This is required
  45. // since we are launching the intent from the game thread, and this callback
  46. // will return before this happens. If we do not hold onto a durable reference,
  47. // the code calling us will clean up the intent before we have a chance to display
  48. // it.
  49. IntPtr intentRef = AndroidJNI.NewGlobalRef(intent);
  50. PlayGamesHelperObject.RunOnGameThread(() =>
  51. {
  52. try
  53. {
  54. LaunchBridgeIntent(intentRef);
  55. }
  56. finally
  57. {
  58. // Now that we've launched the intent, release the global reference.
  59. AndroidJNI.DeleteGlobalRef(intentRef);
  60. }
  61. });
  62. });
  63. if (clientConfig.IsHidingPopups)
  64. {
  65. config.SetOptionalViewForPopups(CreateHiddenView(activity.GetRawObject()));
  66. }
  67. }
  68. return config;
  69. }
  70. public TokenClient CreateTokenClient(bool reset)
  71. {
  72. if (tokenClient == null)
  73. {
  74. tokenClient = new AndroidTokenClient();
  75. }
  76. else if (reset)
  77. {
  78. tokenClient.Signout();
  79. }
  80. return tokenClient;
  81. }
  82. private IntPtr CreateHiddenView(IntPtr activity)
  83. {
  84. // Keep it static so it will always be referenced.
  85. if (invisible == null || invisible.GetRawObject() == IntPtr.Zero) {
  86. invisible = new AndroidJavaObject("android.view.View", activity);
  87. invisible.Call("setVisibility",/*View.INVISIBLE*/(int)0x00000004);
  88. invisible.Call("setClickable", false);
  89. }
  90. return invisible.GetRawObject();
  91. }
  92. // Must be launched from the game thread (otherwise the classloader cannot locate the unity
  93. // java classes we require).
  94. private static void LaunchBridgeIntent(IntPtr bridgedIntent)
  95. {
  96. object[] objectArray = new object[2];
  97. jvalue[] jArgs = AndroidJNIHelper.CreateJNIArgArray(objectArray);
  98. try
  99. {
  100. using (var bridgeClass = new AndroidJavaClass(BridgeActivityClass))
  101. {
  102. using (var currentActivity = AndroidTokenClient.GetActivity())
  103. {
  104. // Unity no longer supports constructing an AndroidJavaObject using an IntPtr,
  105. // so I have to manually munge with JNI here.
  106. IntPtr methodId = AndroidJNI.GetStaticMethodID(bridgeClass.GetRawClass(),
  107. LaunchBridgeMethod,
  108. LaunchBridgeSignature);
  109. jArgs[0].l = currentActivity.GetRawObject();
  110. jArgs[1].l = bridgedIntent;
  111. AndroidJNI.CallStaticVoidMethod(bridgeClass.GetRawClass(), methodId, jArgs);
  112. }
  113. }
  114. }
  115. catch (Exception e)
  116. {
  117. GooglePlayGames.OurUtils.Logger.e("Exception launching bridge intent: " + e.Message);
  118. GooglePlayGames.OurUtils.Logger.e(e.ToString());
  119. }
  120. finally
  121. {
  122. AndroidJNIHelper.DeleteJNIArgArray(objectArray, jArgs);
  123. }
  124. }
  125. public void Signout()
  126. {
  127. if (tokenClient != null)
  128. {
  129. tokenClient.Signout();
  130. }
  131. }
  132. public void GetPlayerStats(IntPtr apiClient,
  133. Action<CommonStatusCodes,
  134. GooglePlayGames.BasicApi.PlayerStats> callback)
  135. {
  136. GoogleApiClient client = new GoogleApiClient(apiClient);
  137. StatsResultCallback resCallback;
  138. try
  139. {
  140. resCallback = new StatsResultCallback((result, stats) =>
  141. {
  142. Debug.Log("Result for getStats: " + result);
  143. GooglePlayGames.BasicApi.PlayerStats s = null;
  144. if (stats != null)
  145. {
  146. s = new GooglePlayGames.BasicApi.PlayerStats();
  147. s.AvgSessonLength = stats.getAverageSessionLength();
  148. s.DaysSinceLastPlayed = stats.getDaysSinceLastPlayed();
  149. s.NumberOfPurchases = stats.getNumberOfPurchases();
  150. s.NumberOfSessions = stats.getNumberOfSessions();
  151. s.SessPercentile = stats.getSessionPercentile();
  152. s.SpendPercentile = stats.getSpendPercentile();
  153. s.ChurnProbability = stats.getChurnProbability();
  154. s.SpendProbability = stats.getSpendProbability();
  155. s.HighSpenderProbability = stats.getHighSpenderProbability();
  156. s.TotalSpendNext28Days = stats.getTotalSpendNext28Days();
  157. }
  158. callback((CommonStatusCodes)result, s);
  159. });
  160. }
  161. catch (Exception e)
  162. {
  163. Debug.LogException(e);
  164. callback(CommonStatusCodes.DeveloperError, null);
  165. return;
  166. }
  167. PendingResult<Stats_LoadPlayerStatsResultObject> pr =
  168. Games.Stats.loadPlayerStats(client, true);
  169. pr.setResultCallback(resCallback);
  170. }
  171. public void SetGravityForPopups(IntPtr apiClient, Gravity gravity) {
  172. GoogleApiClient client = new GoogleApiClient(apiClient);
  173. Games.setGravityForPopups(client, (int)gravity | (int)Gravity.CENTER_HORIZONTAL);
  174. }
  175. class StatsResultCallback : ResultCallbackProxy<Stats_LoadPlayerStatsResultObject>
  176. {
  177. private Action<int, Com.Google.Android.Gms.Games.Stats.PlayerStats> callback;
  178. public StatsResultCallback(Action<int, Com.Google.Android.Gms.Games.Stats.PlayerStats> callback)
  179. {
  180. this.callback = callback;
  181. }
  182. public override void OnResult(Stats_LoadPlayerStatsResultObject arg_Result_1)
  183. {
  184. callback(arg_Result_1.getStatus().getStatusCode(), arg_Result_1.getPlayerStats());
  185. }
  186. }
  187. }
  188. }
  189. #endif