Skip to content

Instantly share code, notes, and snippets.

@nazmulidris
Created August 29, 2013 22:04
Show Gist options
  • Save nazmulidris/6383985 to your computer and use it in GitHub Desktop.
Save nazmulidris/6383985 to your computer and use it in GitHub Desktop.
My goal was to try allow a developer to seamlessly integrate PlusClient into their Activity and Service classes, without really knowing any callbacks or state machines, and just implementing a few methods (only the ones that they need to) from an abstract base class that they extend. However, the methods that they implement closely mirror PlusCl…
public class PlusService
extends SimplePlusIntentService
{
/** default constructor */
public PlusService() {
super(PlusService.class.getSimpleName());
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// actually do something for service execution
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/** service is executing ... and {@link PlusClient} is connected */
public void onServiceStarted() {
String email = plusClient.getAccountName();
Log.i("Social", "PlusService - PlusClient connected, email address is" + email);
}
/** {@link PlusClient} is connected, and {@link PlusClient#loadPerson} was called */
public void onProfileDataLoaded(ConnectionResult status, Person person) {
Log.i("Social", "PlusService - onPersonLoaded running");
try {
if (status.getErrorCode() == ConnectionResult.SUCCESS) {
Log.i("Social", "Display Name" + person.getDisplayName());
}
}
catch (Exception e) {AndroidUtils.logErr(IconPaths.Social, "PlusService", "onPersonLoaded had a problem", e);}
}
/** {@link PlusClient} is connected, and {@link PlusClient#loadPeople} was called */
public void onCircleDataLoaded(ConnectionResult status, PersonBuffer personBuffer, String nextPageToken) {
Log.i("Social", "PlusService - onPeopleLoaded running");
try {
if (status.getErrorCode() == ConnectionResult.SUCCESS) {
try {
int count = personBuffer.getCount();
for (int i = 0; i < count; i++) {
Log.i("Social", "Circle.Display Name: " + personBuffer.get(i).getDisplayName());
}
}
finally {
personBuffer.close();
}
}
else {
AndroidUtils.logErr(Social, "Error listing people: " + status.getErrorCode());
}
}
catch (Exception e) {AndroidUtils.logErr(IconPaths.Social, "PlusService", "onPeopleLoaded had a problem", e);}
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// load/save data
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// service startup
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/** schedules recurring alarm & starts the service */
public static void startServiceNow(Context ctx) {
// actually start service
ctx.startService(new Intent(ctx, PlusService.class));
Log.i("Social", "PlusService startServiceNow called");
// schedule the alarm (even though it might have been scheduled by {@link BootReceiver}
setRecurringAlarm(ctx);
}
public static void setRecurringAlarm(Context ctx) {
scheduleRecurringAlarm(ctx, PlusService.class, true);
}
public static void cancelRecurringAlarm(Context ctx) {
scheduleRecurringAlarm(ctx, PlusService.class, false);
}
}// end class PlusService
public abstract class PlusSimpleFragmentActivity
extends FragmentActivity
implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener
{
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// abstract methods to impl
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/** called at the end of {@link #onCreate} execution */
protected abstract void onCreateComplete();
/** {@link PlusClient} disconnect handler */
protected abstract void onPlusClientRevokeAccess();
/** {@link PlusClient} connected handler */
protected abstract void onPlusClientSignin();
/** {@link PlusClient} revoke access handler */
protected abstract void onPlusClientSignout();
/** if you have a progress bar widget, this tells you when to show or hide it */
protected abstract void onPlusClientBlockingUI(boolean show);
/**
* if you have signin/connect, signout/disconnect, revokeaccess buttons, this lets you know
* when their states need to be updated
*/
protected abstract void updateConnectButtonState();
/**
* allows you to handle your own {@link #onActivityResult(int, int, Intent)} that can't
* be automatically handled by the {@link PlusClient}.
*/
protected abstract void handleActivityResult(int request, int response, Intent intent);
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// plusclient data
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/** A magic number we will use to know that our sign-in error resolution activity has completed */
public static final int OUR_REQUEST_CODE = 49404;
/** A flag to stop multiple dialogues appearing for the user */
public boolean autoResolveOnFail;
/** this the object that connects to the Google Play Services */
public PlusClient plusClient;
/**
* this is not null if the {@link #onConnectionFailed(ConnectionResult)} ran.
* if this is null then the connect method is still running.
*/
public ConnectionResult mConnectionResult;
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// activity lifecycle stuff
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// plus client setup stuff
plusClient = new PlusClient.Builder(this, this, this)
.setScopes(PlusUtils.getScopeArray(this))
.setVisibleActivities(PlusUtils.getMomentTypeArray(this))
.build();
onCreateComplete();
}
/** sign in the user */
public void signIn() {
if (!plusClient.isConnected()) {
// Show the dialog as we are now signing in.
_setProgressBarVisible(true);
// Make sure that we will start the resolution (e.g. fire the intent and pop up a dialog for the user)
// for any errors that come in.
autoResolveOnFail = true;
// We should always have a connection result ready to resolve, so we can start that process.
if (mConnectionResult != null) {
_startResolution();
}
else {
// If we don't have one though, we can start connect in
// order to retrieve one.
_initiatePlusClientConnect();
}
}
updateConnectButtonState();
}
/**
* feedback from sam: About the connected and connecting: whenever you call plusClient.connect()
* surround it with if(!connected and !connecting) { ... }.
* Even if you're 100% sure this if check will always pass, it makes your class more
* extensible. That's another thing that I thought I didn't need from PCF and it
* turned out to solve some edge cases.
*/
private void _initiatePlusClientConnect() {
if (!plusClient.isConnected() && !plusClient.isConnecting()) {
plusClient.connect();
}
}
/**
* feedback from sam: And for disconnect, make sure to only call it if(connected),
* otherwise it can throw and error (or it at least logs one).
*/
private void _initiatePlusClientDisconnect() {
if (plusClient.isConnected()) { plusClient.disconnect(); }
}
/** sign out the user (so they can switch to another account) */
public void signOut() {
// We only want to sign out if we're connected.
if (plusClient.isConnected()) {
// Clear the default account in order to allow the user to potentially choose a different account from the
// account chooser.
plusClient.clearDefaultAccount();
// Disconnect from Google Play Services, then reconnect in order to restart the process from scratch.
_initiatePlusClientDisconnect();
AndroidUtils.showToastShort(getApplicationContext(),
"Sign out successful!");
}
updateConnectButtonState();
}
/** Prior to disconnecting, run clearDefaultAccount() */
public void revokeAccess() {
if (plusClient.isConnected()) {
// Clear the default account as in the Sign Out.
plusClient.clearDefaultAccount();
// Go away and revoke access to this entire application. This will call back to onAccessRevoked when it is
// complete as it needs to go away to the Google authentication servers to revoke all token.
plusClient.revokeAccessAndDisconnect(new PlusClient.OnAccessRevokedListener() {
public void onAccessRevoked(ConnectionResult result) {
updateConnectButtonState();
onPlusClientRevokeAccess();
}
});
}
}
@Override
protected void onStart() {
super.onStart();
_initiatePlusClientConnect();
}
@Override
protected void onStop() {
super.onStop();
_initiatePlusClientDisconnect();
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// plusclient isConnecting?
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public boolean plusClientIsConnecting = false;
public boolean isPlusClientConnecting() {return plusClientIsConnecting;}
private void _setProgressBarVisible(boolean flag) {
plusClientIsConnecting = flag;
onPlusClientBlockingUI(flag);
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// plusclient orchestration
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/**
* A helper method to flip the mResolveOnFail flag and start the resolution
* of the ConnenctionResult from the failed connect() call.
*/
private void _startResolution() {
try {
// Don't start another resolution now until we have a result from the activity we're about to start.
autoResolveOnFail = false;
// If we can resolve the error, then call start resolution and pass it an integer tag we can use to track.
// This means that when we get the onActivityResult callback we'll know its from being started here.
mConnectionResult.startResolutionForResult(this, OUR_REQUEST_CODE);
}
catch (IntentSender.SendIntentException e) {
// Any problems, just try to connect() again so we get a new ConnectionResult.
mConnectionResult = null;
_initiatePlusClientConnect();
}
}
/**
* connection failed, and this is the result of the resolution attempt by plusclient
*
* @see #onConnectionFailed(ConnectionResult)
*/
@Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
updateConnectButtonState();
if (requestCode == OUR_REQUEST_CODE && responseCode == RESULT_OK) {
// If we have a successful result, we will want to be able to resolve any further errors, so turn on
// resolution with our flag.
autoResolveOnFail = true;
// If we have a successful result, lets call connect() again. If there are any more errors to
// resolve we'll get our onConnectionFailed, but if not, we'll get onConnected.
_initiatePlusClientConnect();
}
else if (requestCode == OUR_REQUEST_CODE && responseCode != RESULT_OK) {
// If we've got an error we can't resolve, we're no longer in the midst of signing in, so we can stop
// the progress spinner.
_setProgressBarVisible(false);
}
else {
handleActivityResult(requestCode, responseCode, intent);
}
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// plusclient callbacks
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/** connection successful; called by PlusClient */
@Override
public void onConnected(Bundle connectionHint) {
updateConnectButtonState();
_setProgressBarVisible(false);
String emailAddress = plusClient.getAccountName();
StringBuilder sb = new StringBuilder();
sb.append("PlusClient is connected. User email: ").append(emailAddress);
AndroidUtils.showToastLong(getApplicationContext(), sb.toString());
onPlusClientSignin();
}
/** connection ended successfully; called by PlusClient */
@Override
public void onDisconnected() {
updateConnectButtonState();
onPlusClientSignout();
}
/**
* connection failed for some reason; called by PlusClient
* try and resolve
*
* @see #onActivityResult(int, int, Intent)
*/
@Override
public void onConnectionFailed(ConnectionResult result) {
updateConnectButtonState();
// Most of the time, the connection will fail with a user resolvable result. We can store that in our
// mConnectionResult property ready for to be used when the user clicks the sign-in button.
if (result.hasResolution()) {
mConnectionResult = result;
if (autoResolveOnFail) {
// This is a local helper function that starts the resolution of the problem, which may be
// showing the user an account chooser or similar.
_startResolution();
}
}
}
}//end class PlusSimpleFragmentActivity
public class SettingsActivity
extends PlusSimpleFragmentActivity
{
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// app specific data
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
private ViewHolder viewHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setup UI stuff
viewHolder = new ViewHolder();
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// abstract method impl
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/** called at the end of {@link #onCreate} execution */
@Override
protected void onCreateComplete() {
// stop the PlusService from running as long as this screen is up
// since user might disconnect from PlusClient in this screen
PlusService.cancelRecurringAlarm(this);
}
/** {@link PlusClient} disconnect handler */
@Override
protected void onPlusClientSignout() {
AndroidUtils.showToastLong(getApplicationContext(), "PlusClient is disconnected");
}
/** {@link PlusClient} connected handler */
@Override
protected void onPlusClientSignin() {
// start the PlusService &
// schedule recurring alarm to keep it running
PlusService.startServiceNow(getAppData());
}
/** {@link PlusClient} revoke access handler */
@Override
protected void onPlusClientRevokeAccess() {
// plusClient is now disconnected and access has been revoked. Trigger app logic to comply with the
// developer policies
AndroidUtils.showToastShort(getApplicationContext(),
"Todo - Access revoked ... should delete data now");
}
/** if you have a progress bar widget, this tells you when to show or hide it */
@Override
protected void onPlusClientBlockingUI(boolean show) {
viewHolder._showProgressBar(show);
}
/**
* if you have signin/connect, signout/disconnect, revokeaccess buttons, this lets you know
* when their states need to be updated
*/
@Override
protected void updateConnectButtonState() {
viewHolder._updateButtonState();
}
@Override
protected void handleActivityResult(int request, int response, Intent intent) {}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// token stuff
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/**
* <ol>
* <li><a href="http://goo.gl/9mH8T">more info on validating tokens with tokeninfo endpoint</a>.
* <li><a href="http://goo.gl/qV0Sj9">more info on using GoogleAuthUtil to get access token</a>.
* </ol>
*/
private void _retrieveToken() {
AsyncTask task = new AsyncTask() {
protected void onPostExecute(Object o) {
viewHolder.btn_retrievetoken.setEnabled(true);
}
protected void onPreExecute() {
viewHolder.btn_retrievetoken.setEnabled(false);
}
@Override
protected Object doInBackground(Object... params) {
try {
String requestedOAuth2Scopes = PlusUtils.getOauthScopes(getApplicationContext());
AndroidUtils.log(IconPaths.Social, "OAuth2 scopes requested", requestedOAuth2Scopes);
// We can retrieve the token to check via tokeninfo or to pass to a service-side application.
String token = GoogleAuthUtil.getToken(getAppData(),
plusClient.getAccountName(),
requestedOAuth2Scopes);
AndroidUtils.log(IconPaths.Social, "OAuth2 auth token ", token);
// resolve token via tokeninfo endpoint
String tokenInfoEndpoint =
String.format("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=%s",
token);
HttpClient hc = getAppData().netClient.getHttpClient();
HttpResponse resp = hc.execute(new HttpGet(tokenInfoEndpoint));
String tokenInfoJSONString = EntityUtils.toString(resp.getEntity());
AndroidUtils.log(IconPaths.Social, "OAuth2 token info", tokenInfoJSONString);
PopupInfoDialogFragment newFragment = new PopupInfoDialogFragment(tokenInfoJSONString);
newFragment.show(getSupportFragmentManager(),
PopupInfoDialogFragment.class.getSimpleName());
}
catch (Exception e) {
AndroidUtils.logErr(IconPaths.Social, "problem getting OAuth2 auth token", e);
}
return null;
}
};
task.execute((Void) null);
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// view holder stuff
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public class ViewHolder {
private TextView lbl_signin_status;
public Button btn_signin;
public Button btn_signout;
public Button btn_revokeaccess;
public Button btn_retrievetoken;
public Button[] btnRay;
public ViewHolder() {
setContentView(R.layout.settingsactivity);
btn_signin = (Button) findViewById(R.id.btn_sign_in);
btn_signout = (Button) findViewById(R.id.btn_sign_out);
btn_revokeaccess = (Button) findViewById(R.id.btn_revoke_access);
btn_retrievetoken = (Button) findViewById(R.id.btn_retrieve_token);
lbl_signin_status = (TextView) findViewById(R.id.lbl_signin_status);
btnRay = new Button[]{btn_signin, btn_signout, btn_revokeaccess, btn_retrievetoken};
_wireButtons();
}
private void _showProgressBar(boolean enabled) {
// signin is in progress ... so disable everything
if (enabled) {
lbl_signin_status.setVisibility(View.VISIBLE);
for (Button button : btnRay) {button.setEnabled(false);}
}
// signin is done ... so enable everything
else {
lbl_signin_status.setVisibility(View.GONE);
for (Button button : btnRay) {button.setEnabled(true);}
}
}
private void _wireButtons() {
// wire the sign-in button
btn_signin.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
signIn();
}
});
// wire the sign-out button
btn_signout.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
signOut();
}
});
// wire the revoke-access button
btn_revokeaccess.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
revokeAccess();
}
});
// wire the retrieve token button
btn_retrievetoken.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
_retrieveToken();
}
});
}
private void _updateButtonState() {
if (plusClient.isConnected()) {
// connected
btn_signin.setVisibility(View.GONE);
btn_signout.setVisibility(View.VISIBLE);
btn_revokeaccess.setVisibility(View.VISIBLE);
btn_retrievetoken.setVisibility(View.VISIBLE);
}
else {
// not connected
btn_signout.setVisibility(View.GONE);
btn_revokeaccess.setVisibility(View.GONE);
btn_retrievetoken.setVisibility(View.GONE);
btn_signin.setVisibility(View.VISIBLE);
}
}
}// end class ViewHolder
}// end class SettingsActivityWithFragment
public abstract class SimplePlusIntentService
extends IntentService
implements GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
PlusClient.OnPersonLoadedListener,
PlusClient.OnPeopleLoadedListener
{
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// abstract methods
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
protected abstract void onServiceStarted();
protected abstract void onCircleDataLoaded(ConnectionResult result, PersonBuffer persons, String nextPageToken);
protected abstract void onProfileDataLoaded(ConnectionResult result, Person person);
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// data
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/**
* {@link PlusClient} instance managed by this service; all the 'work' happens in the callback
* methods that are registered with this object, and orchestrated by it (on the main thread)
*/
protected PlusClient plusClient;
/** used to keep track of whether {@link #plusClient} can disconnect or not */
protected TaskMonitor taskMonitor;
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// constructor
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/** @param name only useful for debugging */
public SimplePlusIntentService(String name) {
super(name);
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// android service lifecycle hooks
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/**
* this simply tells the {@link PlusClient#connect()} to run, which will cause
* {@link #onConnected(Bundle)}, where all the real work actually happens.
*/
protected void onHandleIntent(Intent intent) {
Log.i("Social", "PlusService Service Started!");
plusClient = new PlusClient.Builder(this, this, this)
.setScopes(PlusUtils.getScopeArray(getApplicationContext()))
.setVisibleActivities(PlusUtils.getMomentTypeArray(getApplicationContext()))
.build();
plusClient.connect();
}
public void onCreate() {
super.onCreate();
taskMonitor = new TaskMonitor();
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// callbacks
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public void onPeopleLoaded(ConnectionResult result, PersonBuffer persons, String nextPageToken) {
try {
onCircleDataLoaded(result, persons, nextPageToken);
}
finally {
_initiateDisconnect(TaskMonitor.TaskName.CircleInformation);
}
}
public void onPersonLoaded(ConnectionResult result, Person person) {
try {
onProfileDataLoaded(result, person);
}
finally {
_initiateDisconnect(TaskMonitor.TaskName.MyProfileInformation);
}
}
protected void _initiateDisconnect(TaskMonitor.TaskName task) {
// mark this task as complete
taskMonitor.done(task);
Log.i("Social", "PlusService - marking task as done" + task.toString());
// check to see if all tasks are done before exiting
if (taskMonitor.areAllDone()) {
plusClient.disconnect();
Log.i("Social", "PlusService - disconnected!");
}
else {
Log.i("Social", "PlusService - can't disconnect yet - a task is pending execution!");
}
}
/**
* this is where the actual work of this service happens.
* <p/>
* only do something if the {@link PlusClient} can connect (ie, the user has already
* granted their consent earlier
*/
public void onConnected(Bundle bundle) {
try {
onServiceStarted();
plusClient.loadPerson(this, "me");
plusClient.loadPeople(this, Person.Collection.VISIBLE);
}
catch (Exception e) {
AndroidUtils.logErr(IconPaths.Social, "PlusService had a problem", e);
}
}
/** ignore this */
public void onDisconnected() {
Log.i("Social", "PlusService - PlusClient disconnected, ending service");
}
/** ignore this */
public void onConnectionFailed(ConnectionResult result) {
Log.i("Social", "PlusService - PlusClient connection failed, ending service");
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// determine when you can call PlusClient.disconnect
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/**
* holds information on whether all the tasks are completed or not;
* used to decide when to disconnect {@link #plusClient}
*/
static class TaskMonitor {
/** list all the tasks you want monitored */
enum TaskName {
MyProfileInformation, CircleInformation
}
HashMap<TaskName, Boolean> doneMap = new HashMap<TaskName, Boolean>();
/** init the {@link #doneMap} */
public TaskMonitor() {
for (TaskName task : TaskName.values()) {
notDone(task);
}
}
/** mark a {@link TaskName} not done */
public void notDone(TaskName task) {
doneMap.put(task, false);
}
/** mark a {@link TaskName} done */
public void done(TaskName task) {
doneMap.put(task, true);
}
/** check to see if a {@link TaskName} is done or not */
public boolean isDone(TaskName task) {
try {
return doneMap.get(task);
}
catch (Exception e) {return false;}
}
/** check to see if all {@link TaskName} are done or not */
public boolean areAllDone() {
boolean allDone = true;
for (TaskName task : TaskName.values()) {
allDone = isDone(task) && allDone;
}
return allDone;
}
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// alarm
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/**
* helper method to create a recurring alarm that's used to fire off this intent service.
* there's no need to save a reference to the previously created alarm (in the case of removing
* the alarm), since you can just create a matching {@link PendingIntent}, see docs
* <a href="http://goo.gl/9izcQ">here</a>.
*
* @param ctx caller has to supply this since this is a static method
* @param enable true means create the alarm, and start the service (but this might not happen until the 2nd alarm
* cycle, and this is non deterministic, since this is inexact repeating).
* false means cancel it
*/
public static void scheduleRecurringAlarm(Context ctx, Class claz, boolean enable) {
PendingIntent pendingIntent = PendingIntent.getService(ctx,
-1,
new Intent(ctx, claz),
PendingIntent.FLAG_UPDATE_CURRENT
);
AlarmManager mgr = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
if (enable) {
// remove any previously scheduled alarms that match the pendingIntent
mgr.cancel(pendingIntent);
// add an alarm to fire the pendingIntent
mgr.setInexactRepeating(AlarmManager.RTC,
java.lang.System.currentTimeMillis() + MyIntentServiceStartDelay_ms,
MyIntentServiceRepeatDelay_ms,
//AlarmManager.INTERVAL_FIFTEEN_MINUTES,
pendingIntent);
AndroidUtils.log(Social, "PlusService - alarm added to Android OS");
}
else {
// this cancels all alarms that match the pendingIntent
mgr.cancel(pendingIntent);
AndroidUtils.log(Social, "PlusService - alarm removed from Android OS");
}
}
}//end class SimplePlusIntentService
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment