11

My authorization flow in the new Google Drive Android API is as follows:

  1. Menu: SELECT ACCOUNT
  2. connect();
  3. onConnectionFailed() result.startResolutionForResult() invokes AccountSelectDialog / DriveAuthorization
  4. onConnected() do your stuff

Works like a charm. Now repeating with the aim to switch accounts:

  1. Menu: SELECT ACCOUNT
  2. connect();
  3. onConnected()

Here, I have no chance to get to the AccountSelectDialog since I never get onConnectionFailed() with 'result' to invoke startResolutionForResult(). What am I missing this time?

pinoyyid
  • 21,499
  • 14
  • 64
  • 115
seanpj
  • 6,735
  • 2
  • 33
  • 54

5 Answers5

8

First, add the Plus.API:

mGoogleApiClient = new GoogleApiClient.Builder(this).addApi(Drive.API).addApi(Plus.API).addScope(Drive.SCOPE_APPFOLDER).addConnectionCallbacks(this).addOnConnectionFailedListener(this).build();

Then you can switch accounts like this:

public void onClick(View view) {
  if (view.getId() == R.id.sign_out_button) {
    if (mGoogleApiClient.isConnected()) {
      Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
      mGoogleApiClient.disconnect();
      mGoogleApiClient.connect();
    }
  }
}

For more, see here.

Mark
  • 7,446
  • 5
  • 55
  • 75
  • I must add, I don't know why this works - shouldn't it just change the Plus account? - but it looks like it does... Would appreciate someone else to confirm. – Mark Apr 05 '14 at 14:36
  • This worked for me, as well. I'm wondering if it's clearing the accounts for Google Drive only because you pass in `mGoogleApiClient`. – Randy Jun 23 '14 at 20:41
7

Just call

mGoogleApiClient.clearDefaultAccountAndReconnect();

have a look at the docs.

This will call the onConnectionFailed callback that will present the layout to choose among the available Google accounts:

@Override
public void onConnectionFailed(ConnectionResult connectionResult) 
{
    if (connectionResult.hasResolution()) {
        try {                                              
            connectionResult.startResolutionForResult(this, RESOLVE_CONNECTION_REQUEST_CODE);
        } catch (IntentSender.SendIntentException e) {
            // Unable to resolve, message user appropriately
        }
    } else {                                           
        GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
    }

}
Xavi Gil
  • 11,460
  • 4
  • 56
  • 71
  • 1
    Thanks, that works, but my app still needs to know the active account (email) the user picked in the process. Managing it on the app level (per my answer below) is 1/ messy, 2/ needs 'GET_ACCOUNTS permission. Any ideas? BTW, the method you mention did not exist when I asked the question. – seanpj Jul 18 '15 at 23:59
  • Now, 16-05-26. Just call . mGoogleApiClient.clearDefaultAccountAndReconnect(); / "clearDefaultAccountAndReconnect" is deprecated. – Brownsoo Han May 26 '16 at 00:40
  • @BrownsooHan Where do you see that? There is no indication in code or in docs. – Bevor Jun 25 '17 at 16:42
6

I realize I made quite a mess by opening two SO questions about essentially the same topic. So, it is a good time to consolidate the answers. I was searching for direct getter / setter methods in GDAA but found only the 'setter' - setAccountName()) - SO question 21583828 (actually did not, but Burcu helped me).

On the other side, 'getter' can be substituted by getting the account name from "onActivityResult()" - SO question 21501829

And yet another SO question - this one - on the same topic has been resolved as well.

So the conclusion is:

  1. get account from 'onActivityResult()'
  2. set account in 'setAccountName()'
  3. keep your current account email around, so you can detect a new one (should user decide to switch) and reset Google Account Client if necessary.

UPDATE 2014-11-04:

Here is a wrapper that I use to persist and manage the Google accounts within my app.

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.google.android.gms.auth.GoogleAuthUtil;

public class GooAccMgr {
  private static final String ACC_NAME = "account_name";
  public  static final int FAIL = -1;
  public  static final int UNCHANGED =  0;
  public  static final int CHANGED = +1;

  private String mCurrEmail = null;  // cache locally

  public Account[] getAllAccnts(Context ctx) {
    return AccountManager.get(acx(ctx)).getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
  }

  public Account getPrimaryAccnt(Context ctx) {
    Account[] accts = getAllAccnts(ctx);
    return accts == null || accts.length == 0 ? null : accts[0];
  }

  public Account getActiveAccnt(Context ctx) {
    return email2Accnt(ctx, getActiveEmail(ctx));
  }

  public String getActiveEmail(Context ctx) {
    if (mCurrEmail != null) {
      return mCurrEmail;
    }
    mCurrEmail = ctx == null ? null : pfs(ctx).getString(ACC_NAME, null);
    return mCurrEmail;
  }

