按照此页面的步骤将now.gg登录集成到您的Android应用中,配置并将now.gg登录按钮添加到您的应用布局中,以启动登录流程。
确保您已检查了 前提条件清单 并设置好了开发者环境。
首先添加权限到 AndroidManifest.xml
。
<uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.INTERNET"/>
添加以下依赖项到您应用程序的 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'
now.gg登录按钮的资源(assets)包含于 now.gg SDK包 中,位于 Assets/Login
目录下。
Button signInButton = findViewById(R.id.now_gg_login_button);
在Android activity的 onCreate
方法中为登录按钮添加 OnClickListener
。
findViewById(R.id.now_gg_login_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { signIn(); } });
您现在应检查 GET_ACCOUNTS
权限和结果。
@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(); } } }
以下示例代码用于“登录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"); } } };
用户登录后,您将在 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(); } } }
现在您需要通过调用 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_TOKEN
或 INVALID_TOKEN
的响应,可以通过再次调用 signIn
函数请求新的token。本节阐释如何为您的用户实现登出。
要执行用户登出:
以下部分说明了如何使用检索到的用户档案信息的过程。
id_token
。要获取在 OnTokenAcquired
中收到的用户详情,您需要调用 verifyToken
函数。如果您的应用程序没有后端服务器,您可以使用以下章节描述的方式来获取用户个人资料。
您可以使用设备上的(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 + '\'' + '}'; } }
userData.getUserId()
获取 userId
。userId
作为用户数据的响应。