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

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