package io.nlopez.smartlocation.activity.providers; import android.app.Activity; import android.app.IntentService; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.os.Bundle; import android.support.annotation.NonNull; 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.location.ActivityRecognition; import com.google.android.gms.location.ActivityRecognitionResult; import com.google.android.gms.location.DetectedActivity; import io.nlopez.smartlocation.OnActivityUpdatedListener; import io.nlopez.smartlocation.activity.ActivityProvider; import io.nlopez.smartlocation.activity.ActivityStore; import io.nlopez.smartlocation.activity.config.ActivityParams; import io.nlopez.smartlocation.utils.GooglePlayServicesListener; import io.nlopez.smartlocation.utils.Logger; /** * Created by mrm on 3/1/15. */ public class ActivityGooglePlayServicesProvider implements ActivityProvider, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status> { public static final int RESULT_CODE = 10002; private static final String GMS_ID = "GMS"; private static final String BROADCAST_INTENT_ACTION = ActivityGooglePlayServicesProvider.class.getCanonicalName() + ".DETECTED_ACTIVITY"; private static final String DETECTED_ACTIVITY_EXTRA_ID = "activity"; private GoogleApiClient client; private Logger logger; private OnActivityUpdatedListener listener; private ActivityStore activityStore; private Context context; private boolean shouldStart = false; private boolean stopped = false; private PendingIntent pendingIntent; private ActivityParams activityParams; private final GooglePlayServicesListener googlePlayServicesListener; public ActivityGooglePlayServicesProvider() { this(null); } public ActivityGooglePlayServicesProvider(GooglePlayServicesListener playServicesListener) { googlePlayServicesListener = playServicesListener; } @Override public void init(@NonNull Context context, Logger logger) { this.context = context; this.logger = logger; activityStore = new ActivityStore(context); if (!shouldStart) { this.client = new GoogleApiClient.Builder(context) .addApi(ActivityRecognition.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); client.connect(); } else { logger.d("already started"); } } @Override public void start(OnActivityUpdatedListener listener, @NonNull ActivityParams params) { this.activityParams = params; this.listener = listener; IntentFilter intentFilter = new IntentFilter(BROADCAST_INTENT_ACTION); context.registerReceiver(activityReceiver, intentFilter); if (client.isConnected()) { startUpdating(params); } else if (stopped) { shouldStart = true; client.connect(); stopped = false; } else { shouldStart = true; logger.d("still not connected - scheduled start when connection is ok"); } } private void startUpdating(ActivityParams params) { // TODO wait until the connection is done and retry if (client.isConnected()) { pendingIntent = PendingIntent.getService(context, 0, new Intent(context, ActivityRecognitionService.class), PendingIntent.FLAG_UPDATE_CURRENT); ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(client, params.getInterval(), pendingIntent).setResultCallback(this); } } @Override public void stop() { logger.d("stop"); if (client.isConnected()) { ActivityRecognition.ActivityRecognitionApi.removeActivityUpdates(client, pendingIntent); client.disconnect(); } try { context.unregisterReceiver(activityReceiver); } catch (IllegalArgumentException e) { logger.d("Silenced 'receiver not registered' stuff (calling stop more times than necessary did this)"); } shouldStart = false; stopped = true; } @Override public DetectedActivity getLastActivity() { if (activityStore != null) { return activityStore.get(GMS_ID); } return null; } @Override public void onConnected(Bundle bundle) { logger.d("onConnected"); if (shouldStart) { startUpdating(activityParams); } if (googlePlayServicesListener != null) { googlePlayServicesListener.onConnected(bundle); } } @Override public void onConnectionSuspended(int i) { logger.d("onConnectionSuspended " + i); if (googlePlayServicesListener != null) { googlePlayServicesListener.onConnectionSuspended(i); } } @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { logger.d("onConnectionFailed"); if (googlePlayServicesListener != null) { googlePlayServicesListener.onConnectionFailed(connectionResult); } } private void notifyActivity(final DetectedActivity detectedActivity) { if (listener != null) { listener.onActivityUpdated(detectedActivity); } if (activityStore != null) { activityStore.put(GMS_ID, detectedActivity); } } private BroadcastReceiver activityReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (BROADCAST_INTENT_ACTION.equals(intent.getAction()) && intent.hasExtra(DETECTED_ACTIVITY_EXTRA_ID)) { logger.d("sending new activity"); DetectedActivity detectedActivity = intent.getParcelableExtra(DETECTED_ACTIVITY_EXTRA_ID); notifyActivity(detectedActivity); } } }; public static class ActivityRecognitionService extends IntentService { public ActivityRecognitionService() { super(ActivityRecognitionService.class.getSimpleName()); } @Override protected void onHandleIntent(Intent intent) { if (ActivityRecognitionResult.hasResult(intent)) { ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent); DetectedActivity mostProbableActivity = result.getMostProbableActivity(); // Broadcast an intent containing the activity Intent activityIntent = new Intent(BROADCAST_INTENT_ACTION); activityIntent.putExtra(DETECTED_ACTIVITY_EXTRA_ID, mostProbableActivity); sendBroadcast(activityIntent); } } } @Override public void onResult(@NonNull Status status) { if (status.isSuccess()) { logger.d("Activity update request successful"); } else if (status.hasResolution() && context instanceof Activity) { logger.w("Unable to register, but we can solve this - will startActivityForResult expecting result code " + RESULT_CODE + " (if received, please try again)"); try { status.startResolutionForResult((Activity) context, RESULT_CODE); } catch (IntentSender.SendIntentException e) { logger.e(e, "problem with startResolutionForResult"); } } else { // No recovery. Weep softly or inform the user. logger.e("Registering failed: " + status.getStatusMessage()); } } }