按照此页面的步骤将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 > 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 作为用户数据的响应。用户帐户服务
用户帐户服务
文档版本 1.0