Android: inicio de sesión de Google con pantalla de inicio de sesión en lugar de inicio de sesión automático

He añadido un inicio de sesión de Google a mi aplicación para Android (los pasos se pueden encontrar en la edición 3 de esta publicación). Todo funciona como debería, pero me gustaría hacer algunos pequeños ajustes. Con el código actual encontrado en la edición 3, se inicia sesión automáticamente cada vez que inicia la aplicación, sin la pantalla de inicio de sesión. Quiero desactivar esto, ya que la aplicación se utilizará en una tableta en el trabajo, donde un empleado diferente debe iniciar sesión en la aplicación todos los días.

Empecé por eliminar el mGoogleApiClient.connect(); Desde el método onStart() , y ahora tengo el botón de inicio de sesión de Google de nuevo. Cuando agrego mGoogleApiClient.connect(); A signInWithGoogle() puedo signInWithGoogle() sesión con el usuario recordado.

Lo que quiero ahora es la pantalla predeterminada de inicio de sesión de Google en la que puede rellenar su correo electrónico de Google y su contraseña, cada vez que haga clic en el botón de inicio de sesión, en lugar de simplemente iniciar sesión en el usuario recordado. (PS: Tenga en cuenta que en mi dispositivo Android actualmente sólo tengo un usuario en Settings -> Google Accounts , tal vez por eso se inicia sesión automáticamente en lugar de dar la opción de elegir qué cuenta debe conectarse.)

Voy a probar si hace una diferencia cuando tengo varias cuentas de Google en mi dispositivo Android. Aceptar, he añadido una segunda cuenta de Google a mi dispositivo Android, pero mi aplicación todavía se registra automáticamente en el usuario recordado cuando hago clic en Iniciar sesión ..


EDIT 2:

Todavía no he podido encontrar una solución para mi problema.

Encontré algunos tutoriales AccountManager con diferentes formas de iniciar sesión en Google, como usar el AccountManager para que un usuario pueda seleccionar una de las cuentas de Google existentes en el dispositivo. (Sólo he leído sobre este método hoy, así que no tengo ejemplos de código para esto, pero esto tampoco es lo que estoy buscando de todos modos.)

Probablemente ya lo dejé en claro en el post anterior, pero esto es lo que quiero explicar en imágenes:

  1. Un usuario inicia la aplicación en su Dispositivo Android.
  2. Un usuario introduce su nombre de usuario de cuenta de Google (correo electrónico utilizado) y su contraseña Producto en la lista
  3. Después de que un usuario ha iniciado sesión correctamente, podemos hacer otras cosas con la aplicación

PS: Sólo para asegurarse de que esta pantalla de inicio de sesión es la de Google. Así que no es una pantalla de inicio de sesión creada por mí mismo. Esto en teoría me haría capaz de guardar la contraseña introducida, que está en contra del protocolo de Google OAuth.


EDIT 3 (El Código):

Los pasos que hice para que los servicios de Google funcionaran hasta ahora son los siguientes. Ahora sólo tengo que averiguar cómo forzar la pantalla de inicio de sesión o completamente cerrar la sesión, lo que resulta en la pantalla de inicio de sesión cada vez.

He seguido el siguiente tutorial: http://www.androidhive.info/2014/02/android-login-with-google-plus-account-1/

Con información adicional utilizada en los siguientes tutoriales / sitios:

  • 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

Esto generó el código siguiente:

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); } } 

Los otros pasos que hice fueron:

En https://console.developers.google.com/project He creado un proyecto con:

API de Google+ en:

API de Google+ en

Y un ID de cliente creado con el SHA1 correcto y el mismo espacio de nombres que el proyecto:

Y un ID de cliente creado con el SHA1 correcto

En Eclipse:

He instalado la biblioteca google-play-services:

Servicios Google Play instalados

Y lo agregó al proyecto:

Se agregó la biblioteca de servicios de Google Play (2)Se agregó la biblioteca de servicios de Google Play (2)

