适用于原生安卓的登录模块

按照此页面的步骤将now.gg登录集成到您的Android应用中,配置并将now.gg登录按钮添加到您的应用布局中,以启动登录流程。

在您开始之前

确保您已检查了 前提条件清单 并设置好了开发者环境。

添加权限、依赖项和资产

1. 添加权限

首先添加权限到 AndroidManifest.xml

 <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
  <uses-permission android:name="android.permission.INTERNET"/> 

2. 添加依赖项

添加以下依赖项到您应用程序的 build.gradle 文件:

//retrofit
 implementation 'com.squareup.retrofit2:retrofit:2.9.0'
 implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
 implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
 implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

3. 展示now.gg登录按钮

3.1 下载now.gg登录按钮的资源

now.gg登录按钮的资源(assets)包含于 now.gg SDK包 中,位于 Assets/Login 目录下。

3.2 选择集成登录按钮的方式
  • 推荐的登录按钮:使用提供的按钮资源实现标准的观感。
  • 自定义登录按钮: 使用提供的仅包含标志的资源和您自己的文字,创建一个符合您应用程序风格的“使用 now.gg 登录”按钮。
3.3 在您应用的布局中添加“now.gg登录按钮”:
Button signInButton = findViewById(R.id.now_gg_login_button);


开始登录流程

1. 将 OnClick 事件添加到登录按钮

在Android activity的 onCreate 方法中为登录按钮添加 OnClickListener

 findViewById(R.id.now_gg_login_button).setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         signIn();
     }
 });

2. 检查权限和结果

您现在应检查 GET_ACCOUNTS 权限和结果。

  • 要检查 GET_ACCOUNTS 权限,在 onCreate 方法中添加以下代码:
    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
     
         if (checkSelfPermission(Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) {
             requestPermissions(new String[] {
                     Manifest.permission.GET_ACCOUNTS
                 },
                 GET_ACCOUNTS_REQUEST_CODE);
         } else {
             // 已有 GET_ACCOUNTS 权限,可以继续登录
             Log.d(TAG, "onCreate: Already has permission, proceed with signin");
         }
     }
    
  • 要检查结果,在 onRequestPermissionsResult 中添加以下代码:
    @Override
     public void onRequestPermissionsResult(int requestCode,
         String permissions[], int[] grantResults) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
         if (requestCode == GET_ACCOUNTS_REQUEST_CODE) {
             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                 // 已授权,可以继续登录
                 signIn();
             }
         }
     }
    

获取用户和会话信息

  • 我们还提供了一个高级且更安全的 Auth Code Flow 用于now.gg登录集成。
    • 此流程使您能够获取详细的用户和会话信息。
    • 完成上述步骤后,转到 此章节 以使用 Auth Code flow 实现 now.gg 登录
  • 如果您只希望在用户登录后获取基本的个人信息,可以继续以下步骤。

继续基本登录流程


3. 执行登录

以下示例代码用于“登录now.gg账户”(’Sign-in with now.gg Account’),应在点击登录按钮时触发。

 public static final String ID_TOKEN = "id_token"; // 不要修改
  public static final String CLIENT_ID = "<your client id>"; // 替换为您的client id
  public static final String ACCOUNT_TYPE = "now.gg"; // 不要修改
  public static final String HOST_URL = "hostUrl"; //不要修改

  private void signIn() {
      Account account = getNowggAccount();
      if (account != null) {
          Bundle bundle = new Bundle();
          bundle.putString("client_id", CLIENT_ID);
          String authTokenType = ID_TOKEN;
          AccountManager.get(getApplicationContext()).
          getAuthToken(account, authTokenType, bundle, MainActivity.this, new OnTokenAcquired(), null);
      } else {
          addNowggAccount();
      }
  }

  private Account getNowggAccount() {
      Account[] accounts = AccountManager.get(getApplicationContext()).getAccountsByType(ACCOUNT_TYPE);
      if (accounts.length & gt; 0) {
          Log.d(TAG, "getNowggAccount: account found");
          // 在一个系统中,目前仅允许添加一个now.gg账户
          return accounts[0];
      }
      return null;
  }

  private void addNowggAccount() {
      try {
          Intent intent = new Intent();
          intent.setComponent(new ComponentName("gg.now.accounts", "gg.now.accounts.AuthenticatorActivity"));
          intent.setAction("IAP_ADD_ACCOUNT");
          intent.putExtra("resultReceiver", parcelResultReceiver(resultReceiver));
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          startActivity(intent);
      } catch (ActivityNotFoundException e) {
          e.printStackTrace();
      }
  }

  ResultReceiver parcelResultReceiver(ResultReceiver actualReceiver) {
      Parcel parcel = Parcel.obtain();
      actualReceiver.writeToParcel(parcel, 0);
      parcel.setDataPosition(0);
      ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
      parcel.recycle();
      return receiverForSending;
  }

  ResultReceiver resultReceiver = new ResultReceiver(new Handler(Looper.getMainLooper())) {
      @Override
      protected void onReceiveResult(int resultCode, Bundle resultData) {
          super.onReceiveResult(resultCode, resultData);
          Log.d(TAG, "onReceiveResult() called with: resultCode = [" + resultCode + "], resultData = [" + resultData.toString() + "]");
          // onReceiveResult
          if (resultCode == 0) { // 登录成功
              signIn();
          } else {
              // 登录失败
             Log.d(TAG, "onReceiveResult: sign in failed");
          }
      }
  };

