I'm developing an application in which i have integrated google plus. So far Its working fine, I am able to retrieve the user profile.
But now i want to do the following:
1)I have two activity signInActivity and shareActivity.
2)If user is already signin using signInActivity then it should not ask for signin again in
shareActivity and should directly share the content.
3)If user is not signedin in the signInActivity and try to share data using shareActivitythen app should signin the user and then only share the data. In this case if user goes back to the signInActivity then app should show that "you have already signedin"
In short i want user signin to be Central within application so that if it is alrady signedin it should be accessible from any activity.
I have heard about the access token but i dont know how to use it and document says that it expires in an hour which is not what i want.
How can i make central google plus signin? is it possible? or i need to authenticate user in each activity?
Managing a separate instance of GoogleApiClient in each activity will not result in the user being asked to sign in multiple times.
Google+ Sign-in (ie. GoogleApiClient) provides an interface to the Google accounts on the device and the Google Play services core service - it doesn't have state per GoogleApiClient instance. So once a device account has been authenticated for your app, new instances of GoogleApiClient will access the same state. GoogleApiClient is specifically designed to be a lightweight way to access the central state managed by Google Play services.
You're in luck regarding access tokens! Google Play services takes care of all token management for you. So although access tokens only last for one hour, as you say, if you try to use your PlusClient to access a Google API and your access token has expired, Google Play services will transparently request a new access token for you and complete the call.
Take a look at the first part of this Google I/O talk for more details:
http://www.youtube.com/watch?v=_KBHf1EODuk
0. TL;DR
For the impatient coder, a working version of the following implementation can be found on GitHub. This is the same answer written on another Stack Overflow post.
After rewriting the login activity code several times in many different apps, the easy (and not so elegant) solution was create the Google API client as a Application class object. But, since the connection state affect the UX flow, I never was happy about with this approach.
Reducing our problem only to the connection concept, we may consider that:
It hides the Google API client.
It has finite states.
It is a (rather) unique.
The current state affect the behavior of the app.
1. Proxy Pattern
Since the Connection encapsulates the GoogleApiClient, it will implement the ConnectionCallbacks and OnConnectionFailedListener:
#Override
public void onConnected(Bundle hint) {
changeState(State.OPENED);
}
#Override
public void onConnectionSuspended(int cause) {
changeState(State.CLOSED);
connect();
}
#Override
public void onConnectionFailed(ConnectionResult result) {
if (currentState.equals(State.CLOSED) && result.hasResolution()) {
changeState(State.CREATED);
connectionResult = result;
} else {
connect();
}
}
Activities can communicate to the Connection class through the methods connect, disconnect, and revoke, but their behaviors are decided by the current state. The following methods are required by the state machine:
protected void onSignIn() {
if (!googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
googleApiClient.connect();
}
}
protected void onSignOut() {
if (googleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(googleApiClient);
googleApiClient.disconnect();
googleApiClient.connect();
changeState(State.CLOSED);
}
}
protected void onSignUp() {
Activity activity = activityWeakReference.get();
try {
changeState(State.OPENING);
connectionResult.startResolutionForResult(activity, REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
changeState(State.CREATED);
googleApiClient.connect();
}
}
protected void onRevoke() {
Plus.AccountApi.clearDefaultAccount(googleApiClient);
Plus.AccountApi.revokeAccessAndDisconnect(googleApiClient);
googleApiClient = googleApiClientBuilder.build();
googleApiClient.connect();
changeState(State.CLOSED);
}
2. State Pattern
This is a behavioral pattern the allow an object to alter its behavior when its internal state changes. The GoF Design Patterns book describes how a TCP connection can be represent by this pattern (which is also our case).
A state from a state machine should be a singleton, and the easiest away of doing it in Java was to create Enum named State as follows:
public enum State {
CREATED {
#Override
void connect(Connection connection) {
connection.onSignUp();
}
#Override
void disconnect(Connection connection) {
connection.onSignOut();
}
},
OPENING {},
OPENED {
#Override
void disconnect(Connection connection) {
connection.onSignOut();
}
#Override
void revoke(Connection connection) {
connection.onRevoke();
}
},
CLOSED {
#Override
void connect(Connection connection) {
connection.onSignIn();
}
};
void connect(Connection connection) {}
void disconnect(Connection connection) {}
void revoke(Connection connection) {}
The Connection class holds the context, i.e. the current state, which defines how the Connection methods connect, disconnect, and revoke will behave:
public void connect() {
currentState.connect(this);
}
public void disconnect() {
currentState.disconnect(this);
}
public void revoke() {
currentState.revoke(this);
}
private void changeState(State state) {
currentState = state;
setChanged();
notifyObservers(state);
}
3. Singleton Pattern
Since there is not need to recreate this class repeatedly, we provide it as a singleton:
public static Connection getInstance(Activity activity) {
if (null == sConnection) {
sConnection = new Connection(activity);
}
return sConnection;
}
public void onActivityResult(int result) {
if (result == Activity.RESULT_OK) {
changeState(State.CREATED);
} else {
changeState(State.CLOSED);
}
onSignIn();
}
private Connection(Activity activity) {
activityWeakReference = new WeakReference<>(activity);
googleApiClientBuilder = new GoogleApiClient
.Builder(activity)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Plus.API, Plus.PlusOptions.builder().build())
.addScope(new Scope("email"));
googleApiClient = googleApiClientBuilder.build();
currentState = State.CLOSED;
}
4. Observable Pattern
The Connection class extends Java Observable, so 1 or more activities can observe the state changes:
#Override
protected void onCreate(Bundle bundle) {
connection = Connection.getInstance(this);
connection.addObserver(this);
}
#Override
protected void onStart() {
connection.connect();
}
#Override
protected void onDestroy() {
connection.deleteObserver(this);
connection.disconnect();
}
#Override
protected void onActivityResult(int request, int result, Intent data) {
if (Connection.REQUEST_CODE == request) {
connection.onActivityResult(result);
}
}
#Override
public void update(Observable observable, Object data) {
if (observable != connection) {
return;
}
// Your presentation logic goes here...
}
For anyone reading this question you can also check this answer by Ian Barber and also the one below, answered by Lee, that explains three broad ways of working with Google plus login and multiple activies which I found very useful actually.
Related
I was integrating azure adb2c on my native android app using MSAL. My token expiry is set to 60minutes in the portal. Currently I'm calling the acquireTokenSilentAsync each time the app launches in order to make sure access token is not expired. But is there any way to avoid calling acquireTokenSilentAsync each time and make the call happens only when the access token expires? This is to make the app load much faster,by avoid calling acquireTokenSilentAsync every time.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.microsoft_azure);
context = MicrosoftAzureActivity.this;
initializeUI();
// Creates a PublicClientApplication object with res/raw/auth_config_single_account.json
PublicClientApplication.createSingleAccountPublicClientApplication(MicrosoftAzureActivity.this,
R.raw.auth_config_single_account,
new IPublicClientApplication.ISingleAccountApplicationCreatedListener() {
#Override
public void onCreated(ISingleAccountPublicClientApplication application) {
/**
* This test app assumes that the app is only going to support one account.
* This requires "account_mode" : "SINGLE" in the config json file.
**/
loadAccount();
}
#Override
public void onError(MsalException exception) {
displayError(exception);
}
});
}
Interactively fetching Token:
mSingleAccountApp.signIn(MicrosoftAzureActivity.this, null, getScopes(), getAuthInteractiveCallback());
Load Account when already token is fetched Interactively and account is already Loaded:
private void loadAccount() {
if (mSingleAccountApp == null) {
Log.d("SKT","Account Not Signed In");
return;
}
Log.d("SKT","Account Not Signed In#1");
mSingleAccountApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() {
#Override
public void onAccountLoaded(#Nullable IAccount activeAccount) {
// You can use the account data to update your UI or your app database.
mAccount = activeAccount;
if (activeAccount != null) {
Log.d("SKT","Account Already Signed In");
mSingleAccountApp.acquireTokenSilentAsync(getScopes(), B2CConfiguration.getAuthorityFromPolicyName("B2C_1_SignInSignUp"), getAuthSilentCallback());
}
}
#Override
public void onAccountChanged(#Nullable IAccount priorAccount, #Nullable IAccount currentAccount) {
if (currentAccount == null) {
// Perform a cleanup task as the signed-in account changed.
showToastOnSignOut();
}
}
#Override
public void onError(#NonNull MsalException exception) {
displayError(exception);
}
});
}
No, you must call acquireTokenAsync for this, it evaluates whether the token in cache is expired or for a different scope than being requested. If neither is true, MSAL returns the tokens from the cache, it doesn’t make any network calls and should be almost instant. You wouldn’t get any perf advantage by doing anything different as that is the minimum.
I have a problem with the last update of the google nearby connections API.
When I call start Discovery () or start Advertising () before the update I needed to pass a googleApiClient as a parameter.
After the update, I don't need to do this, but I still need to access the api with googleApiClient.
How can I run the sample without using googleApiClient?
private void startAdvertising() {
Nearby.getConnections(context).startAdvertising(
getUserNickname(),
SERVICE_ID,
mConnectionLifecycleCallback,
new AdvertisingOptions(STRATEGY))
.addOnSuccessListener(
new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void unusedResult) {
// We're advertising!
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// We were unable to start advertising.
}
});
}
Use Nearby.Connections instead of Nearby.getConnections.
Use Nearby.getConnectionsClient(Context), to use the API without making a GoogleApiClient.
I am trying to connect my app to Google Fit. I am using an IntentService that needs to do the following things. Gets started when I have information about steps. At this point I am trying to create the GoogleApiClient by calling the following code:
mClient = new GoogleApiClient.Builder(this)
.addApi(Fitness.HISTORY_API)
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
.addScope(new Scope(Scopes.FITNESS_LOCATION_READ_WRITE))
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
log.info("FITNESS_API: Connected!!!");
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
insertOrUpdateDataPoints();
}
});
thread.start();
}
#Override
public void onConnectionSuspended(int i) {
// If your connection to the sensor gets lost at some point,
// you'll be able to determine the reason and react to it here.
if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
log.info("FITNESS_API: Connection lost. Cause: Network Lost.");
} else if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
log.info("FITNESS_API: Connection lost. Reason: Service Disconnected");
}
}
}
).build();
mClient.connect();
After creating a DataSet and adding the steps details as DataPoint elemnets, I sync the information to Google Fit and close the GoogleApiClient with:
com.google.android.gms.common.api.Status insertStatus =
Fitness.HistoryApi.insertData(mClient, dataSet).await(1, TimeUnit.MINUTES);
// Before querying the data, check to see if the insertion succeeded.
if (!insertStatus.isSuccess()) {
log.info("FITNESS_API: There was a problem inserting the dataset. Status = " + insertStatus.getStatusCode());
}
mClient.disconnect();
mClient = null;
The problem is that by trying to manage the GoogleApiClient on my own (without enableAutoManage), I don't get prompted to allow the app to post data to Google Fit. This behaviour changes if I use enableAutoManage when creating the GoogleApiClient. However, in order to enableAutoManage for the client, I need to have a ActivityFragment due to the parameters required by enableAutoManage. I don't have access to an ActivityFragment in the IntentyService and I do want to keep the management of the client and the insert action in a separate service which can run in the background.
Also when I don't use enableAutoManage even though I have registered the connect callback for the GoogleApiClient nothing happens.
How can I ensure that my application prompts the user to allow the app to post to Google Fit? I need this to happen if the app doesn't have permission to post in Google Fit when the user opens the app. Any ideas? Thank you.
I have found the solution.
If you don't want to use "enableAutoManage", you need to register onConnectionFailed method like this:
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if( !authInProgress ) {
try {
authInProgress = true;
connectionResult.startResolutionForResult( MainActivity.this, REQUEST_OAUTH );
} catch(IntentSender.SendIntentException e ) {
}
} else {
Log.e( "GoogleFit", "authInProgress" );
}
}
This will present the dialog.
In your intent service use the above method mentioned by #Vlad. Create a notification(Sticky or otherwise depending on your importance) asking user to give you permission when onconnection failed is encountered. The notification will redirect user to an activity where you ask user to give you fitaccess again.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_OAUTH:
if (resultCode != Activity.RESULT_OK) {
return;
}
if (!googleApiClient.isConnected()) {
googleApiClient.connect(GoogleApiClient.SIGN_IN_MODE_OPTIONAL);
} else {
readDataTask();
}
return;
case RC_SIGN_IN:
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
GoogleSignInAccount account = result.getSignInAccount();
String email = account.getEmail();//you can get OAuth user's email AT THIS POINT.
if (GoogleFitService.googleApiClient.isConnected()) {
readDataTask();
}
}
}
}
First time what you have to DO is Oauth Goole accout, then get your's email.
gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.setAccountName("user's email")
.requestScopes(
new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE),
new Scope(Scopes.FITNESS_BODY_READ_WRITE),
new Scope(Scopes.FITNESS_NUTRITION_READ_WRITE),
new Scope(Scopes.FITNESS_LOCATION_READ_WRITE))
.build();
when you get Google Fit data at background, you need to set Email.
I'm trying to retrieve profile information for a signed in user, but problem is that
Plus.PeopleApi.getCurrentPerson(googleApiClient)
always returns null.
Here is my google api client:
googleApiClient = new GoogleApiClient.Builder(getActivity()).addConnectionCallbacks(this).addOnConnectionFailedListener(this).addApi(Plus.API).addScope(Plus.SCOPE_PLUS_PROFILE).addScope(Plus.SCOPE_PLUS_LOGIN).build();
#Override
public void onConnected(Bundle bundle) {
Plus.PeopleApi.loadVisible(googleApiClient, null).setResultCallback(new ResultCallback<People.LoadPeopleResult>() {
#Override
public void onResult(People.LoadPeopleResult loadPeopleResult) {}
});
if (Plus.PeopleApi.getCurrentPerson(googleApiClient) != null) {
.....
}
}
What I'm doing wrong?
The cause of this call returning null was that the Google+ API Was not enabled for your application. Navigate to https://console.developers.google.com, select your project and enable the Google+ API to get it working!
Like this :
and
Check this for more detail link
What would be a good/recommended way of tying up the Google+ api client life cycle with the flow of a multi-activity app? Make the activities depend on the onConnected api client method to trigger its functionality, use it as a one-time only "activation" thing, or maybe something else entirely?
I am currently struggling to understand how to correctly use the Google+ sign in in my Android app, which has more than one activity.
The idea is, in a first phase, to use the G+ sign in just to authenticate the user and be able to get her email, to send notifications and stuff like that. Eventually I plan to roll out other Google functionality like maybe Maps or other Google Play services, so I think it's useful to implement it already.
However, my app is not behaving as expected, and I have narrowed down the issue to the fact that I have not yet understood the G+ sign in app cycle when more than one activity is present.
What is the correct or recommended way to implement this auth method? is there maybe a pattern of sorts that could guide me in the right direction?
For example, I have found a very simple diagram of the life cycle of the api client, but how does this relate to the app flow?
Initially I have a Login Activity, where I put the sign in button. Following Google's guide I am able to sign in, and when the onConnected method is called, I start the Home Activity (kinda like the dashboard or main screen of the app).
This works somewhat. For example, what would be a good way of handling the onStart and onStop for each activity? should I re-connect and re-authenticate the api client every time for every activity? So maybe its a good idea to have a BaseActivity to implement all this.
Another issue is, should I use the same api client object and pass it around somehow, or maybe store it in the Base Activity class? or should I be creating and initializing a new api client object every time?
How about just using the Login Activity to authenticate with G+ and then just get the email and store it in a local database, and flag the user as "authenticated" or "active" or something. That would prevent me from having to re-authenticate every time the app is closed or connection is suspended, even allowing for some battery savings.
The app is not really using G+ posting or any other functionality like that. Ideally it should work well offline, and only need connection for stuff like initial authentication or other one-time only things.
Any suggestions or pointers in the right direction are very much appreciated.
Edit: I have read every guide and tutorial I could find, that uses Google+, and every one of them addresses this from a single activity perspective. I would think this is a common enough problem that it would benefit from a pattern or at least a general guideline.
Reconnecting for each activity is absolutely fine. Broadly there are 3 ways I've seen of people implementing this:
Implement mostly in a baseactivity, and have the others extend that. This is connect/disconnect in each activity, but with code in only one place.
Implement connect/disconnect in a fragment, and include that in activities where auth is needed. This is helpful if you already have a baseactivity you can't extend (e.g. some games cases).
Implement a service to connect/disconnect. This can fire a broadcastintent or similar if sign in is required.
All of these work, and I've seen them all used in real world apps. The main thing to remember is to separate the 99% logic (user is either signed in or signed out, and you are being informed of that) from the relatively rare "signing in at this present moment" use-case. So for example, you might have onConnected/onConnection failed firing a lot, but mostly you are ignoring or just flipping a bit as to the state of the application. Only on a screen with a login button do you need the connection result resolution and onActivityResult stuff. Think of the google play services connection as being mostly about asking for the state of the user, rather than signing them in, and you should be fine.
I agree with Ian Barber's answer but to explain a little further, your Activitys should be considered in two types - Activitys that resolve sign in, and Activitys that require sign in.
Most Activitys do not concern themselves with authenticating the user and will have the same logic in your app. They will create a GoogleApiClient, which connects to the Google Play services process running on the device and reads the cached sign-in state of the user - returning onConnected() if the user is signed in, and onConnectionFailed() if not. Most of your Activitys will want to reset your application state and start your LoginActivity if the user was not signed in. Each Activity should maintain its own instance of GoogleApiClient since it is a lightweight object used to access the shared state held by the Google Play services process. This behaviour could, for example, be encapsulated in a shared BaseActivity class or a shared SignInFragment class, but each instance should have its own GoogleApiClient instance.
Your LoginActivity needs to be implemented differently however. It should also create a GoogleApiClient, but when it receives onConnected() indicating the user is signed in, it should start an appropriate Activity for the user and finish(). When your LoginActivity receives onConnectionFailed() indicating the user is not signed in, you should attempt to resolve sign in issues with startResolutionForResult().
0. TL;DR
For the impatient coder, a working version of the following implementation can be found on GitHub.
After rewriting the login activity code several times in many different apps, the easy (and not so elegant) solution was create the Google API client as a Application class object. But, since the connection state affect the UX flow, I never was happy about with this approach.
Reducing our problem only to the connection concept, we may consider that:
It hides the Google API client.
It has finite states.
It is a (rather) unique.
The current state affect the behavior of the app.
1. Proxy Pattern
Since the Connection encapsulates the GoogleApiClient, it will implement the ConnectionCallbacks and OnConnectionFailedListener:
#Override
public void onConnected(Bundle hint) {
changeState(State.OPENED);
}
#Override
public void onConnectionSuspended(int cause) {
changeState(State.CLOSED);
connect();
}
#Override
public void onConnectionFailed(ConnectionResult result) {
if (currentState.equals(State.CLOSED) && result.hasResolution()) {
changeState(State.CREATED);
connectionResult = result;
} else {
connect();
}
}
Activities can communicate to the Connection class through the methods connect, disconnect, and revoke, but their behaviors are decided by the current state. The following methods are required by the state machine:
protected void onSignIn() {
if (!googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
googleApiClient.connect();
}
}
protected void onSignOut() {
if (googleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(googleApiClient);
googleApiClient.disconnect();
googleApiClient.connect();
changeState(State.CLOSED);
}
}
protected void onSignUp() {
Activity activity = activityWeakReference.get();
try {
changeState(State.OPENING);
connectionResult.startResolutionForResult(activity, REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
changeState(State.CREATED);
googleApiClient.connect();
}
}
protected void onRevoke() {
Plus.AccountApi.clearDefaultAccount(googleApiClient);
Plus.AccountApi.revokeAccessAndDisconnect(googleApiClient);
googleApiClient = googleApiClientBuilder.build();
googleApiClient.connect();
changeState(State.CLOSED);
}
2. State Pattern
This is a behavioral pattern the allow an object to alter its behavior when its internal state changes. The GoF Design Patterns book describes how a TCP connection can be represent by this pattern (which is also our case).
A state from a state machine should be a singleton, and the easiest away of doing it in Java was to create Enum named State as follows:
public enum State {
CREATED {
#Override
void connect(Connection connection) {
connection.onSignUp();
}
#Override
void disconnect(Connection connection) {
connection.onSignOut();
}
},
OPENING {},
OPENED {
#Override
void disconnect(Connection connection) {
connection.onSignOut();
}
#Override
void revoke(Connection connection) {
connection.onRevoke();
}
},
CLOSED {
#Override
void connect(Connection connection) {
connection.onSignIn();
}
};
void connect(Connection connection) {}
void disconnect(Connection connection) {}
void revoke(Connection connection) {}
The Connection class holds the context, i.e. the current state, which defines how the Connection methods connect, disconnect, and revoke will behave:
public void connect() {
currentState.connect(this);
}
public void disconnect() {
currentState.disconnect(this);
}
public void revoke() {
currentState.revoke(this);
}
private void changeState(State state) {
currentState = state;
setChanged();
notifyObservers(state);
}
3. Singleton Pattern
Since there is not need to recreate this class repeatedly, we provide it as a singleton:
public static Connection getInstance(Activity activity) {
if (null == sConnection) {
sConnection = new Connection(activity);
}
return sConnection;
}
public void onActivityResult(int result) {
if (result == Activity.RESULT_OK) {
changeState(State.CREATED);
} else {
changeState(State.CLOSED);
}
onSignIn();
}
private Connection(Activity activity) {
activityWeakReference = new WeakReference<>(activity);
googleApiClientBuilder = new GoogleApiClient
.Builder(activity)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Plus.API, Plus.PlusOptions.builder().build())
.addScope(new Scope("email"));
googleApiClient = googleApiClientBuilder.build();
currentState = State.CLOSED;
}
4. Observable Pattern
The Connection class extends Java Observable, so 1 or more activities can observe the state changes:
#Override
protected void onCreate(Bundle bundle) {
connection = Connection.getInstance(this);
connection.addObserver(this);
}
#Override
protected void onStart() {
connection.connect();
}
#Override
protected void onDestroy() {
connection.deleteObserver(this);
connection.disconnect();
}
#Override
protected void onActivityResult(int request, int result, Intent data) {
if (Connection.REQUEST_CODE == request) {
connection.onActivityResult(result);
}
}
#Override
public void update(Observable observable, Object data) {
if (observable != connection) {
return;
}
// Your presentation logic goes here...
}