También he creado un emulador con la versión de Google 4.4.2 (por lo que no Android 4.4.2) y también cambió el proyecto a Google 4.4.2 en lugar de Android 4.4.2:

Error de la soluciónEmulador de error de solución


EDIT 4:

Ok, tengo una solución temporal para mi propio caso. En mi caso, la aplicación que estoy haciendo debe ejecutarse en un Tablet que se utiliza explícitamente para mi aplicación. Como este es el caso, puedo eliminar todas las cuentas de Google de la configuración del dispositivo en el momento en que alguien revoca el acceso (como función de cierre de sesión).

Comencé por quitar los cambios anteriores (re-added the .connect(); al onStart() y lo quitó del signInWithGPlus() )

Luego agregué una línea al método 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); } }); } } 

Con el siguiente método:

 // 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(); } 

Sin embargo, esto sólo funciona en mi caso. Esto no funcionará en un dispositivo personal en el que obviamente no desea eliminar todas las cuentas de Google del dispositivo. Todavía me gustaría saber una solución si no quieres eliminar las cuentas de Google de ningún dispositivo.

3 Solutions collect form web for “Android: inicio de sesión de Google con pantalla de inicio de sesión en lugar de inicio de sesión automático”

La respuesta correcta está aquí .

Se necesita llamar a mClient.clearDefaultAccountAndReconnect() para borrar la cuenta previamente almacenada en caché.
Es una buena práctica hacer esto cada vez que un usuario hace clic en el botón de inicio de sesión, de modo que el usuario se muestre todas sus cuentas cada vez.

Intente firmar al usuario y luego volver a firmar al usuario. Si desea forzar al usuario a iniciar sesión cada vez, puede firmarlos cuando salgan de la aplicación. No te recomiendo que cambies el comportamiento de salida de la aplicación porque podría sorprender al usuario.

Asegúrese de que el entorno de desarrollo está configurado correctamente ejecutando el ejemplo de Google+ Android Quickstart . Si el comportamiento de inicio de sesión en la aplicación de ejemplo es el mismo que aparece, existe un problema con el entorno de desarrollo (por ejemplo, la versión de GMS o la versión de la API de Android) o el comportamiento del botón de inicio de sesión no es lo que espera que sea .

Por último, es posible que pueda beneficiarse de este artículo que cubre algunos puntos problemáticos comunes con Android Sign-In . Cubre una vista de alto nivel de los distintos pasos de autorización y resolución en el flujo de inicio de sesión de Android.

Salir no le ayudará a resolver su problema. Tienes que revocar el acceso cada vez que sales de la aplicación o cuando quieres salir de la aplicación e iniciar sesión nuevamente. Por lo tanto, basta con llamar revocar el método de acceso en el método de retroceso en la aplicación de salida como a continuación:

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

Esto definitivamente resolverá su problema.

  • ¿Qué es una buena manera de realizar un seguimiento de los eventos de fondo en Android sin inflar los números de sesión?
  • Cómo mantener la sesión en una aplicación Android de teléfono
  • Estrategia de la aplicación de Android para realizar un seguimiento de una sesión de inicio de sesión
  • La cookie de sesión no expira Cuando Chrome Browser está cerrado en teléfonos inteligentes
  • Reutilizando sesiones SSL en Android con HttpClient
  • Mantener sesión entre llamadas HttpUrlConnection (Native / Webview)
  • Las solicitudes de volea no funcionan con Django Sessions
  • Android - ¿Cómo manejar correctamente los métodos onPause / onResume?
  • Usuario de comprobación de Android iniciado antes, de lo contrario iniciar actividad de inicio de sesión
  • ¿Cuál es el equivalente más cercano a una variable Session / Cookie en Android?
  • Cómo persistir las cookies en Android incluso después de cerrar la aplicación o poner en segundo plano mediante el botón de menú en el android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.