4. 获取请求的令牌

用户登录后,您将在 OnTokenAcquired 回调函数中取得请求的令牌(id_token)。

通过验证此令牌或将其传给您的后端服务器,您能够获取用户详情。您将从您的服务器取得作为响应的用户详情。

      
 private class OnTokenAcquired implements AccountManagerCallback {

 @Override
  public void run(AccountManagerFuture result) {
  try {
       Bundle bundle = (Bundle) result.getResult();
       boolean success = bundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
         if (success) {
            final String token = bundle.getString(ID_TOKEN);
            // 获取now.gg服务器URL,用其获得用户详情和验证令牌

            final String hostUrl = bundle.getString(HOST_URL);
            verifyToken(token, hostUrl);
            }
            else {
                 // 获取令牌失败
                 // 错误时,开发者可以展示错误或者采用其它登录策略。
                 Log.d(TAG, "run: get token failed " + bundle);
                 }
          } catch (AuthenticatorException | IOException | OperationCanceledException e) {
            e.printStackTrace();
          }
     }
 }

5. 验证令牌并获取用户详情

现在您需要通过调用 verifyToken 函数以验证 id_token(您在 OnTokenAcquired 接收到的)的完整性,并获取用户详情,如下所示:

     
  private void verifyToken(String idToken, String hostUrl) {
  BackendApiCallService backendApiCallService = getBackendRetrofit(hostUrl).create(BackendApiCallService.class);

  Call < TokenVerifyResponse > tokenVerifyResponseCall = backendApiCallService.verifyIdToken(new TokenVerifyRequest(ID_TOKEN, idToken, CLIENT_ID));

  tokenVerifyResponseCall.enqueue(new Callback < TokenVerifyResponse > () {
    @Override
    public void onResponse(Call < TokenVerifyResponse > call, Response < TokenVerifyResponse > response) {
      if (response.isSuccessful()) {
        Log.d(TAG, "onResponse: " + response.body().toString());
      } else {
        Gson gson = new Gson();
        TokenVerifyResponse error = gson.fromJson(response.errorBody().charStream(), TokenVerifyResponse.class);

        if (!error.isSuccess() && ("EXPIRED_TOKEN".equals(error.getCode()) || "INVALID_TOKEN".equals(error.getCode()))) {
          // retry the verify token call, after getting a new TOKEN
        }
      }
    }
    @Override
    public void onFailure(Call call, Throwable t) {
      Log.d(TAG, "onFailure() called with: call = [" + call + "], t = [" + t + "]");
    }
  });

}


 public Retrofit getBackendRetrofit(String hostUrl) {
   OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
   return new Retrofit.Builder()
     .baseUrl(hostUrl)
     .addConverterFactory(ScalarsConverterFactory.create())
     .addConverterFactory(GsonConverterFactory.create())
     .client(okHttpClient)
     .build();
 }

 public interface BackendApiCallService {
   @POST("/accounts/oauth2/v1/verify-token")
   Call < TokenVerifyResponse > verifyIdToken(@Body TokenVerifyRequest authRequest);
 }

 public class TokenVerifyRequest {
   String token_type;
   String token;
   String client_id;

   public TokenVerifyRequest(String token_type, String token, String client_id) {
     this.token_type = token_type;
     this.token = token;
     this.client_id = client_id;
   }
 }

 public class TokenVerifyResponse {
   boolean success;
   String code;
   String msg;
   @SerializedName("decodedData")
   UserDataVerified userDataVerified;

   public TokenVerifyResponse(boolean success, String code, String msg, UserDataVerified userDataVerified) {
     this.success = success;
     this.code = code;
     this.msg = msg;
     this.userDataVerified = userDataVerified;
   }

   public UserDataVerified getUserDataVerified() {
     return userDataVerified;
   }

   public boolean isSuccess() {
     return success;
   }
   public String getCode() {
    return code;
   }

   @Override
   public String toString() {
     return "TokenVerifyResponse{" +
       "success=" + success +
       ", code='" + code + '\'' +
       ", msg='" + msg + '\'' +
       ", userDataVerified=" + userDataVerified +
       '}';
   }
 }

