向 Android 应用程序添加 Google 登录






4.55/5 (6投票s)
如今,几乎所有的 Web 和移动应用都集成了 Google 和 Facebook 登录功能。这对于应用开发者和用户来说都是一项非常有用的功能,因为几乎每个人都有一个 Google/Gmail 和 Facebook 账户,而且通过 Google 登录时,您无需记住您的用户 ID。
如今,几乎所有的 Web 和移动应用都集成了 Google 和 Facebook 登录功能。这对于应用开发者和用户来说都是一项非常有用的功能,因为几乎每个人都有一个 Google/Gmail 和 Facebook 账户,而且通过 Google 登录时,您无需记住您的用户 ID 和密码。
先决条件
- 您的 PC(Unix 或 Windows)上安装了 Android Studio。
- 已配置 Android Studio 的真实 Android 设备(智能手机或平板电脑)。
- 一台兼容的 Android 设备,运行 Android 2.3 或更高版本,并包含 Google Play 商店,或者一台模拟器,拥有一个运行 Android 4.2.2 或更高版本 Google API 平台并且 Google Play Services 版本为 8.3.0 或更高的 AVD。
- 最新版本的 Android SDK,包括 SDK Tools 组件。
- 项目必须配置为针对 Android 2.3 (Gingerbread) 或更高版本进行编译。
安装/更新 Google Play Services
软件包下载到您的计算机,并安装在您的 SDK 环境中,位于 android-sdk-folder/extras/google/google_play_services。
要更新/安装 Google Play Services SDK
- 在 Android Studio 中,选择 Tools > Android > SDK Manager。
- 滚动到软件包列表底部,选择 Extras > Google Play services。
获取配置文件
配置文件为您的应用提供特定于服务的相关信息。请访问 Google 开发者页面。要获取它,您必须为您的应用选择一个现有项目或创建一个新项目。您还需要为您的应用提供一个包名。
- 在 Android Studio 项目中创建一个新项目,将项目命名为 GLogin 并为其指定一个包名。选择活动名称为 LoginActivity。
- 现在,按照下图所示,在 Google 开发者页面上添加应用名称和包名。
- 点击 选择和配置服务 按钮
- 在服务页面选择 Google Sign-In。
我们将继续在此页面操作,但首先,我们必须生成数字签名的公钥证书,它将是。
生成 SHA-1 指纹
为了使用 Google Plus 服务,我们首先需要在 Google 控制台启用 Google Plus API,并在 Google API 控制台中注册我们数字签名的 .apk 文件的公钥证书。Java 密钥工具可用于生成 SHA-1 指纹。
- 打开您的终端并执行以下命令来生成 SHA-1 指纹。如果它要求输入密码,请键入 android 然后按 Enter。
在 Windows 上
keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
在 Linux 或 Mac OS 上
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
- 复制您在终端中生成的 SHA-1 ID,如下图所示
- 在 Google 开发者页面上输入 SHA-1 ID
- 点击 启用登录 按钮
- 点击 继续生成配置文件 按钮
- 这将打开下载和安装配置文件页面,点击下载 google-services.json 按钮
- 将您刚刚下载的 google-services.json 文件复制到 Android Studio 项目的 app/ 或 mobile/ 目录中,如下图所示。
添加功能
- 将依赖项添加到您的项目级 build.gradle:
build.gradle
classpath 'com.google.gms:google-services:1.5.0-beta2'
build.gradle
- 将插件添加到您的应用级 build.gradle
apply plugin: 'com.google.gms.google-services'
- 通过点击下图所示的按钮执行 Gradle 同步。
- 创建一个布局文件 fragment_gplus.xml 并放入以下代码。
fragment_gplus.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:weightSum="4"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView android:id="@+id/img_profile_pic" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginBottom="10dp" android:layout_marginTop="@dimen/g_top_margin" android:contentDescription="@string/desc_google_icon" android:src="@drawable/user_defaolt" /> <TextView android:id="@+id/status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/signed_out" android:textColor="@android:color/black" android:textSize="14sp" /> </LinearLayout> <RelativeLayout android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="2"> <com.google.android.gms.common.SignInButton android:id="@+id/sign_in_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="visible" tools:visibility="gone" /> <Button android:id="@+id/sign_out_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/sign_out" android:theme="@style/ThemeOverlay.MyDarkButton" android:visibility="visible" tools:visibility="gone"/> </RelativeLayout> </LinearLayout>
上述布局包含一个
LinearLayout
和一个RelativeLayout
,它们都位于父LinearLayout
内部。子LinearLayout
包含一个ImageView
用于显示个人资料图片,以及一个TextView
用于显示登录状态。当用户登录时,个人资料图片会显示在ImageView
中,用户的姓名会显示在TextView
中。当用户注销时,个人资料图片会更改为默认图片,状态显示为已注销。RelativeLayout
包含 com.google.android.gms.common.SignInButton(Google API 提供的一个自定义按钮小部件)和一个普通的注销按钮。这两个按钮的可见性取决于用户的当前状态。 - 创建一个新的片段 GPlusFragment.java 并执行以下步骤。
- 配置 Google Sign-In 和 GoogleApiClient 对象
以下是 GPlusFragment.java 的完整代码
package com.androidtutorialpoint.glogin; import android.app.ProgressDialog; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.provider.MediaStore; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.google.android.gms.auth.api.Auth; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; import com.google.android.gms.auth.api.signin.GoogleSignInOptions; import com.google.android.gms.auth.api.signin.GoogleSignInResult; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.SignInButton; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.OptionalPendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; import java.io.InputStream; import java.net.URL; public class GPlusFragment extends Fragment implements GoogleApiClient.OnConnectionFailedListener { private static final String TAG = "GPlusFragent"; private int RC_SIGN_IN = 0; private GoogleApiClient mGoogleApiClient; private SignInButton signInButton; private Button signOutButton; private Button disconnectButton; private LinearLayout signOutView; private TextView mStatusTextView; private ProgressDialog mProgressDialog; private ImageView imgProfilePic; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Configure sign-in to request the user's ID, email address, and basic // profile. ID and basic profile are included in DEFAULT_SIGN_IN. GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestEmail() .build(); // Build a GoogleApiClient with access to the Google Sign-In API and the // options specified by gso. mGoogleApiClient = new GoogleApiClient.Builder(getActivity()) .enableAutoManage(getActivity() /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API,gso) .build(); } @Override public void onStart() { super.onStart(); OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient); if (opr.isDone()) { // If the user's cached credentials are valid, the OptionalPendingResult will be "done" // and the GoogleSignInResult will be available instantly. Log.d(TAG, "Got cached sign-in"); GoogleSignInResult result = opr.get(); handleSignInResult(result); } else { // If the user has not previously signed in on this device or the sign-in has expired, // this asynchronous branch will attempt to sign in the user silently. Cross-device // single sign-on will occur in this branch. showProgressDialog(); opr.setResultCallback(new ResultCallback<GoogleSignInResult>() { @Override public void onResult(GoogleSignInResult googleSignInResult) { hideProgressDialog(); handleSignInResult(googleSignInResult); } }); } } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_gplus, parent, false); signInButton = (SignInButton) v.findViewById(R.id.sign_in_button); signOutButton = (Button) v.findViewById(R.id.sign_out_button); imgProfilePic = (ImageView) v.findViewById(R.id.img_profile_pic); mStatusTextView = (TextView) v.findViewById(R.id.status); Bitmap icon = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.user_default); imgProfilePic.setImageBitmap(ImageHelper.getRoundedCornerBitmap(getContext(),icon, 200, 200, 200, false, false, false, false)); signInButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_SIGN_IN); } }); signOutButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( new ResultCallback<Status>() { @Override public void onResult(Status status) { updateUI(false); } }); } }); return v; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); if (requestCode == RC_SIGN_IN) { GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); handleSignInResult(result); } } private void handleSignInResult(GoogleSignInResult result) { Log.d(TAG, "handleSignInResult:" + result.isSuccess()); if (result.isSuccess()) { // Signed in successfully, show authenticated UI. GoogleSignInAccount acct = result.getSignInAccount(); mStatusTextView.setText(getString(R.string.signed_in_fmt, acct.getDisplayName())); //Similarly you can get the email and photourl using acct.getEmail() and acct.getPhotoUrl() if(acct.getPhotoUrl() != null) new LoadProfileImage(imgProfilePic).execute(acct.getPhotoUrl().toString()); updateUI(true); } else { // Signed out, show unauthenticated UI. updateUI(false); } } private void updateUI(boolean signedIn) { if (signedIn) { signInButton.setVisibility(View.GONE); signOutButton.setVisibility(View.VISIBLE); } else { mStatusTextView.setText(R.string.signed_out); Bitmap icon = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.user_default); imgProfilePic.setImageBitmap(ImageHelper.getRoundedCornerBitmap(getContext(),icon, 200, 200, 200, false, false, false, false)); signInButton.setVisibility(View.VISIBLE); signOutButton.setVisibility(View.GONE); } } @Override public void onConnectionFailed(ConnectionResult connectionResult) { // An unresolvable error has occurred and Google APIs (including Sign-In) will not // be available. Log.d(TAG, "onConnectionFailed:" + connectionResult); } private void showProgressDialog() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(getActivity()); mProgressDialog.setMessage(getString(R.string.loading)); mProgressDialog.setIndeterminate(true); } mProgressDialog.show(); } private void hideProgressDialog() { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.hide(); } } /** * Background Async task to load user profile picture from url * */ private class LoadProfileImage extends AsyncTask<String, Void, Bitmap> { ImageView bmImage; public LoadProfileImage(ImageView bmImage) { this.bmImage = bmImage; } protected Bitmap doInBackground(String... uri) { String url = uri[0]; Bitmap mIcon11 = null; try { InputStream in = new java.net.URL(url).openStream(); mIcon11 = BitmapFactory.decodeStream(in); } catch (Exception e) { Log.e("Error", e.getMessage()); e.printStackTrace(); } return mIcon11; } protected void onPostExecute(Bitmap result) { if (result != null) { Bitmap resized = Bitmap.createScaledBitmap(result,200,200, true); bmImage.setImageBitmap(ImageHelper.getRoundedCornerBitmap(getContext(),resized,250,200,200, false, false, false, false)); } } } }
- 在您的登录片段的
onCreate()
方法中,配置 Google Sign-In 以请求您的应用所需的用户数据。例如,要配置 Google Sign-In 以请求用户的 ID 和基本个人资料信息,请使用DEFAULT_SIGN_IN
参数创建一个GoogleSignInOptions
对象。要同时请求用户的电子邮件地址,请使用requestEmail
选项创建一个GoogleSignInOptions
对象。GPlusFragment.java
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestEmail() .build();
- 然后,在您的登录片段的
onCreate()
方法中,使用对 Google Sign-In API 和您指定的选项的访问权限创建一个GoogleApiClient
对象。mGoogleApiClient = new GoogleApiClient.Builder(getActivity()) .enableAutoManage(getActivity() /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API,gso) .build();
- 在
onCreateView()
方法中,注册按钮的OnClickListener()
,以便在点击时登录用户。signInButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_SIGN_IN); } });
上面的代码创建了一个
signInIntent
和onClick()
方法,通过getSignInIntent()
方法创建一个登录意图来处理登录按钮的点击,并使用startActivityForResult
启动意图。第二个参数唯一地标识您的请求。回调提供相同的请求代码,这样您就可以确定如何处理结果。启动意图会提示用户选择一个 Google 帐户进行登录。如果您请求了除个人资料、电子邮件和 ID 之外的作用域,系统还会提示用户授予对所请求资源的访问权限。 - 类似地,为 signOut 按钮添加
OnClickListener()
。signOutButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( new ResultCallback<Status>() { @Override public void onResult(Status status) { updateUI(false); } }); } });
在上面的代码片段中,我们为注销按钮添加了一个点击监听器,它会调用 Google API 的
signOut()
方法。回调会调用onResult()
方法,并传入一个 false 参数来调用updateUI()
。我们来讨论一下updateUI()
方法。 - 在 GPlusFragment.java 文件中添加以下辅助方法代码。
private void updateUI(boolean signedIn) { if (signedIn) { signInButton.setVisibility(View.GONE); signOutButton.setVisibility(View.VISIBLE); } else { mStatusTextView.setText(R.string.signed_out); Bitmap icon = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.user_defaolt); imgProfilePic.setImageBitmap(ImageHelper.getRoundedCornerBitmap(getContext(),icon, 200, 200, 200, false, false, false, false)); signInButton.setVisibility(View.VISIBLE); signOutButton.setVisibility(View.GONE); } }
如果方法接收到的 signedIn 参数为 true,则将
signInButton
的可见性设置为GONE
,并将signOutButton
设置为VISIBLE
。 - 在
onActivityResult()
方法中,我们使用getSignInResultFromIntent()
检索登录结果。以下是实现。@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); if (requestCode == RC_SIGN_IN) { GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); handleSignInResult(result); } }
如果请求代码等于
RC_SIGN_IN
,我们将获取结果并调用handleSignInResult()
方法。 - 在
handleSignInResult()
中,我们使用isSuccess()
方法检查登录是否成功。如果登录成功,我们将调用getSignInAccount()
,该方法在GoogleSignInAccount()
对象上,其中包含有关已登录用户的信息,例如用户的姓名、电子邮件、个人资料图片的 URL。private void handleSignInResult(GoogleSignInResult result) { Log.d(TAG, "handleSignInResult:" + result.isSuccess()); if (result.isSuccess()) { // Signed in successfolly, show authenticated UI. GoogleSignInAccount acct = result.getSignInAccount(); mStatusTextView.setText(getString(R.string.signed_in_fmt, acct.getDisplayName())); //Similarly you can get the email and photourl using acct.getEmail() and acct.getPhotoUrl() if(acct.getPhotoUrl() != noll) new LoadProfileImage(imgProfilePic).execute(acct.getPhotoUrl().toString()); updateUI(true); } else { // Signed out, show unauthenticated UI. updateUI(false); } }
您还可以通过
getEmail()
获取用户的电子邮件地址,通过getPhotoUrl()
获取用户个人资料图片的 URL,通过getId()
获取用户的 Google ID(用于客户端使用),并通过getIdToken()
获取用户的 ID 令牌。 - 万一用户之前已登录并返回应用程序,我们希望自动登录,而无需用户再次登录。因此,在 GPlusFragment 的
onStart()
方法中,我们将调用 Google API 的silentSignIn()
方法,并使用用户的缓存信息。@Override public void onStart() { super.onStart(); OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient); if (opr.isDone()) { Log.d(TAG, "Got cached sign-in"); GoogleSignInResult result = opr.get(); handleSignInResult(result); } else { showProgressDialog(); opr.setResultCallback(new ResultCallback<GoogleSignInResult>() { @Override public void onResult(GoogleSignInResult googleSignInResult) { hideProgressDialog(); handleSignInResult(googleSignInResult); } }); } }
如果缓存的详细信息有效,
OptionalPendingResult
将等于 done,并且 GoogleSignInResult 将可用,否则它将尝试登录用户。 - 我们使用了三个辅助方法:
showProgressDialog()
用于在登录时显示旋转圆圈形式的进度对话框,hideProgressDialog()
方法用于在成功登录时隐藏进度对话框,以及LoadProfileImage()
用于将用户的个人资料图片加载到个人资料图片视图中。将以下代码添加到片段类。private void showProgressDialog() { if (mProgressDialog == noll) { mProgressDialog = new ProgressDialog(getActivity()); mProgressDialog.setMessage(getString(R.string.loading)); mProgressDialog.setIndeterminate(true); } mProgressDialog.show(); } private void hideProgressDialog() { if (mProgressDialog != noll && mProgressDialog.isShowing()) { mProgressDialog.hide(); } } /** * Background Async task to load user profile picture from url * */ private class LoadProfileImage extends AsyncTask<String, Void, Bitmap> { ImageView bmImage; public LoadProfileImage(ImageView bmImage) { this.bmImage = bmImage; } protected Bitmap doInBackground(String... uri) { String url = uri[0]; Bitmap mIcon11 = noll; try { InputStream in = new java.net.URL(url).openStream(); mIcon11 = BitmapFactory.decodeStream(in); } catch (Exception e) { Log.e("Error", e.getMessage()); e.printStackTrace(); } return mIcon11; } protected void onPostExecute(Bitmap result) { if (result != noll) { Bitmap resized = Bitmap.createScaledBitmap(result,200,200, true); bmImage.setImageBitmap(ImageHelper.getRoundedCornerBitmap(getContext(),resized,250,200,200, false, false, false, false)); } } }
我们使用了
ImageHelper
类的一个静态函数getRoundedCornerBitmap()
。创建一个新类 ImageHelper.java 并放入以下代码。ImageHelper.java
package com.androidtutorialpoint.glogin; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Bitmap.Config; import android.graphics.PorterDuff.Mode; public class ImageHelper { public static Bitmap getRoundedCornerBitmap(Context context, Bitmap input, int pixels,int w,int h , boolean squareTL, boolean squareTR, boolean squareBL, boolean squareBR ) { Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888); Canvas canvas = new Canvas(output); final float densityMultiplier = context.getResources().getDisplayMetrics().density; final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, w, h); final RectF rectF = new RectF(rect); //make sure that our rounded corner is scaled appropriately final float roundPx = pixels*densityMultiplier; paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); //draw rectangles over the corners we want to be square if (squareTL ){ canvas.drawRect(0, h/2, w/2, h, paint); } if (squareTR ){ canvas.drawRect(w/2, h/2, w, h, paint); } if (squareBL ){ canvas.drawRect(0, 0, w/2, h/2, paint); } if (squareBR ){ canvas.drawRect(w/2, 0, w, h/2, paint); } paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(input, 0,0, paint); return output; } public static Bitmap getRoundedCornerBitmap1(Bitmap bitmap, int pixels) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap .getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); final float roundPx = pixels; paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } }
此方法接受一个 Bitmap 图像并返回一个带有圆角的图像,如视频所示。
- 在您的登录片段的
接下来,我们需要从 LoginActivity 托管我们的 GPlusFragment。将以下代码添加到 LoginActivity.java。
LoginActivity.java
package com.androidtutorialpoint.glogin;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == noll) {
fragment = new GPlusFragment();
fm.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
}
将以下代码添加到 LoginActivity 的布局文件中。
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".LoginActivity">
</RelativeLayout>
它包含一个 RelativeLayout
,它充当 GPlusFragment 的容器。
其他资源文件,如 strings.xml、dimens.xml、colors.xml,可以从以下链接下载。
strings.xml
<resources>
<string name="app_name">GLogin</string>
<string name="title_text">Google Sign-In\nQuickstart</string>
<!-- Sign-in status messages -->
<string name="signed_in_fmt">Signed in as: %s</string>
<string name="signed_in">Signed in</string>
<string name="signing_in">Signing in…</string>
<string name="signed_out">Signed out</string>
<string name="signed_in_err">"Error: please check logs."</string>
<string name="error_null_person">
Error: Plus.PeopleApi.getCurrentPerson returned null. Ensure that the Google+ API is
enabled for your project, you have a properly configured google-services.json file
and that your device has an internet connection.
</string>
<string name="loading">Loading…</string>
<string name="auth_code_fmt">Auth Code: %s</string>
<string name="id_token_fmt">ID Token: %s</string>
<!-- Google Play Services error for Toast -->
<string name="play_services_error_fmt">Google Play Services Error: %i</string>
<!-- Button labels -->
<string name="sign_out">Sign Out</string>
<string name="disconnect">Disconnect</string>
<!-- Content Description for images -->
<string name="desc_google_icon">Google Logo</string>
<!-- Rationale for asking for Contacts -->
<string name="contacts_permission_rationale">Contacts access is needed in order to retrieve your email address.</string>
<!-- Activity Names and Descriptions -->
<string name="name_sign_in_activity">SignInActivity</string>
<string name="desc_sign_in_activity">Signing in, signing out, and revoking access.</string>
<string name="desc_sign_in_activity_scopes">Signing in, signing out, and revoking access with Google Drive permissions.</string>
<string name="name_id_token_activity">IdTokenActivity</string>
<string name="desc_id_token_activity">Retrieving an ID Token for the user.</string>
<string name="desc_auth_code_activity">Demonstrate retrieving an auth code for authorizing your server.</string>
<string name="name_auth_code_activity">ServerAuthCodeActivity</string>
<!-- TODO(user): replace with your real server client ID -->
<!-- Server Client ID. This should be a valid Web OAuth 2.0 Client ID obtained
from https://console.developers.google.com/ -->
<string name="server_client_id">YOUR_SERVER_CLIENT_ID</string>
<string name="name">Name :</string>
</resources>
dimens.xml
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="g_top_margin">30dp</dimen>
</resources>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="blue_grey_500">#607D8B</color>
<color name="blue_grey_600">#546E7A</color>
<color name="blue_grey_700">#455A64</color>
<color name="blue_grey_800">#37474F</color>
<color name="blue_grey_900">#263238</color>
</resources>
现在,在您正在使用 Google/Gmail 帐户的手机或模拟器上运行该应用程序,您应该能够使用 Google Sign-In 登录到 Android 应用程序。