androidgoogle-signinandroid-googleapiclient

How to pass an GoogleApiClient objet to another activity


In my loginactivity, I have an google Sign In button to LogIn. I checked and works fine. I had anothers buttons to log out and Revoke and worked fine too.

This is my code of the loggin screen.

 import android.app.ProgressDialog;
 import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.google.android.gms.auth.api.Auth;
  import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
   import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.OptionalPendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;

import java.util.ArrayList;
 import java.util.List;

 public class MainActivity extends AppCompatActivity implements
    View.OnClickListener,
    GoogleApiClient.OnConnectionFailedListener
{

private static final String TAG = MainActivity.class.getSimpleName();
private static final int RC_SIGN_IN = 007;

private GoogleApiClient mGoogleApiClient;
private ProgressDialog mProgressDialog;

private SignInButton btnSignIn;

List<String> profile=new ArrayList<>();
private MyGoogleApi_Singleton myGoogleApi_singleton;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    btnSignIn = (SignInButton) findViewById(R.id.sign_in_button);
    btnSignIn.setOnClickListener(this);

    updateUI(false);

    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail()
            .build();

    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .enableAutoManage(this, this)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build();

    /* Customizing G+ button
    btnSignIn.setSize(SignInButton.SIZE_STANDARD);
    btnSignIn.setScopes(gso.getScopeArray());*/

    //Create a new objet to handle in all class
    myGoogleApi_singleton= new MyGoogleApi_Singleton();
    myGoogleApi_singleton.getInstance(mGoogleApiClient);
}


private void signIn()
{
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_SIGN_IN);

}


private void signOut()
{
    Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
            new ResultCallback<Status>()
            {
                @Override
                public void onResult(Status status)
                {
                    updateUI(false);
                }
            });
}

private void revokeAccess()
{
    Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback(
            new ResultCallback<Status>()
            {
                @Override
                public void onResult(Status status)
                {
                    updateUI(false);
                }
            });
}

private void handleSignInResult(GoogleSignInResult result)
{
    Log.d(TAG, "handleSignInResult:" + result.isSuccess());
    if (result.isSuccess())
    {
        // Signed in successfully, show authenticated UI.
        GoogleSignInAccount acct = result.getSignInAccount();

        String personName = acct.getDisplayName();
        String personPhotoUrl = acct.getPhotoUrl().toString();
        String email = acct.getEmail();
        String idnumber= acct.getId();

        Log.e(TAG, "Name: " + personName + ", email: " + email
                + ", Image: " + personPhotoUrl+ ", Id Number: "+ idnumber);
        //Save the data into the arraylist
        profile.add(idnumber);
        profile.add(personName);
        profile.add(email);
        profile.add(personPhotoUrl);

        //save into sharedpreferences
        StringBuilder stringBuilder = new StringBuilder();

        for (String s:profile)
        {
            stringBuilder.append(s);
            stringBuilder.append(",");
        }

        SharedPreferences sharpref = getSharedPreferences("ProfileList",0);
        SharedPreferences.Editor editor = sharpref.edit();
        editor.putString("ProfileList", stringBuilder.toString());
        editor.commit();

        goIndexScreen();
    } else
        {
        // Signed out, show unauthenticated UI.
        updateUI(false);
        }
}

@Override
public void onClick(View v)
{
    switch (v.getId())
    {
        case R.id.sign_in_button:
            signIn();
            break;

        default:
            break;
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
    if (requestCode == RC_SIGN_IN)
    {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        handleSignInResult(result);
    }
}

@Override
public void onStart()
{
    super.onStart();

    OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
    if (opr.isDone())
    {
        // If the user's cached credentials are valid, the OptionalPendingResult will be "done"
        // and the GoogleSignInResult will be available instantly.
        Log.d(TAG, "Got cached sign-in");
        GoogleSignInResult result = opr.get();
        handleSignInResult(result);
    } else
        {
        // If the user has not previously signed in on this device or the sign-in has expired,
        // this asynchronous branch will attempt to sign in the user silently.  Cross-device
        // single sign-on will occur in this branch.
        showProgressDialog();
        opr.setResultCallback(new ResultCallback<GoogleSignInResult>()
        {
            @Override
            public void onResult(GoogleSignInResult googleSignInResult)
            {
                hideProgressDialog();
                handleSignInResult(googleSignInResult);
            }
        });
    }
}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult)
{
    // An unresolvable error has occurred and Google APIs (including Sign-In) will not
    // be available.
    Log.d(TAG, "onConnectionFailed:" + connectionResult);
}