有关 UserDataVerified 类的更多信息,请参阅 获取个人资料 章节。

注意:

  • 如果您收到诸如 EXPIRED_TOKENINVALID_TOKEN 的响应,可以通过再次调用 signIn 函数请求新的token。
  • 要将当前登录的用户传递给后端服务器,应将ID token发送到您的后端服务器并验证它。请参阅 – 用后端服务器进行验证
  • 用户登录后,将登录信息保存在您应用的本地数据库或 shared preferences中,以确保用户在后续的启动中能自动继续游戏,不用重复触发登录流程。

6. 实现用户登出 -可选

本节阐释如何为您的用户实现登出。

要执行用户登出:

  • 从应用的本地数据库/shared preferences移除保存的登录信息,并再次展示登录界面。
  • 登出后,用户能继续作为游客进行游戏,或使用您游戏内的任意其它登录选项。



使用个人资料

以下部分说明了如何使用检索到的用户档案信息的过程。

在用户使用now.gg账户登录后,您便获得了 id_token。要获取在 OnTokenAcquired 中收到的用户详情,您需要调用 verifyToken 函数。
如果您的应用程序有后端服务器,并且您希望在后端获取已登录用户的个人资料,请参考此章节

提醒

  • 请勿使用用户的电子邮件地址或用户ID为当前登录的用户与您的应用程序后端服务器通信。而应将用户的ID令牌(ID token)发送到您的后端服务器,并在 服务器上验证令牌

如果您的应用程序没有后端服务器,您可以使用以下章节描述的方式来获取用户个人资料。

获取个人资料

您可以使用设备上的(on-device)API调用获取已登录用户的个人资料。为此,请使用通过调用 verifyToken 函数收到的 TokenVerifyResponse 中的 userDataVerified 字段。

UserDataVerified userData = tokenVerifyResponse.getUserDataVerified();
 String userId = userData.getUserId();
 String name = userData.getName();
 String email = userData.getEmail();
 String picture = userData.getPicture();
 
 public class UserDataVerified {
   String iss;
   String sub;
   String aud;
   String exp;
   String iat;
   String auth_time;
   String tokenId;
   String sessionId;
   String scope;

   String email;
   String name;
   String picture;
   String mobile;
   String userId;

   public UserDataVerified(String iss, String sub, String aud, String exp, String iat,
                           String auth_time, String email, String mobile, String userId,
                           String tokenId, String sessionId, String scope, String name, String picture) {
       this.iss = iss;
       this.sub = sub;
       this.aud = aud;
       this.exp = exp;
       this.iat = iat;
       this.auth_time = auth_time;
       this.email = email;
       this.mobile = mobile;
       this.userId = userId;
       this.tokenId = tokenId;
       this.sessionId = sessionId;
       this.scope = scope;
       this.name = name;
       this.picture = picture;
  }

   @Override
   public String toString() {
       return "UserDataVerified{" +
               "iss='" + iss + '\'' +
               ", sub='" + sub + '\'' +
               ", aud='" + aud + '\'' +
               ", exp='" + exp + '\'' +
               ", iat='" + iat + '\'' +
               ", auth_time='" + auth_time + '\'' +
               ", tokenId='" + tokenId + '\'' +
               ", sessionId='" + sessionId + '\'' +
               ", scope='" + scope + '\'' +
               ", email='" + email + '\'' +
               ", name='" + name + '\'' +
               ", picture='" + picture + '\'' +
               ", mobile='" + mobile + '\'' +
               ", userId='" + userId + '\'' +
               '}';
   }
 }         
一旦获取到用户详情,您应该将这些信息保存在应用程序的数据库中,并在下次用户启动应用程序时跳过登录过程。

重要信息

  • 建议不要使用电子邮件地址来鉴别用户,而应使用userId。
  • 如果您的应用程序没有后端服务器,您可以在客户端使用 userData.getUserId() 获取 userId
  • 如果您的应用程序有后端服务器,您可以调用 验证令牌API,并获取 userId 作为用户数据的响应。


×

用户帐户服务

适用于原生安卓的登录模块

用户帐户服务

文档版本 1.0

文本已复制到剪贴板
copyLinkText
有疑问?请通过以下方式联系我们: dev-support@now.gg