Android - вход в Google с помощью входа в систему вместо автоматического входа в систему
Я добавил в Google приложение для входа в Google (шаги можно найти в редакции 3 этого сообщения). Все работает так, как должно, но я бы хотел внести небольшие корректировки. С текущим кодом, найденным в редакторе 3, вы автоматически регистрируетесь при каждом запуске приложения без экрана входа в систему. Я хочу отключить это, так как приложение будет использоваться на планшете на работе, где каждый сотрудник должен Войти в приложение каждый день.
Я начал с удаления mGoogleApiClient.connect();
из метода onStart()
, и теперь у меня снова есть кнопка входа в Google. Когда я добавляю mGoogleApiClient.connect();
в signInWithGoogle()
, я могу войти в систему с запоминающимся пользователем.
Я хочу прямо сейчас - это экран Google Login по умолчанию, где вы можете заполнить свой адрес электронной почты и пароль Google каждый раз, когда вы нажимаете кнопку "Логин", вместо того, чтобы просто войти в систему с сохраненным пользователем. (PS: Имейте в виду, что на моем устройстве Android у меня есть только один пользователь в Settings -> Google Accounts
, возможно, поэтому он автоматически регистрируется, а не дает возможность выбрать, какая учетная запись должна быть подключена.)
Я проверю, изменится ли у меня несколько учетных записей Google на моем устройстве Android. Хорошо, я добавил вторую учетную запись Google на свое Android-устройство, но мое приложение по-прежнему автоматически регистрируется в запоминающемся пользователе, когда я нажимаю на Sign-In..
ИЗМЕНИТЬ 2:
Я еще не смог найти решение для своей проблемы.
Я нашел несколько учебных руководств с различными способами входа в Google, например, с помощью AccountManager
, чтобы пользователь мог выбрать одну из существующих учетных записей Google на устройстве. (Сегодня я только читал об этом методе, поэтому для этого пока нет примеров кода. Но это также не то, что я ищу в любом случае.)
Я, вероятно, уже разъяснил это сообщение выше, но это то, что я хочу объяснить на фотографиях:
- Пользователь запускает приложение на своем Android-устройстве.
- Пользователь вставляет свое имя пользователя своей учетной записи Google (используемое письмо) и пароль
![List item]()
- После успешного входа в систему, мы можем делать другие вещи с помощью приложения
PS: Чтобы убедиться, что этот экран входа в систему - это сам Google. Так что это не экран входа, созданный мной. Это теоретически позволит мне сохранить введенный пароль, что противоречит протоколу Google OAuth.
ИЗМЕНИТЬ 3 (Код):
Шаги, которые я сделал для работы Google Services до сих пор, приведены ниже. Теперь мне просто нужно выяснить, как заставить экран входа в систему или полностью выйти из системы, что приводит к экрану входа в систему каждый раз.
Я выполнил следующий учебник:
http://www.androidhive.info/2014/02/android-login-with-google-plus-account-1/
С дополнительной информацией, используемой в следующих учебниках/сайтах:
Сгенерирован следующий код:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testproject_gmaillogin"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
<activity
android:name="com.example.testproject_gmaillogin.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">TestProject_GmailLogin</string>
<string name="action_settings">Settings</string>
<string name="profile_pic_description">Google Profile Picture</string>
<string name="btn_logout_from_google">Logout from Google</string>
<string name="btn_revoke_access">Revoke Access</string>
</resources>
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity" >
<LinearLayout
android:id="@+id/profile_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:orientation="horizontal"
android:weightSum="3"
android:visibility="gone">
<ImageView
android:id="@+id/img_profile_pic"
android:contentDescription="@string/profile_pic_description"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="vertical"
android:layout_weight="2" >
<TextView
android:id="@+id/txt_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:textSize="20sp" />
<TextView
android:id="@+id/txt_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<com.google.android.gms.common.SignInButton
android:id="@+id/btn_sign_in"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"/>
<Button
android:id="@+id/btn_sign_out"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/btn_logout_from_google"
android:visibility="gone"
android:layout_marginBottom="10dp"/>
<Button
android:id="@+id/btn_revoke_access"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/btn_revoke_access"
android:visibility="gone" />
</LinearLayout>
MainActivity.java:
package com.example.testproject_gmaillogin;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.plus.Plus;
import com.google.android.gms.plus.model.people.Person;
import android.support.v7.app.ActionBarActivity;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity implements ConnectionCallbacks, OnConnectionFailedListener, OnClickListener
{
// Logcat tag
private static final String TAG = "MainActivity";
// Profile pix image size in pixels
private static final int PROFILE_PIC_SIZE = 400;
// Request code used to invoke sign in user interactions
private static final int RC_SIGN_IN = 0;
// Client used to interact with Google APIs
private GoogleApiClient mGoogleApiClient;
// A flag indicating that a PendingIntent is in progress and prevents
// us from starting further intents
private boolean mIntentInProgress;
// Track whether the sign-in button has been clicked so that we know to resolve
// all issues preventing sign-in without waiting
private boolean mSignInClicked;
// Store the connection result from onConnectionFailed callbacks so that we can
// resolve them when the user clicks sign-in
private ConnectionResult mConnectionResult;
// The used UI-elements
private SignInButton btnSignIn;
private Button btnSignOut, btnRevokeAccess;
private ImageView imgProfilePic;
private TextView txtName, txtEmail;
private LinearLayout profileLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get the UI-elements
btnSignIn = (SignInButton) findViewById(R.id.btn_sign_in);
btnSignOut = (Button) findViewById(R.id.btn_sign_out);
btnRevokeAccess = (Button) findViewById(R.id.btn_revoke_access);
imgProfilePic = (ImageView) findViewById(R.id.img_profile_pic);
txtName = (TextView) findViewById(R.id.txt_name);
txtEmail = (TextView) findViewById(R.id.txt_email);
profileLayout = (LinearLayout) findViewById(R.id.profile_layout);
// Set the Button onClick-listeners
btnSignIn.setOnClickListener(this);
btnSignOut.setOnClickListener(this);
btnRevokeAccess.setOnClickListener(this);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Plus.API, null)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.build();
}
@Override
protected void onStart(){
super.onStart();
mGoogleApiClient.connect(); // <- REMOVED (EDIT 4: Added again)
}
@Override
protected void onStop(){
super.onStop();
if(mGoogleApiClient.isConnected())
mGoogleApiClient.disconnect();
}
@Override
public void onClick(View view){
switch(view.getId()){
case R.id.btn_sign_in:
signInWithGPlus();
break;
case R.id.btn_sign_out:
signOutFromGPlus();
break;
case R.id.btn_revoke_access:
revokeGPlusAccess();
break;
}
}
@Override
public void onConnectionFailed(ConnectionResult result) {
if(!result.hasResolution()){
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
return;
}
if(!mIntentInProgress){
// Store the ConnectionResult so that we can use it later when the user clicks 'sign-in'
mConnectionResult = result;
if(mSignInClicked)
// The user has already clicked 'sign-in' so we attempt to resolve all
// errors until the user is signed in, or they cancel
resolveSignInErrors();
}
}
@Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent){
if(requestCode == RC_SIGN_IN && responseCode == RESULT_OK)
SignInClicked = true;
mIntentInProgress = false;
if(!mGoogleApiClient.isConnecting())
mGoogleApiClient.connect();
}
}
@Override
public void onConnected(Bundle connectionHint) {
mSignInClicked = false;
Toast.makeText(this, "User is connected!", Toast.LENGTH_LONG).show();
// Get all the user information
getProfileInformation();
// Update the UI after sign-in
updateUI(true);
}
@Override
public void onConnectionSuspended(int cause){
mGoogleApiClient.connect();
updateUI(false);
}
// Updating the UI, showing/hiding buttons and profile layout
private void updateUI(boolean isSignedIn){
if(isSignedIn){
btnSignIn.setVisibility(View.GONE);
btnSignOut.setVisibility(View.VISIBLE);
btnRevokeAccess.setVisibility(View.VISIBLE);
profileLayout.setVisibility(View.VISIBLE);
}
else{
btnSignIn.setVisibility(View.VISIBLE);
btnSignOut.setVisibility(View.GONE);
btnRevokeAccess.setVisibility(View.GONE);
profileLayout.setVisibility(View.GONE);
}
}
// Sign-in into Google
private void signInWithGPlus(){
//if(!mGoogleApiClient.isConnecting()) // <- ADDED (EDIT 4: Removed again)
//mGoogleApiClient.connect(); // <- ADDED (EDIT 4: Removed again)
if(!mGoogleApiClient.isConnecting()){
mSignInClicked = true;
resolveSignInErrors();
}
}
// Method to resolve any sign-in errors
private void resolveSignInErrors(){
if(mConnectionResult.hasResolution()){
try{
mIntentInProgress = true;
//Toast.makeText(this, "Resolving Sign-in Errors", Toast.LENGTH_SHORT).show();
mConnectionResult.startResolutionForResult(this, RC_SIGN_IN);
}
catch(SendIntentException e){
// The intent was cancelled before it was sent. Return to the default
// state and attempt to connect to get an updated ConnectionResult
mIntentInProgress = false;
mGoogleApiClient.connect();
}
}
}
// Fetching the user infromation name, email, profile pic
private void getProfileInformation(){
try{
if(Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null){
Person currentPerson = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);
String personName = currentPerson.getDisplayName();
String personPhotoUrl = currentPerson.getImage().getUrl();
String personGooglePlusProfile = currentPerson.getUrl();
String personEmail = Plus.AccountApi.getAccountName(mGoogleApiClient);
Log.e(TAG, "Name: " + personName + ", "
+ "plusProfile: " + personGooglePlusProfile + ", "
+ "email: " + personEmail + ", "
+ "image: " + personPhotoUrl);
txtName.setText(personName);
txtEmail.setText(personEmail);
// by default the profile url gives 50x50 px image,
// but we can replace the value with whatever dimension we
// want by replacing sz=X
personPhotoUrl = personPhotoUrl.substring(0, personPhotoUrl.length() - 2)
+ PROFILE_PIC_SIZE;
new LoadProfileImage(imgProfilePic).execute(personPhotoUrl);
}
else{
Toast.makeText(getApplicationContext(), "Person information is null", Toast.LENGTH_LONG).show();
}
}
catch(Exception ex){
ex.printStackTrace();
}
}
// Sign-out from Google
private void signOutFromGPlus(){
if(mGoogleApiClient.isConnected()){
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
mGoogleApiClient.disconnect();
mGoogleApiClient.connect();
updateUI(false);
}
}
// Revoking access from Google
private void revokeGPlusAccess(){
if(mGoogleApiClient.isConnected()){
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient)
.setResultCallback(new ResultCallback<Status>(){
@Override
public void onResult(Status s){
Log.e(TAG, "User access revoked!");
mGoogleApiClient.connect();
updateUI(false);
}
});
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings)
return true;
return super.onOptionsItemSelected(item);
}
}
LoadProfileImage.java:
package com.example.testproject_gmaillogin;
import java.io.InputStream;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;
/**
* Background async task to load user profile picture from url
**/
public class LoadProfileImage extends AsyncTask<String, Void, Bitmap> {
private ImageView bmImage;
public LoadProfileImage(ImageView bmImage){
this.bmImage = bmImage;
}
@Override
protected Bitmap doInBackground(String... urls){
String urlDisplay = urls[0];
Bitmap mIcon11 = null;
try{
InputStream in = new java.net.URL(urlDisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
}
catch(Exception ex){
Log.e("Error", ex.getMessage());
ex.printStackTrace();
}
return mIcon11;
}
@Override
protected void onPostExecute(Bitmap result){
bmImage.setImageBitmap(result);
}
}
Другие шаги, которые я сделал, были:
В https://console.developers.google.com/project Я создал проект с помощью:
API Google+:
![Google+ API on]()
И идентификатор клиента, созданный с правильным SHA1 и тем же самым пространством имен, что и проект:
![And a Client ID created with the correct SHA1]()
В Eclipse:
Я установил библиотеку google-play-services:
![Google-play services installed]()
И добавил его в проект:
![Google-play services library added (2)]()
![Google-play services library added (2)]()
Я также создал эмулятор с версией Google 4.4.2 (а не Android 4.4.2), а также изменил проект на Google 4.4.2 вместо Android 4.4.2:
![Solution Error]()
![Solution Error Emulator]()
ИЗМЕНИТЬ 4:
Хорошо, у меня есть временное решение для моего собственного дела. В моем случае приложение, которое я создаю, должно работать на планшете, который явно используется для моего приложения. Поскольку это так, я могу удалить все учетные записи Google из настроек устройства в тот момент, когда кто-то отменяет доступ (как функция выхода из системы).
Я начал с удаления предыдущих изменений (добавил .connect();
в onStart()
и удалил его из signInWithGPlus()
)
Затем я добавил одну строку к методу revokeGPlusAccess
:
// Revoking access from Google
private void revokeGPlusAccess(){
if(mGoogleApiClient.isConnected()){
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient)
.setResultCallback(new ResultCallback<Status>(){
@Override
public void onResult(Status s){
Log.e(TAG, "User access revoked!");
removeAllGoogleAccountsFromDevice(); // <- Added
mGoogleApiClient.connect();
updateUI(false);
}
});
}
}
Следующим методом:
// Method to remove ALL Google Accounts from the Android Device
private void removeAllGoogleAccountsFromDevice(){
// Ask if this really is what you want
new AlertDialog.Builder(MainActivity.mActivity)
.setMessage("Are you sure you want to delete all Google Accounts from this Android Device?\r\n\r\n" +
"WARNING: If you run this app on the Work Tablet, click YES. If you run this on your own device, it recommended to click NO.")
.setCancelable(false)
.setPositiveButton("Yes, continue", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// AccountManager is final because we use it in the separate Thread below
final AccountManager accountManager = AccountManager.get(MainActivity.this);
Account[] googleAccounts = accountManager.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
// Account is final because we use it in the separate Thread below
for(final Account a : googleAccounts){
// Separate Thread because AccountManager #removeAccount is an async operation
Thread worker = new Thread(new Runnable() {
@Override
public void run() {
accountManager.removeAccount(a, null, null);
}
});
worker.start();
}
}
})
.setNegativeButton("No", null)
.show();
}
Тем не менее, это работает только в моем случае. Это не будет работать на персональном устройстве, где вы, очевидно, не хотите удалять все учетные записи устройств Google. Я все равно хотел бы знать решение, когда вы не хотите удалять учетные записи устройств Google.
Ответы
Ответ 1
Правильный ответ здесь.
Для очистки ранее кэшированной учетной записи нужно позвонить mClient.clearDefaultAccountAndReconnect()
.
Это хорошая практика делать это каждый раз, когда пользователь нажимает кнопку входа в систему, чтобы каждый пользователь показывал все свои учетные записи каждый раз.
Ответ 2
Попробуйте выписать пользователя, а затем снова подписать пользователя. Если вы хотите заставить пользователя входить в систему каждый раз, вы можете просто подписывать их, когда они выходят из приложения. Я не рекомендую вам изменять поведение входа в приложение, потому что это может удивить пользователя.
Убедитесь, что ваша среда разработчика настроена правильно с помощью примера Google Quickstart Google+. Если поведение signin в примере приложения совпадает с тем, что вы видите, либо возникает проблема с вашей средой разработки (например, версия GMS, версия Android API), либо поведение кнопки входа не является тем, что вы ожидаете от нее.
Наконец, вы можете извлечь выгоду из этой статьи, которая охватывает несколько распространенных проблемных мест с Android Sign-In. Он охватывает высокоуровневое представление различных шагов авторизации и разрешения в потоке входа Android.
Ответ 3
Выйти не поможет решить вашу проблему. вы должны отменить доступ каждый раз, когда вы выходите из приложения или когда вы хотите вывести из приложения и снова войти в систему. поэтому просто вызовите метод отозвать доступ в обратном методе при выходе из приложения, как показано ниже:
@Override
public void onBackPressed() {
Log.d("CDA", "onBackPressed Called");
revokeGplusAccess();
super.onBackPressed();
}
Это определенно решит вашу проблему.