  public Account email2Accnt(Context ctx, String emil) {
    if (emil != null) {
      Account[] accounts =
       AccountManager.get(acx(ctx)).getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
      for (Account account : accounts) {
        if (emil.equalsIgnoreCase(account.name)) {
          return account;
        }
      }
    }
    return null;
  }

  /**
   * Stores a new email in persistent app storage, reporting result
   * @param ctx activity context
   * @param newEmail new email, optionally null
   * @return FAIL, CHANGED or UNCHANGED (based on the following table)
   * OLD    NEW   SAVED   RESULT
   * ERROR                FAIL
   * null   null  null    FAIL
   * null   new   new     CHANGED
   * old    null  old     UNCHANGED
   * old != new   new     CHANGED
   * old == new   new     UNCHANGED
   */
  public int setEmail(Context ctx, String newEmail) {
    int result = FAIL;  // 0  0

    String prevEmail = getActiveEmail(ctx);
    if        ((prevEmail == null) && (newEmail != null)) {
      result = CHANGED;
    } else if ((prevEmail != null) && (newEmail == null)) {
      result = UNCHANGED;
    } else if ((prevEmail != null) && (newEmail != null)) {
      result = prevEmail.equalsIgnoreCase(newEmail) ? UNCHANGED : CHANGED;
    }
    if (result == CHANGED) {
      mCurrEmail = newEmail;
      pfs(ctx).edit().putString(ACC_NAME, newEmail).apply();
    }
    return result;
  }

  private Context acx(Context ctx) {
    return ctx == null ? null : ctx.getApplicationContext();
  }
  private SharedPreferences pfs(Context ctx) {
    return ctx == null ? null : PreferenceManager.getDefaultSharedPreferences(acx(ctx));
  }
}

Hat-tip to Alex Lockwood for initial inspiration. Unfortunately, I can't find reference to his original code.

Community
  • 1
  • 1
seanpj
  • 6,735
  • 2
  • 33
  • 54
0

It sounds like you are relying on the default account selection. In this setup, the user is prompted once to select an account, and this status is remembered.

If you want to provide account switching capabilities in your app, you need to instead launch the account picker from your own app and provide the account name selected when you instantiate the GoogleApiClient.

You could persist the last selected account name in shared preferences so you remember it until the next time the user switches accounts.

Cheryl Simon
  • 46,552
  • 15
  • 93
  • 82
  • Thank you for your answer. Unfortunately, this is not what I'm after. My question was related to porting to the NEW Google Drive Android API (GDAA) https://developers.google.com/drive/android from Drive SDK v2 (SDKV2). In SDKV2, I can start the account picker "startActivityForResult(newChooseAccountIntent(),...)". In GDAA I have to do it the way you mentioned, leading to a convoluted code with redundancy. So the question was not "how to get around" but "is there something I'm missing" in GDAA. Sorry for not being more specific the first time around. – seanpj Feb 07 '14 at 12:49
  • 2
    In GDAA, if you do not supply an account name it uses the default account selection. If no default account has been selected yet, the user will be prompted to pick one. After that time, that default account will always be used. There is not currently a way to reset that. If you want to fully support multiple accounts and switching between them, you need to do the account selection logic yourself. – Cheryl Simon Feb 07 '14 at 21:41
  • Thank you Cheryl, I added another answer below, just to clean the mess I made. – seanpj Feb 08 '14 at 03:19
  • Hi Cheryl, I want to use account which is not exist on device like third party account. Is this possible ? – Umang Kothari Dec 22 '14 at 06:07
0

if you use GoogleApiClient, just call mGoogleApiClient.clearDefaultAccountAndReconnect().

if you use DriveClient with GoogleSignInAccount (drive library 16.0.0), try this.

// try connect Drive
fun startSignIn() {
    val requiredScopes = HashSet<Scope>()
    requiredScopes.add(Drive.SCOPE_FILE)
    requiredScopes.add(Drive.SCOPE_APPFOLDER)
    val account = GoogleSignIn.getLastSignedInAccount(this)
    if (account != null && account.grantedScopes.containsAll(requiredScopes)) {
        // TODO: Get DriveClient and DriveResourceClient
    } else {
        val option = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(Drive.SCOPE_FILE, Drive.SCOPE_APPFOLDER)
            .build()
        val client = GoogleSignIn.getClient(this, option)
        startActivityForResult(client.signInIntent, REQUEST_CODE_SIGN_IN)
    }
}

// try change account
fun changeAccount() {
    val option = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .build()
    val client = GoogleSignIn.getClient(activity, option)
    client.signOut()
        .addOnSuccessListener {
            Log.d(TAG, "signOut success")
            // Try again sign-in
            startSignIn()
        }
        .addOnFailureListener {
            Log.e(TAG, "signOut failed $it")
        }
    }
Brownsoo Han
  • 4,549
  • 3
  • 20
  • 20