diff --git a/samples/SmokeTest/Source/Assets/SmokeTest/MainGui.cs b/samples/SmokeTest/Source/Assets/SmokeTest/MainGui.cs
index 3af91a688..5a47872fb 100644
--- a/samples/SmokeTest/Source/Assets/SmokeTest/MainGui.cs
+++ b/samples/SmokeTest/Source/Assets/SmokeTest/MainGui.cs
@@ -944,29 +944,20 @@ private void ShowPopupGravityUi()
private void ShowPermissionsUi()
{
DrawStatus();
- DrawTitle("PopupGravity Ui");
+ DrawTitle("Permissions Ui");
- if (GUI.Button(CalcGrid(0, 1), "Has Drive - AppData"))
- {
- Status = "Drive - AppData Permission " + PlayGamesPlatform.Instance.HasPermission(
- /* scope= */ "https://www.googleapis.com/auth/drive.appdata");
- }
- else if (GUI.Button(CalcGrid(1, 1), "Has Games Lite"))
+ if (GUI.Button(CalcGrid(0, 1), "Has Permission - Email"))
{
- Status = "Games Lite Permission " + PlayGamesPlatform.Instance.HasPermission(
- /* scope= */ "https://www.googleapis.com/auth/games_lite");
+ Status = "Email Permission " + PlayGamesPlatform.Instance.HasPermission("email");
}
- else if (GUI.Button(CalcGrid(0, 2), "Has User Info - Profile"))
+ else if (GUI.Button(CalcGrid(1, 1), "Request Permission- Email"))
{
- Status = "User Info - Profile Permission " + PlayGamesPlatform.Instance.HasPermission(
- /* scope= */ "https://www.googleapis.com/auth/userinfo.profile");
+ Status = "Asking permission for email";
+ PlayGamesPlatform.Instance.RequestPermission(
+ code => { Status = "Result code " + code; },
+ "email");
}
- else if (GUI.Button(CalcGrid(1, 2), "Has Play Games Activity"))
- {
- Status = "Play Games Activity Permission " + PlayGamesPlatform.Instance.HasPermission(
- /* scope= */ "https://www.googleapis.com/auth/games ");
- }
- else if (GUI.Button(CalcGrid(1, 4), "Back"))
+ else if (GUI.Button(CalcGrid(1, 6), "Back"))
{
SetUI(Ui.Main);
ShowEffect(true);
diff --git a/source/PluginDev/Assets/GooglePlayGames/BasicApi/DummyClient.cs b/source/PluginDev/Assets/GooglePlayGames/BasicApi/DummyClient.cs
index 8859099c9..6a79a0abb 100644
--- a/source/PluginDev/Assets/GooglePlayGames/BasicApi/DummyClient.cs
+++ b/source/PluginDev/Assets/GooglePlayGames/BasicApi/DummyClient.cs
@@ -372,7 +372,19 @@ public void SubmitScore(
callback.Invoke(false);
}
}
-
+
+ /// Asks user to give permissions for the given scopes.
+ /// Callback used to indicate the outcome of the operation.
+ /// Scope to ask permission for
+ public void RequestPermissions(Action callback, string[] scopes)
+ {
+ LogUsage();
+ if (callback != null)
+ {
+ callback.Invoke(SignInStatus.Failed);
+ }
+ }
+
/// Returns whether or not user has given permissions for given scopes.
///
/// array of scopes
diff --git a/source/PluginDev/Assets/GooglePlayGames/BasicApi/IPlayGamesClient.cs b/source/PluginDev/Assets/GooglePlayGames/BasicApi/IPlayGamesClient.cs
index 5f938716b..09f038063 100644
--- a/source/PluginDev/Assets/GooglePlayGames/BasicApi/IPlayGamesClient.cs
+++ b/source/PluginDev/Assets/GooglePlayGames/BasicApi/IPlayGamesClient.cs
@@ -304,6 +304,13 @@ void SubmitScore(string leaderboardId, long score,
void SubmitScore(string leaderboardId, long score, string metadata,
Action successOrFailureCalllback);
+ ///
+ /// Asks user to give permissions for the given scopes.
+ ///
+ /// Callback used to indicate the outcome of the operation.
+ /// list of scopes to ask permission for
+ void RequestPermissions(Action callback, string[] scopes);
+
///
/// Returns whether or not user has given permissions for given scopes
///
diff --git a/source/PluginDev/Assets/GooglePlayGames/BasicApi/SignInStatus.cs b/source/PluginDev/Assets/GooglePlayGames/BasicApi/SignInStatus.cs
new file mode 100644
index 000000000..22a3535ca
--- /dev/null
+++ b/source/PluginDev/Assets/GooglePlayGames/BasicApi/SignInStatus.cs
@@ -0,0 +1,42 @@
+namespace GooglePlayGames.BasicApi
+{
+ public enum SignInStatus
+ {
+ /// The operation was successful.
+ Success,
+
+ ///
+ /// The client attempted to connect to the service but the user is not signed in. The client may
+ /// choose to continue without using the API. Alternately, if {@link Status#hasResolution} returns
+ /// {@literal true} the client may call {@link Status#startResolutionForResult(Activity, int)} to
+ /// prompt the user to sign in. After the sign in activity returns with {@link Activity#RESULT_OK}
+ /// further attempts should succeed.
+ ///
+ UiSignInRequired,
+
+ /// A network error occurred. Retrying should resolve the problem.
+ NetworkError,
+
+ /// An internal error occurred.
+ InternalError,
+
+ /// The sign in was canceled.
+ Canceled,
+
+ ///
+ /// A sign in process is currently in progress and the current one cannot continue. e.g. the user
+ /// clicks the SignInButton multiple times and more than one sign in intent was launched.
+ ///
+ AlreadyInProgress,
+
+ ///
+ /// Failure reason is unknown. Check adb log to see details if any.
+ ///
+ Failed,
+
+ ///
+ /// Currently not authenticated. Silent or interactive sign in is required.
+ ///
+ NotAuthenticated,
+ }
+}
diff --git a/source/PluginDev/Assets/GooglePlayGames/BasicApi/SignInStatus.cs.meta b/source/PluginDev/Assets/GooglePlayGames/BasicApi/SignInStatus.cs.meta
new file mode 100644
index 000000000..39f52979a
--- /dev/null
+++ b/source/PluginDev/Assets/GooglePlayGames/BasicApi/SignInStatus.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 0992bc2597d741e59dc3f8c963a3ca25
+timeCreated: 1574256557
\ No newline at end of file
diff --git a/source/PluginDev/Assets/GooglePlayGames/ISocialPlatform/PlayGamesPlatform.cs b/source/PluginDev/Assets/GooglePlayGames/ISocialPlatform/PlayGamesPlatform.cs
index 6b2bf97b4..089ff67e4 100644
--- a/source/PluginDev/Assets/GooglePlayGames/ISocialPlatform/PlayGamesPlatform.cs
+++ b/source/PluginDev/Assets/GooglePlayGames/ISocialPlatform/PlayGamesPlatform.cs
@@ -1280,6 +1280,30 @@ public void LoadScores(ILeaderboard board, Action callback)
(PlayGamesLeaderboard) board, scoreData, callback));
}
+ /// Asks user to give permissions for the given scopes.
+ /// Callback used to indicate the outcome of the operation.
+ /// Scope to ask permission for
+ public void RequestPermission(Action callback, string scope)
+ {
+ RequestPermissions(callback, new string[] {scope});
+ }
+
+ /// Asks user to give permissions for the given scopes.
+ /// Callback used to indicate the outcome of the operation.
+ /// List of scopes to ask permission for
+ public void RequestPermissions(Action callback, string[] scopes)
+ {
+ if (!IsAuthenticated())
+ {
+ GooglePlayGames.OurUtils.Logger.e(
+ "HasPermissions can only be called after authentication.");
+ callback(SignInStatus.NotAuthenticated);
+ return;
+ }
+
+ mClient.RequestPermissions(callback, scopes);
+ }
+
/// Returns whether or not user has given permissions for given scopes.
/// scope
/// true, if given, false otherwise.
@@ -1287,7 +1311,7 @@ public bool HasPermission(string scope)
{
return HasPermissions(new string[] {scope});
}
-
+
/// Returns whether or not user has given permissions for given scopes.
/// array of scopes
/// true, if given, false otherwise.
@@ -1299,7 +1323,7 @@ public bool HasPermissions(string[] scopes)
"HasPermissions can only be called after authentication.");
return false;
}
-
+
return mClient.HasPermissions(scopes);
}
diff --git a/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidClient.cs b/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidClient.cs
index c67d7e19b..b199e90af 100644
--- a/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidClient.cs
+++ b/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidClient.cs
@@ -1019,6 +1019,30 @@ public void SubmitScore(string leaderboardId, long score, string metadata,
}
}
+ public void RequestPermissions(Action callback, string[] scopes)
+ {
+ callback = AsOnGameThreadCallback(callback);
+ mTokenClient.RequestPermissions((code =>
+ {
+ UpdateClients();
+ callback(code);
+ }), scopes);
+ }
+
+ private void UpdateClients()
+ {
+ lock (GameServicesLock)
+ {
+ var account = mTokenClient.GetAccount();
+ mSavedGameClient = new AndroidSavedGameClient(account);
+ mEventsClient = new AndroidEventsClient(account);
+ mVideoClient = new AndroidVideoClient(mVideoClient.IsCaptureSupported(), account);
+ mRealTimeClient = new AndroidRealTimeMultiplayerClient(this, account);
+ mTurnBasedClient = new AndroidTurnBasedMultiplayerClient(this, account);
+ mTurnBasedClient.RegisterMatchDelegate(mConfiguration.MatchDelegate);
+ }
+ }
+
/// Returns whether or not user has given permissions for given scopes.
///
public bool HasPermissions(string[] scopes)
diff --git a/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidTokenClient.cs b/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidTokenClient.cs
index 44fd96e32..066d0c76a 100644
--- a/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidTokenClient.cs
+++ b/source/PluginDev/Assets/GooglePlayGames/Platforms/Android/AndroidTokenClient.cs
@@ -18,6 +18,7 @@
namespace GooglePlayGames.Android
{
using System;
+ using System.Linq;
using BasicApi;
using OurUtils;
using UnityEngine;
@@ -132,6 +133,79 @@ public void FetchTokens(bool silent, Action callback)
PlayGamesHelperObject.RunOnGameThread(() => DoFetchToken(silent, callback));
}
+ public void RequestPermissions(Action callback, string[] scopes)
+ {
+ using (var bridgeClass = new AndroidJavaClass(HelperFragmentClass))
+ using (var currentActivity = AndroidHelperFragment.GetActivity())
+ using (var task =
+ bridgeClass.CallStatic("showRequestPermissionsUi", currentActivity,
+ oauthScopes.Union(scopes).ToArray()))
+ {
+ AndroidTaskUtils.AddOnSuccessListener(task, /* disposeResult= */ false,
+ accountWithNewScopes =>
+ {
+ if (accountWithNewScopes == null)
+ {
+ callback(SignInStatus.InternalError);
+ return;
+ }
+
+ account = accountWithNewScopes;
+ email = account.Call("getEmail");
+ idToken = account.Call("getIdToken");
+ authCode = account.Call("getServerAuthCode");
+ oauthScopes = oauthScopes.Union(scopes).ToList();
+ callback(SignInStatus.Success);
+ });
+
+ AndroidTaskUtils.AddOnFailureListener(task, e =>
+ {
+ var failCode = ToSignInStatus(e.Call("getStatusCode"));
+ OurUtils.Logger.e("Exception requesting new permissions: " + failCode);
+ callback(failCode);
+ });
+ }
+ }
+
+ private static SignInStatus ToSignInStatus(int code)
+ {
+ Dictionary dictionary = new Dictionary()
+ {
+ {
+ /* CommonUIStatus.UI_BUSY */ -12, SignInStatus.AlreadyInProgress
+ },
+ {
+ /* CommonStatusCodes.SUCCESS */ 0, SignInStatus.Success
+ },
+ {
+ /* CommonStatusCodes.SIGN_IN_REQUIRED */ 4, SignInStatus.UiSignInRequired
+ },
+ {
+ /* CommonStatusCodes.NETWORK_ERROR */ 7, SignInStatus.NetworkError
+ },
+ {
+ /* CommonStatusCodes.INTERNAL_ERROR */ 8, SignInStatus.InternalError
+ },
+ {
+ /* CommonStatusCodes.CANCELED */ 16, SignInStatus.Canceled
+ },
+ {
+ /* CommonStatusCodes.API_NOT_CONNECTED */ 17, SignInStatus.Failed
+ },
+ {
+ /* GoogleSignInStatusCodes.SIGN_IN_FAILED */ 12500, SignInStatus.Failed
+ },
+ {
+ /* GoogleSignInStatusCodes.SIGN_IN_CANCELLED */ 12501, SignInStatus.Canceled
+ },
+ {
+ /* GoogleSignInStatusCodes.SIGN_IN_CURRENTLY_IN_PROGRESS */ 12502, SignInStatus.AlreadyInProgress
+ },
+ };
+
+ return dictionary.ContainsKey(code) ? dictionary[code] : SignInStatus.Failed;
+ }
+
/// Returns whether or not user has given permissions for given scopes.
/// array of scopes
/// true, if given, false otherwise.
diff --git a/source/PluginDev/Assets/GooglePlayGames/Platforms/TokenClient.cs b/source/PluginDev/Assets/GooglePlayGames/Platforms/TokenClient.cs
index e8e1e525b..bb70aa0a6 100644
--- a/source/PluginDev/Assets/GooglePlayGames/Platforms/TokenClient.cs
+++ b/source/PluginDev/Assets/GooglePlayGames/Platforms/TokenClient.cs
@@ -17,6 +17,7 @@
#if UNITY_ANDROID
namespace GooglePlayGames
{
+ using GooglePlayGames.BasicApi;
using System;
internal interface TokenClient
@@ -68,6 +69,8 @@ void GetAnotherServerAuthCode(bool reAuthenticateIfNeeded,
void FetchTokens(bool silent, Action callback);
+ void RequestPermissions(Action callback, string[] scopes);
+
bool HasPermissions(string[] scopes);
}
}
diff --git a/source/SupportLib/PlayGamesPluginSupport/src/main/java/com/google/games/bridge/HelperFragment.java b/source/SupportLib/PlayGamesPluginSupport/src/main/java/com/google/games/bridge/HelperFragment.java
index c9b1a7927..de15351ac 100644
--- a/source/SupportLib/PlayGamesPluginSupport/src/main/java/com/google/games/bridge/HelperFragment.java
+++ b/source/SupportLib/PlayGamesPluginSupport/src/main/java/com/google/games/bridge/HelperFragment.java
@@ -56,6 +56,7 @@ public class HelperFragment extends Fragment
static final int RC_INBOX_UI = 9007;
static final int RC_SHOW_WAITING_ROOM_UI = 9008;
static final int RC_SHOW_INVITATION_INBOX_UI = 9009;
+ static final int RC_SHOW_REQUEST_PERMISSIONS_UI = 9010;
// Pending token request. There can be only one outstanding request at a
// time.
@@ -204,7 +205,7 @@ public static Task showInvitationInboxUI(Ac
return request.getTask();
}
- public static Task showInboxUi(Activity parentActivity){
+ public static Task showInboxUi(Activity parentActivity) {
InboxUiRequest request = new InboxUiRequest();
if(!HelperFragment.startRequest(parentActivity, request)) {
@@ -214,13 +215,26 @@ public static Task showInboxUi(Activity parentActivity){
return request.getTask();
}
- public static boolean hasPermissions(Activity parentActivity, String[] scopeUris) {
+ public static Task showRequestPermissionsUi(Activity parentActivity, String[] scopes) {
+ RequestPermissionsRequest request = new RequestPermissionsRequest(toScopeList(scopes));
+
+ if(!HelperFragment.startRequest(parentActivity, request)) {
+ request.setFailure(CommonUIStatus.UI_BUSY);
+ }
+
+ return request.getTask();
+ }
+
+ public static boolean hasPermissions(Activity parentActivity, String[] scopes) {
+ return GoogleSignIn.hasPermissions(getAccount(parentActivity), toScopeList(scopes));
+ }
+
+ private static Scope[] toScopeList(String[] scopeUris) {
Scope[] scopes = new Scope[scopeUris.length];
for (int i = 0; i < scopeUris.length; i++) {
scopes[i] = new Scope(scopeUris[i]);
}
-
- return GoogleSignIn.hasPermissions(getAccount(parentActivity), scopes);
+ return scopes;
}
public static void signOut(Activity activity) {
diff --git a/source/SupportLib/PlayGamesPluginSupport/src/main/java/com/google/games/bridge/RequestPermissionsRequest.java b/source/SupportLib/PlayGamesPluginSupport/src/main/java/com/google/games/bridge/RequestPermissionsRequest.java
new file mode 100644
index 000000000..33a8935fa
--- /dev/null
+++ b/source/SupportLib/PlayGamesPluginSupport/src/main/java/com/google/games/bridge/RequestPermissionsRequest.java
@@ -0,0 +1,104 @@
+package com.google.games.bridge;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.util.Log;
+
+import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.auth.api.signin.GoogleSignIn;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+import com.google.android.gms.auth.api.signin.GoogleSignInClient;
+import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;
+import com.google.android.gms.auth.api.signin.GoogleSignInResult;
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
+import com.google.android.gms.common.api.ApiException;
+import com.google.android.gms.common.api.CommonStatusCodes;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.common.api.Scope;
+import com.google.android.gms.tasks.Task;
+import com.google.android.gms.tasks.TaskCompletionSource;
+import java.util.ArrayList;
+
+class RequestPermissionsRequest implements HelperFragment.Request {
+ private static final String TAG = "RequestPermissions";
+
+ private final TaskCompletionSource resultTaskSource = new TaskCompletionSource<>();
+ private Scope[] scopes;
+ private HelperFragment helperFragment;
+
+ public RequestPermissionsRequest(Scope[] scopes) {
+ this.scopes = scopes;
+ }
+
+ Task getTask() {
+ return resultTaskSource.getTask();
+ }
+
+ public void process(HelperFragment helperFragment) {
+ this.helperFragment = helperFragment;
+ final Activity activity = helperFragment.getActivity();
+ GoogleSignInAccount account = HelperFragment.getAccount(activity);
+ Scope[] unauthorizedScopes = getUnauthorizedScopes(account, scopes);
+ if (unauthorizedScopes.length == 0) {
+ setSuccess(GoogleSignIn.getAccountForScopes(activity, scopes[0], scopes));
+ } else {
+ Intent intent = getSignInIntentForAccountAndScopes(activity, account, unauthorizedScopes);
+ helperFragment.startActivityForResult(intent, HelperFragment.RC_SHOW_REQUEST_PERMISSIONS_UI);
+ }
+ }
+
+ private Scope[] getUnauthorizedScopes(GoogleSignInAccount account, Scope[] scopes) {
+ ArrayList unauthorizedScopes = new ArrayList();
+ for (Scope scope : scopes) {
+ if (!GoogleSignIn.hasPermissions(account, scope)) {
+ unauthorizedScopes.add(scope);
+ }
+ }
+ return unauthorizedScopes.toArray(new Scope[unauthorizedScopes.size()]);
+ }
+
+ private static Intent getSignInIntentForAccountAndScopes(
+ /* @NonNull */ Activity activity, /* @Nullable */ GoogleSignInAccount account, /* @NonNull */ Scope... scopes) {
+ GoogleSignInOptions.Builder optionsBuilder = new GoogleSignInOptions.Builder();
+
+ if (scopes.length > 0) {
+ optionsBuilder.requestScopes(scopes[0], scopes);
+ }
+
+ if (account != null && account.getEmail() != null) {
+ optionsBuilder.setAccountName(account.getEmail());
+ }
+
+ return GoogleSignIn.getClient(activity, optionsBuilder.build()).getSignInIntent();
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == HelperFragment.RC_SHOW_REQUEST_PERMISSIONS_UI) {
+ GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
+ if (result != null && result.isSuccess()) {
+ GoogleSignInAccount account = GoogleSignIn.getAccountForScopes(helperFragment.getActivity(), scopes[0], scopes);
+ setSuccess(account);
+ } else if (resultCode == Activity.RESULT_CANCELED) {
+ if (result != null) {
+ setFailure(result.getStatus().getStatusCode());
+ } else {
+ setFailure(CommonStatusCodes.CANCELED);
+ }
+ } else if (result != null) {
+ setFailure(result.getStatus().getStatusCode());
+ } else {
+ setFailure(CommonStatusCodes.ERROR);
+ }
+ }
+ }
+
+ void setFailure(int code) {
+ resultTaskSource.setException(new ApiException(new Status(code)));
+ HelperFragment.finishRequest(this);
+ }
+
+ private void setSuccess(GoogleSignInAccount account) {
+ resultTaskSource.setResult(account);
+ HelperFragment.finishRequest(this);
+ }
+}