text: package com.o3dr.services.android.lib.util.googleApi;
  
  import android.content.Context;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.HandlerThread;
  import android.util.Log;
  
  import com.google.android.gms.common.ConnectionResult;
  import com.google.android.gms.common.GooglePlayServicesUtil;
  import com.google.android.gms.common.api.Api;
  import com.google.android.gms.common.api.GoogleApiClient;
  
  import java.util.concurrent.LinkedBlockingQueue;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  /**
   * Handles the lifecycle for the google api client. Also takes care of running submitted tasks
   * when the google api client is connected.
   */
  public class GoogleApiClientManager implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
  
      private final static String TAG = GoogleApiClientManager.class.getSimpleName();
  
      public interface ManagerListener {
          void onGoogleApiConnectionError(ConnectionResult result);
  
          void onUnavailableGooglePlayServices(int status);
  
          void onManagerStarted();
  
          void onManagerStopped();
      }
  
      /**
       * Manager background thread used to run the submitted google api client tasks.
       */
      private final Runnable mDriverRunnable = new Runnable() {
          @Override
          public void run() {
              try {
                  while (isStarted.get()) {
                      if (!mGoogleApiClient.isConnected()) {
                          stop();
                          continue;
                      }
  
                      final GoogleApiClientTask task = mTaskQueue.take();
                      if (task == null)
                          continue;
  
                      if (task.mRunOnBackgroundThread) {
                          mBgHandler.post(task);
                      } else {
                          mMainHandler.post(task);
                      }
                  }
              } catch (InterruptedException e) {
                  Log.v(TAG, e.getMessage(), e);
              }
          }
      };
  
      private final GoogleApiClientTask stopTask = new GoogleApiClientTask() {
          @Override
          protected void doRun() {
              stop();
          }
      };
  
      private final AtomicBoolean isStarted = new AtomicBoolean(false);
  
      private Thread mDriverThread;
  
      /**
       * This handler is in charge of running google api client tasks on the calling thread.
       */
      private final Handler mMainHandler;
  
      /**
       * This handler is in charge of running google api client tasks on the background thread.
       */
      private Handler mBgHandler;
      private HandlerThread mBgHandlerThread;
  
      /**
       * Application context.
       */
      private final Context mContext;
  
      /**
       * Handle to the google api client.
       */
      private final GoogleApiClient mGoogleApiClient;
  
      private ManagerListener listener;
  
      /**
       * Holds tasks that needs to be run using the google api client.
       * A background thread will be blocking on this queue until new tasks are inserted. In which
       * case, it will retrieve the new task, and process it.
       */
      private final LinkedBlockingQueue<GoogleApiClientTask> mTaskQueue = new LinkedBlockingQueue<>();
  
      public GoogleApiClientManager(Context context, Handler handler,
                                    Api<? extends Api.ApiOptions.NotRequiredOptions>[] apis) {
          mContext = context;
          mMainHandler = handler;
  
          final GoogleApiClient.Builder apiBuilder = new GoogleApiClient.Builder(context);
          for (Api<? extends Api.ApiOptions.NotRequiredOptions> api : apis) {
              apiBuilder.addApi(api);
          }
  
          mGoogleApiClient = apiBuilder
                  .addConnectionCallbacks(this)
                  .addOnConnectionFailedListener(this)
                  .build();
      }
  
      public void setManagerListener(ManagerListener listener) {
          this.listener = listener;
      }
  
      private void destroyBgHandler() {
          if (mBgHandlerThread != null && mBgHandlerThread.isAlive()) {
              mBgHandlerThread.quit();
              mBgHandlerThread.interrupt();
              mBgHandlerThread = null;
          }
  
          mBgHandler = null;
      }
  
      private void destroyDriverThread() {
          if (mDriverThread != null && mDriverThread.isAlive()) {
              mDriverThread.interrupt();
              mDriverThread = null;
          }
      }
  
      private void initializeBgHandler() {
          if (mBgHandlerThread == null || mBgHandlerThread.isInterrupted()) {
              mBgHandlerThread = new HandlerThread("GAC Manager Background Thread");
              mBgHandlerThread.start();
              mBgHandler = null;
          }
  
          if (mBgHandler == null) {
              mBgHandler = new Handler(mBgHandlerThread.getLooper());
          }
      }
  
      private void initializeDriverThread() {
          if (mDriverThread == null || mDriverThread.isInterrupted()) {
              mDriverThread = new Thread(mDriverRunnable, "GAC Manager Driver Thread");
              mDriverThread.start();
          }
      }
  
      /**
       * Adds a task to the google api client manager tasks queue. This task will be scheduled to
       * run on the calling thread.
       *
       * @param task task making use of the google api client.
       * @return true if the task was successfully added to the queue.
       * @throws IllegalStateException is the start() method was not called.
       */
      public boolean addTask(GoogleApiClientTask task) {
          if (!isStarted()) {
              Log.d(TAG, "GoogleApiClientManager is not started.");
              return false;
          }
  
          task.gApiClient = mGoogleApiClient;
          task.taskQueue = mTaskQueue;
          task.mRunOnBackgroundThread = false;
          return mTaskQueue.offer(task);
      }
  
      /**
       * Adds a task to the google api client manager tasks queue. This task will be scheduled to
       * run on a background thread.
       *
       * @param task task making use of the google api client.
       * @return true if the task was successfully added to the queue.
       * @throws IllegalStateException is the start() method was not called.
       */
      public boolean addTaskToBackground(GoogleApiClientTask task) {
          if (!isStarted()) {
              Log.d(TAG, "GoogleApiClientManager is not started.");
              return false;
          }
  
          task.gApiClient = mGoogleApiClient;
          task.taskQueue = mTaskQueue;
          task.mRunOnBackgroundThread = true;
          return mTaskQueue.offer(task);
      }
  
      /**
       * @return true the google api client manager was started.
       */
      private boolean isStarted() {
          return isStarted.get();
      }
  
      /**
       * Activates the google api client manager.
       */
      public void start() {
          //Check if google play services is available.
          final int playStatus = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext);
          final boolean isValid = playStatus == ConnectionResult.SUCCESS;
  
          if (isValid) {
              //Clear the queue
              mTaskQueue.clear();
  
              //Toggle the started flag
              isStarted.set(true);
              if (mGoogleApiClient.isConnected()) {
                  onConnected(null);
              } else if (!mGoogleApiClient.isConnecting()) {
                  //Connect to the google api.
                  mGoogleApiClient.connect();
              }
          } else {
              Log.e(TAG, "Google Play Services is unavailable.");
              if (listener != null)
                  listener.onUnavailableGooglePlayServices(playStatus);
          }
      }
  
  //    private boolean isGooglePlayServicesValid(){
  //        // Check for the google play services is available
  //
  //        if(!isValid){
  //            PendingIntent errorPI = GooglePlayServicesUtil.getErrorPendingIntent(playStatus, mContext, 0);
  //            if(errorPI != null){
  //                try {
  //                    errorPI.send();
  //                } catch (PendingIntent.CanceledException e) {
  //                    Log.e(TAG, "Seems the pending intent was cancelled.", e);
  //                }
  //            }
  //        }
  //
  //        return isValid;
  //    }
  
      /**
       * Release the resources used by this manager.
       * After calling this method, start() needs to be called again to use that manager again.
       */
      private void stop() {
          isStarted.set(false);
          destroyDriverThread();
          destroyBgHandler();
  
          mTaskQueue.clear();
          if (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting()) {
              mGoogleApiClient.disconnect();
          }
  
          if(listener != null)
              listener.onManagerStopped();
      }
  
      public void stopSafely(){
          addTask(stopTask);
      }
  
      @Override
      public void onConnected(Bundle bundle) {
          initializeBgHandler();
          initializeDriverThread();
  
          if (listener != null)
              listener.onManagerStarted();
      }
  
      @Override
      public void onConnectionSuspended(int i) {
  
      }
  
      @Override
      public void onConnectionFailed(ConnectionResult connectionResult) {
          if(listener != null)
              listener.onGoogleApiConnectionError(connectionResult);
  
          stop();
      }
  
      /**
       * Type for the google api client tasks.
       */
      public static abstract class GoogleApiClientTask implements Runnable {
  
          /**
           * If true, this task will be scheduled to run on a background thread.
           * Otherwise, it will run on the calling thread.
           */
          private boolean mRunOnBackgroundThread = false;
          private GoogleApiClient gApiClient;
          private LinkedBlockingQueue<GoogleApiClientTask> taskQueue;
  
          protected GoogleApiClient getGoogleApiClient() {
              return gApiClient;
          }
  
          @Override
          public void run() {
              if (!gApiClient.isConnected()) {
                  //Add the task back to the queue.
                  taskQueue.offer(this);
                  return;
              }
  
              //Run the task
              doRun();
          }
  
          protected abstract void doRun();
  
      }
  }
  