//method to show a progress dialog during the SignIn
private void showProgressDialog()
{
    if (mProgressDialog == null)
    {
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setMessage("Loading...");
        mProgressDialog.setIndeterminate(true);
    }

    mProgressDialog.show();
}

//method to hide the progress dialog
private void hideProgressDialog()
{
    if (mProgressDialog != null && mProgressDialog.isShowing())
    {
        mProgressDialog.hide();
    }
}
//method to show or hide the buttons
private void updateUI(boolean isSignedIn)
{
    if (isSignedIn)
    {
        btnSignIn.setVisibility(View.GONE);


    } else
        {
        btnSignIn.setVisibility(View.VISIBLE);

        }
}
//method to go to next activity
private void goIndexScreen()
{
    Intent intent=new Intent(this,Index.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
}

}

When the login is succes, the app jump to another activity. I need to LogOut from this activity, but I need to a mGoogleApiClient from LoginActivity and I don´t know how I could doing.

I have create a new class (singleton) here my code

import com.google.android.gms.common.api.GoogleApiClient;

class MyGoogleApi_Singleton {
private static final String TAG = "GoogleApiClient";

private static MyGoogleApi_Singleton instance = null;

private static GoogleApiClient mGoogleApiClient = null;

private MyGoogleApi_Singleton(Context context) {
    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail()
            .build();

    mGoogleApiClient = new GoogleApiClient.Builder(context)
            .enableAutoManage(this, this)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build();

}

public static MyGoogleApi_Singleton getInstance(Context context) {

    if(instance == null) {
        instance = new MyGoogleApi_Singleton(context);

    }

    return instance;
}


//methods SingIn,SignOut and Revoke
public void Login(GoogleApiClient bGoogleApiClient){


}

public void Logout(){
    if(mGoogleApiClient.isConnected()) {
        Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
                new ResultCallback<Status>() {
                    @Override
                    public void onResult(Status status) {
                        Log.d("LOGOUT-RESULT","LOGOUT");
                    }
                });
    }

}

public void Revoke() {
    if ((mGoogleApiClient.isConnected())) {
        Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback(
                new ResultCallback<Status>() {
                    @Override
                    public void onResult(Status status) {
                    Log.d("REVOKE-RESULT","REVOKE");
                    }
                });
    }
}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

}
}

This is the code of Index activity

   import android.content.Intent;
   import android.content.SharedPreferences;
   import android.os.Bundle;
   import android.support.annotation.NonNull;
    import android.support.design.widget.FloatingActionButton;
   import android.support.design.widget.Snackbar;
   import android.util.Log;
   import android.view.View;
   import android.support.design.widget.NavigationView;
   import android.support.v4.view.GravityCompat;
   import android.support.v4.widget.DrawerLayout;
   import android.support.v7.app.ActionBarDrawerToggle;
   import android.support.v7.app.AppCompatActivity;
   import android.support.v7.widget.Toolbar;
   import android.view.Menu;
   import android.view.MenuItem;
   import android.widget.ImageView;
   import android.widget.TextView;

   import com.bumptech.glide.Glide;
   import com.google.android.gms.auth.api.Auth;
   import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
   import com.google.android.gms.common.ConnectionResult;
   import com.google.android.gms.common.api.GoogleApiClient;
   import com.google.android.gms.common.api.ResultCallback;
   import com.google.android.gms.common.api.Status;
   import com.google.android.gms.tasks.OnCompleteListener;
   import com.google.android.gms.tasks.Task;

   import java.util.ArrayList;
   import java.util.List;

 public class Index extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener {
private ImageView ivprofile;
private TextView tvname;
private TextView tvemail;
private TextView tvidnumber;
private String picprofile;
private String name;
private String idnumber;
private String email;
final List<String> profile = new ArrayList<String>();
private View headerview;

private GoogleApiClient mGoogleApiClient;
private MyGoogleApi_Singleton myGoogleApiSingleton;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_index);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);

    //create a headerview to conect to header of left menu
    headerview=navigationView.getHeaderView(0);

    ivprofile=(ImageView)headerview.findViewById(R.id.imageProfile);
    tvname=(TextView)headerview.findViewById(R.id.fullName);
    tvemail=(TextView)headerview.findViewById(R.id.email);
    tvidnumber=(TextView) headerview.findViewById(R.id.idNumber);

    //Load file saved by sharedpreferences into a new arraylist
    final SharedPreferences sharpref = getSharedPreferences("ProfileList",0);
    String Items = sharpref.getString("ProfileList","");
    String [] listItems = Items.split(",");
    for (int i=0;i<listItems.length;i++){
        profile.add(listItems[i]);
    }

    //get the profile

    idnumber=profile.get(0);
    name=profile.get(1);
    email=profile.get(2);
    picprofile=profile.get(3);

    Log.d("ArrayPerfil", name+email+idnumber+picprofile);

    tvname.setText(name);
    tvidnumber.setText(idnumber);
    tvemail.setText(email);
    Glide.with(this).load(picprofile).into(ivprofile);

    //get the mgoogleapiclient objet
    myGoogleApiSingleton=new MyGoogleApi_Singleton();
    mGoogleApiClient=myGoogleApiSingleton.get_GoogleApiClient();


