Last active
August 29, 2015 13:56
-
-
Save mbarcia/9095996 to your computer and use it in GitHub Desktop.
Switching to off-line mode (and back online). Part of http://develop-for-android.blogspot.com.es/2014/02/switching-to-off-line-mode-and-back.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.colaborativa.exampleapp; | |
// check your imports, this list may not be complete | |
import android.accounts.Account; | |
import android.accounts.AccountManager; | |
import android.accounts.AccountManagerCallback; | |
import android.accounts.AccountManagerFuture; | |
import android.accounts.OnAccountsUpdateListener; | |
import android.content.Intent; | |
import android.text.TextUtils; | |
import android.util.Log; | |
import android.widget.Toast; | |
// This activity class (not included) would handle your login form. | |
// See onActivityResult() and createNewAccount(). An example can be seen at | |
// http://goo.gl/78CF69 | |
import net.colaborativa.exampleapp.account.AuthenticatorActivity; | |
public class OfflineOnlineActivity extends Activity { | |
// | |
// request codes for activity result callbacks | |
// | |
private static final int AUTH_CONFIRM_CREDENTIALS = 1; | |
private static final int AUTH_CREATE_ACCOUNT = 2; | |
private boolean mStartedRetrievingToken; | |
/** | |
* Gets an auth token | |
* | |
* @param forceLogin true to have the app call the server's login endpoint | |
* @return String token | |
* @throws OfflineException | |
*/ | |
public String retrieveToken(boolean forceLogin) throws OfflineException { | |
MyApiSession gs = MyApiSession.getInstance(); | |
String token; | |
if (gs == null) { | |
// no session found, start processing, and returning empty token at the end | |
AccountManager am = AccountManager.get(ExampleApplication.getInstance()); | |
Account[] accounts = am.getAccountsByType(AuthenticatorActivity.ACCOUNT_TYPE); | |
if (accounts.length > 0) { | |
// An account IS present in the system: | |
// Create the MyApiSession from the first android account | |
gs = MyApiSession.newSessionFor(accounts[0], ""); | |
if (!isRetrievingToken()) { | |
Log.d(ExampleApplication.TAG, "Trying to establish a session " + | |
"upon initialization..."); | |
// launch the token process if it wasn't already launched | |
token = triggerTokenRetrieval(gs, forceLogin); | |
// there is a chance that, if forceLogin is false, the token in | |
// AM's cache is still valid, ie. the app was stopped & started quickly. | |
// So return it! | |
if (!TextUtils.isEmpty(token)) { | |
Log.d(ExampleApplication.TAG, "Oh wow, the token " + | |
"in AM\'s cache is still valid, returning it..."); | |
return token; | |
} | |
// otherwise, continue... | |
} | |
// Not throwing the offline exception, | |
// only returning an empty token for now | |
return ""; | |
} | |
else { | |
// no account present in the system | |
Log.d(ExampleApplication.TAG, "No account found, empty token returned, " + | |
"creating new account..."); | |
// launch the authenticator activity to create a new account | |
createNewAccount(); | |
// and continue... | |
// return empty token for now | |
return ""; | |
} | |
} | |
// gs is NOT null at this point, continuing with a non-null gs variable... | |
if ((gs.isOffline()) && !forceLogin) { | |
Log.d(ExampleApplication.TAG, "Offline, exception thrown."); | |
// Allright! If offline and we're not being told | |
// to re-login, then there's no alternative other than to... | |
throw new OfflineException(); | |
} | |
else { | |
if (gs.isValid() && !forceLogin) { | |
// the session is valid, and we're not being told to re-login | |
// so everything's ok but we still have to "touch" it to update | |
// the last login time, to be + or - in sync with the service behavior | |
gs.touch(); | |
// and finally, return the session token | |
return gs.getToken(); | |
} | |
else { | |
// session is invalid, probably expired, or we are being told to re-login | |
// Note that, in neither case, the token in the AM's cache could be of help | |
if (!isRetrievingToken()) { | |
// if there's not any login in process | |
// invalidate the session prior than anything, as it is not valid | |
gs.invalidate(); | |
Log.d(ExampleApplication.TAG, "Session invalidated, " + | |
"probably expired, empty token returned."); | |
Log.d(ExampleApplication.TAG, "Trying to re-establish " + | |
"the session automatically..."); | |
// launch the token retrieval process, ignoring any | |
// token string possibly coming from AM's cache | |
triggerTokenRetrieval(gs, forceLogin); | |
// and, continue... | |
} | |
// Now, not throwing the offline exception, | |
// just returning an empty token | |
return ""; | |
} | |
} | |
} | |
/** | |
* If forceLogin is false, peaks into AM's account cache for an existing token. | |
* If a token is found, and it passes MyApiSession validation, returns it. If it doesn't | |
* pass validation, makes sure to invalidate that token before going on to | |
* call getAuthToken(), which will then make an effective call to the service. | |
* | |
* @param gs The recipient session to retrieve the token for | |
*/ | |
public String triggerTokenRetrieval(MyApiSession gs, boolean forceLogin) { | |
String token = null; | |
AccountManager am = AccountManager.get(ExampleApplication.getInstance()); | |
token = am.peekAuthToken(gs.getAccount(), AuthenticatorActivity.ACCOUNT_TYPE); | |
if (!TextUtils.isEmpty(token)) { | |
// forceLogin as TRUE acts as a shortcut in the AND condition | |
// to always invalidate the cached token | |
if (gs.isValid() && !forceLogin) { | |
// can touch this | |
gs.revalidate(token); | |
return token; | |
} | |
else { | |
// this makes sure getAuthToken() doesn't return a bogus token later | |
am.invalidateAuthToken(AuthenticatorActivity.ACCOUNT_TYPE, token); | |
Log.i(ExampleApplication.TAG, "Invalidated AM token for account " + gs.getAccount().name); | |
} | |
} | |
// | |
// make an effective call to the login service | |
// | |
// first set the semaphore | |
setStartedRetrievingToken(true); | |
// then give some feedback to the user, ie. | |
// progress.setMessage("Logging you in...").show(); | |
// use the Account Manager to fetch a token | |
final Bundle result = new Bundle(); | |
result.putString(AccountManager.KEY_ACCOUNT_TYPE, AuthenticatorActivity.ACCOUNT_TYPE); | |
am.getAuthToken( | |
gs.getAccount(), | |
AuthenticatorActivity.AUTHTOKEN_TYPE, | |
result, | |
this, | |
new GetAuthTokenCallback(), | |
null); | |
// returning empty string, freeing the main thread flow | |
return ""; | |
} | |
class GetAuthTokenCallback implements AccountManagerCallback<Bundle> { | |
public void run(AccountManagerFuture<Bundle> result) { | |
Bundle bundle; | |
try { | |
// result comes always non-null | |
bundle = result.getResult(); | |
Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT); | |
if (intent != null) { | |
// User input required | |
startActivityForResult(intent, AUTH_CONFIRM_CREDENTIALS); | |
} else { | |
// turn semaphore light to green | |
setStartedRetrievingToken(false); | |
// YAY! token! | |
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN); | |
// WHOA! Next time retrieveToken() is called, it will have a valid | |
// token at hand. | |
MyApiSession gs = MyApiSession.getInstance(); | |
// calling revalidate | |
gs.revalidate(token); | |
// give some feedback to the user here | |
} | |
} catch (Exception e) { | |
// make sure you dismiss here any progress dialog you have open | |
// then log the event | |
Log.e(ExampleApplication.TAG, e.getMessage()); | |
// turn semaphore light to green | |
setStartedRetrievingToken(false); | |
// update the session object | |
gs.setOfflineStatus(true); | |
gs.invalidate(); | |
// here you should provide some further feedback to the user before returning | |
} | |
} | |
} | |
@Override | |
protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |
// this calls any fragment's code first | |
super.onActivityResult(requestCode, resultCode, data); | |
// Check which request we're responding to | |
if (requestCode == AUTH_CONFIRM_CREDENTIALS) { | |
// set semaphore light to green | |
setStartedRetrievingToken(false); | |
// Make sure the request was successful | |
if (resultCode != RESULT_OK) { | |
// go offline here | |
} | |
else { | |
// you're online now, do what you have to | |
} | |
} | |
else if (requestCode == AUTH_CREATE_ACCOUNT) { | |
// set semaphore light to green | |
setStartedRetrievingToken(false); | |
// Make sure the request was successful | |
if (resultCode != RESULT_OK) { | |
// go offline here | |
} | |
else { | |
// you're online now, do what you have to | |
} | |
} | |
} | |
/** | |
* Create a new android account by means of the authenticator activity | |
*/ | |
private void createNewAccount() { | |
Intent intent = new Intent(); | |
Bundle options = new Bundle(); | |
options.putString(AuthenticatorActivity.ARG_ACCOUNT_TYPE, | |
AuthenticatorActivity.ACCOUNT_TYPE); | |
options.putBoolean(AuthenticatorActivity.ARG_IS_NEW_ACCOUNT, | |
true); | |
intent.putExtras(options); | |
ComponentName component = new ComponentName( | |
AuthenticatorActivity.class.getPackage().getName(), | |
AuthenticatorActivity.class.getName()); | |
intent.setComponent(component); | |
startActivityForResult(intent, AUTH_CREATE_ACCOUNT); | |
} | |
private void setStartedRetrievingToken(boolean value) { | |
mStartedRetrievingToken = value; | |
} | |
private boolean isRetrievingToken() { | |
return (mStartedRetrievingToken == true); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment