Android – вход в Google с помощью входа в систему вместо автоматического входа в систему

Я добавил в Google приложение для входа в Google (шаги можно найти в редакции 3 этого сообщения). Все работает так, как должно, но я бы хотел внести небольшие корректировки. С помощью текущего кода, найденного в редакторе 3, вы автоматически регистрируетесь при каждом запуске приложения без экрана входа в систему. Я хочу отключить это, так как приложение будет использоваться на планшете на работе, где каждый сотрудник должен войти в приложение каждый день.

Я начал с удаления mGoogleApiClient.connect(); Из onStart() , и теперь у меня снова кнопка Google Login. Когда я добавляю mGoogleApiClient.connect(); К signInWithGoogle() Я могу войти в систему с запоминающимся пользователем.

Я хочу прямо сейчас – это экран Google Login по умолчанию, в котором вы можете заполнить свой адрес электронной почты и пароль Google каждый раз, когда вы нажимаете кнопку «Логин», вместо того, чтобы просто войти в систему с сохраненным пользователем. (PS: Имейте в виду, что на моем устройстве Android у меня в настоящее время есть только один пользователь в Settings -> Google Accounts , возможно, поэтому он автоматически регистрируется, а не дает возможность выбрать, с какой учетной записью следует подключаться.)

Я буду тестировать, если это изменится, когда у меня есть несколько учетных записей Google на моем устройстве Android. Итак, я добавил вторую учетную запись Google на свое Android-устройство, но мое приложение по-прежнему автоматически регистрируется у запоминающегося пользователя, когда я нажимаю «Войти».


EDIT 2:

Я еще не смог найти решение для своей проблемы.

Я нашел несколько учебных руководств с различными способами входа в Google, например, с помощью AccountManager чтобы пользователь мог выбрать одну из существующих учетных записей Google на устройстве. (Я только читал об этом методе сегодня, поэтому пока нет примеров кода для этого. Но это также не то, что я ищу в любом случае.)

Я, вероятно, уже разъяснил это сообщение выше, но это то, что я хочу объяснить на фотографиях:

  1. Пользователь запускает приложение на своем Android-устройстве.
  2. Пользователь вводит имя пользователя своей учетной записи Google (используемое e-mail) и пароль Элемент списка
  3. После того, как пользователь успешно войдет в систему, мы можем делать другие вещи с помощью приложения

PS: Чтобы убедиться, этот экран входа в систему – это сам Google. Так что это не экран входа, созданный мной. Это теоретически позволит мне сохранить введенный пароль, что противоречит протоколу Google OAuth.


EDIT 3 (код):

Шаги, которые я сделал, чтобы сделать работу с Google Services до сих пор, ниже. Теперь мне просто нужно выяснить, как заставить экран входа в систему или полностью выйти из системы, что приводит к экрану входа в систему каждый раз.

Я выполнил следующий учебник: http://www.androidhive.info/2014/02/android-login-with-google-plus-account-1/

С дополнительной информацией, используемой в следующих учебниках / сайтах:

  • https://developers.google.com/+/mobile/android/getting-started
  • https://developers.google.com/+/mobile/android/sign-in
  • http://developer.android.com/google/play-services/setup.html#Setup

Это создало следующий код:

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's 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's 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+:

API Google+

И идентификатор клиента, созданный с правильным SHA1 и тем же самым пространством имен, что и проект:

И идентификатор клиента, созданный с правильным SHA1

На Eclipse:

Я установил библиотеку google-play-services:

Установлены сервисы Google Play

И добавил его в проект:

Добавлена ​​библиотека услуг Google Play (2)Добавлена ​​библиотека услуг Google Play (2)

Я также создал эмулятор с версией Google 4.4.2 (а не Android 4.4.2), а также изменил проект на Google 4.4.2 вместо Android 4.4.2:

Ошибка решенияЭмулятор ошибки решения


EDIT 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's 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 для устройств.

Правильный ответ здесь .

Для очистки ранее кэшированной учетной записи нужно вызвать mClient.clearDefaultAccountAndReconnect() .
Это хорошая практика делать это каждый раз, когда пользователь нажимает кнопку входа в систему, чтобы каждый пользователь показывал все свои учетные записи каждый раз.

Попробуйте выписать пользователя, а затем снова подписать пользователя. Если вы хотите заставить пользователя войти в систему каждый раз, вы можете просто подписывать их, когда они выходят из приложения. Я не рекомендую вам изменять поведение входа в приложение, потому что это может удивить пользователя.

Убедитесь, что ваша среда разработчика настроена правильно, запустив образец Google+ для Android Quickstart . Если поведение signin в примере приложения совпадает с тем, что вы видите, либо есть проблема с вашей средой разработки (например, версия GMS, версия Android API), либо поведение кнопки входа не является тем, что вы ожидаете от нее ,

Наконец, вы можете извлечь выгоду из этой статьи, которая охватывает несколько распространенных проблемных мест с Android Sign-In . Он охватывает высокоуровневое представление различных шагов авторизации и разрешения в потоке входа Android.

Выйти не поможет решить вашу проблему. Вы должны отменить доступ каждый раз, когда вы выходите из приложения или когда хотите выписать из приложения и войти снова. Поэтому просто вызовите метод отзыва в обратном методе при выходе из приложения, как показано ниже:

  @Override public void onBackPressed() { Log.d("CDA", "onBackPressed Called"); revokeGplusAccess(); super.onBackPressed(); } 

Это, безусловно, решит вашу проблему.