@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.index, 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();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    if (id == R.id.nav_camera) {
        // Handle the camera action
    } else if (id == R.id.nav_gallery) {

    } else if (id == R.id.nav_slideshow) {

    } else if (id == R.id.nav_manage) {

    } else if (id == R.id.nav_share) {

    } else if (id == R.id.nav_send) {

    }else if (id == R.id.logout) {
        myGoogleApiSingleton.getInstance(context).Logout();
        goLoginScreen();
    }

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}
//method to go to login screen
private void goLoginScreen() {
    Intent intent=new Intent(this,MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
}

}

As you told me, I created a Transparent Activity. I added the method handleSignInResult because is calling on the method OnActivityResult.

This is the code:

public class GoogleActivity extends AppCompatActivity {
public static final int RC_SIGN_IN = 1000;

private static final String ACTION = "calling_action";

public static Intent getIntent(Context context, int action, Intent actionIntent) {
    Intent i = new Intent(context, GoogleActivity.class);
    i.putExtra(ACTION, action);
    i.putExtra(Intent.EXTRA_INTENT, actionIntent);
    return i;
}

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

    Intent actionIntent;
    int action = getIntent().getIntExtra(ACTION, 0);
    switch (action) {
        case RC_SIGN_IN:
            actionIntent = (Intent) getIntent().getExtras().get(Intent.EXTRA_INTENT);
            if (actionIntent != null)
                startActivityForResult(actionIntent, RC_SIGN_IN);
            break;
        case 0:
        default:
            break;
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case RC_SIGN_IN:
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            handleSignInResult(result);
            finish();
            return;
    }
}
private void handleSignInResult(GoogleSignInResult result)
{

    if (result.isSuccess())
    {
        // Signed in successfully
        Log.d("SIGNIN", "YOU ARE LOG IN");


    } else
    {
        // Signed out,
        Log.d("SIGNIN", "YOU ARE NOT LOG IN");
    }
}

}

I wait for your help!

Thanks!


Solution

  • This is not a bad idea, in fact it is you should do. If you want to handle all actions and events from Google Auth, the easiest, elegant, reusable, and testeable way to do it is to wrap this logic in one or more clases dedicated to that.

    If you have few actions you could encalsupate all of them in only one wrap class. For example you could create a class GoogleSingInWrapper and use the Singleton patter to make sure there is only one instance in your entire app.

    public class GoogleSingInWrapper {
       private static GoogleSingInWrapper instance;
       private GoogleApiClient mGoogleApiClient;
    
       private GoogleSingainWrapper() {
          // Private constructor to deny the creation of this object through the constructor and prevent creating more then one instance
          mGoogleApiClient = /*create your client here*/;
       }
    
       public static getInstance(/*params you need*/) {
          if(instance == null)
             instance = new GoogleSingInWrapper (/*params*/);
          return instance;
       }
    
       public void login(/*params*/) {
          // Login
       }
    
       // Other methods
    }
    

    So to get (and create an instance if doesn't exists yet) an instance of GoogleSingInWrapper you use:

    GoogleSingInWrapper.gerInstance(/*params*/);
    

    Now if you put all variables and logic in this class you can access them from where you want. The mGoogleApiClient must be in this wrapper.

    You can now add all method you need such login, logout and revoke.

    And use it as follows:

    GoogleSingInWrapper.getInstance().login(/*params*/);
    GoogleSingInWrapper.getInstance().logout(/*params*/);
    GoogleSingInWrapper.getInstance().revoke(/*params*/);
    

    You shouldn't use mGoogleApiClient directly, it should be encapsulated in GoogleSingInWrapper.

    EDIT

    When I say mGoogleApiClient should be private inside GoogleSingInWrapper it means that you should't have access to it outside the GoogleSingInWrapper class. If you create a GoogleSingInWrapper but you create a method called

    public GoogleApiClient get_GoogleApiClient();
    

    you problems keep existing, because you are still using this mGoogleApiClientin all activities. You don't want this, so you want to be decoupled of this object in all activities.

    Following your edits, I'll type here more code, but the more free code I give you less you learn.

    public class GoogleSingInWrapper {
       private static GoogleSingInWrapper instance;
       private GoogleApiClient mGoogleApiClient;
    
       private GoogleSingainWrapper(Context context) {
          // Private constructor to deny the creation of this object through the constructor and prevent creating more then one instance
          GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail() 
            .build(); 
    
          mGoogleApiClient = new GoogleApiClient.Builder(context)
            .enableAutoManage(this, this)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build(); 
       }
    
       public static getInstance(Context context) {
          if(instance == null)
             instance = new GoogleSingInWrapper (/*params*/);
          return instance;
       }
    
       public void login(/*params*/) {
          // Login
       }
    
       // Other methods
       public void logout(){ 
          if(mGoogleApiClient.isConnected()) {
          Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
                new ResultCallback<Status>() {
                    @Override 
                    public void onResult(Status status) {
                        Log.d("LOGOUT-RESULT","LOGOUT");
                    } 
                }); 
          }  
       }
    }
    

    This should look like your GoogleSingInWrapper. And to call for example logout method, you should call as follows:

    if (id == R.id.logout) {
        GoogleSingInWrapper.getInstance(context).logout();
        goLoginScreen(); 
    } 
    

    Note the constructor is Private intentionally because you doesn't want to call new GoogleSingInWrapper. If you do that, you are creating multiple instances of this object ant you're breaking the Singleton pattern.

    Also, you may note that some processes like login, needs an Activity because the results are posted in onActivityResult. In order to decouple you GoogleSingInWrapper from all your Activities you could create a dedicated Activity to manage all onActivityResults. This activity should be transparent and will be invisible for the user.

    You can achieve that with this code:

    public class GoogleActivity extends AppCompatActivity {
      public static final int RC_SIGN_IN = 1000;
    
      private static final String ACTION = "calling_action";
    
      public static Intent getIntent(Context context, int action, Intent actionIntent) {
        Intent i = new Intent(context, GoogleActivity.class);
        i.putExtra(ACTION, action);
        i.putExtra(Intent.EXTRA_INTENT, actionIntent);
        return i;
      }
    
      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
    
        Intent actionIntent;
        int action = getIntent().getIntExtra(ACTION, 0);
        switch (action) {
          case RC_SIGN_IN:
            actionIntent = (Intent) getIntent().getExtras().get(Intent.EXTRA_INTENT);
            if (actionIntent != null)
              startActivityForResult(actionIntent, RC_SIGN_IN);
            break;
          case 0:
          default:
            break;
        }
      }
    
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
          case RC_SIGN_IN:
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            handleSignInResult(result);
            finish();
            return;
        }
      }
    }
    

    Then set the transparent theme in the manifest for this Activity:

    <activity
            android:name=".data.drive.GoogleActivity"
            android:theme="@style/TransparentActivity"/>
    

    And define your transparent style in your values folder:

    <style name="TransparentActivity" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>
    

    Now you have to catch all pieces I've done and construct your final product in order to get it working.