/**
  * This method is a bit dangerous and it makes some assumptions. Ideally, if we are connected to
  * an MMF app we would just ask it for the app state and get that state in the {@link
  * com.mapmyfitness.android.mmfremote.RemoteCommandListener#onAppStateEvent(com.mapmyfitness.android.mmfremote.AppState)}
  * callback. However, if we are not connected to an MMF app we can't be 100% sure which MMF app
  * you intend to check for if the {@link com.mapmyfitness.android.mmfremote.AppPackage} is not set
  * in this class. Lastly, if no {@link com.mapmyfitness.android.mmfremote.RemoteCommandListener}
  * is set, you will not receieve a callback. In that case the method will appear to fail silently.
  * These are steps we take:
  *
  * <ol>
  *   <li>If MMF app is connected to SDK: ask it the current state and listen for it in the {@link
  *       com.mapmyfitness.android.mmfremote.RemoteCommandListener#onAppStateEvent(com.mapmyfitness.android.mmfremote.AppState)}
  *       callback. Remember if no callback is set, you will not get any feedback about the state
  *       of the app.
  *   <li>If we have an {@link com.mapmyfitness.android.mmfremote.AppPackage} set, then check it to
  *       see if app is installed. If the app is installed we will send a callback with {@link
  *       com.mapmyfitness.android.mmfremote.AppState#APP_NOT_CONNECTED}. If the app is not
  *       installed we will send a callback with {@link
  *       com.mapmyfitness.android.mmfremote.AppState#APP_NOT_INSTALLED}.
  *   <li>If the {@link com.mapmyfitness.android.mmfremote.AppPackage} is not set, we are not sure
  *       which one of the 10 apps you are requesting. In that case, we will use {@link
  *       #findInstalledApps()} to see if any of our supported apps are installed. If at least one
  *       of them is installed we return {@link
  *       com.mapmyfitness.android.mmfremote.AppState#APP_NOT_CONNECTED}. If none of our supported
  *       are installed, we return {@link
  *       com.mapmyfitness.android.mmfremote.AppState#APP_NOT_INSTALLED}.
  * </ol>
  *
  * A better way to get an app's state is to use {@link
  * #requestAppState(com.mapmyfitness.android.mmfremote.AppPackage)} passing it the {@link
  * com.mapmyfitness.android.mmfremote.AppPackage} you are interested in. That will be more precise
  * by narrowing down the options to one app.
  */
 public void requestAppState() {
   if (isAppConnected()) {
     mRemoteCommunication.getCurrentStateCommand();
   } else if (mAppPackage != null) {
     AppInfo appInfo = getAppInfo(mAppPackage);
     if (appInfo.isInstalled()) {
       if (mRemoteCommunication.getCommandListener() != null) {
         mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_CONNECTED);
       }
     } else {
       if (mRemoteCommunication.getCommandListener() != null) {
         mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_INSTALLED);
       }
     }
   } else {
     if (findInstalledApps().size() > 0) {
       if (mRemoteCommunication.getCommandListener() != null) {
         mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_CONNECTED);
       }
     } else {
       if (mRemoteCommunication.getCommandListener() != null) {
         mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_INSTALLED);
       }
     }
   }
 }
  private RemoteManager(Builder init) {
    mRemoteCommunication = new RemoteCommunication();

    this.mContext = init.context;
    if (init.context != null) {
      mContext = init.context;
    }
    if (init.appPackage != null) {
      mAppPackage = init.appPackage;
    }
    mRemoteCommunication.setDataListener(init.dataListener);
    mRemoteCommunication.setCommandListener(init.commandListener);
    if (init.statsCache != null) {
      mRemoteCommunication.setStatsCache(init.statsCache);
    }
  }
 /**
  * Try to connect to the MMF app in the currently assigned {@link
  * com.mapmyfitness.android.mmfremote.AppPackage}. If this enum is null, this method will simply
  * return false. You can use {@link
  * #tryToConnectToMmfApp(com.mapmyfitness.android.mmfremote.AppPackage)} if you would like to pass
  * it one at the time you try to connect, or use {@link #getAppPackage()} to check if one is
  * present.
  *
  * @return
  */
 public boolean tryToConnectToMmfApp() {
   if (!isAppConnected() && mAppPackage != null) {
     return mRemoteCommunication.onConnectionTried(mContext, mAppPackage.getIntentActionFilter());
   } else {
     return true;
   }
 }
 /**
  * This method tries to check the state of an app represented by an {@link
  * com.mapmyfitness.android.mmfremote.AppPackage}. Remember, like {@link #requestAppState()}, it
  * no {@link com.mapmyfitness.android.mmfremote.RemoteCommandListener} is set, you will not
  * receive any callbacks on the state of the app. These are the steps taken in this method:
  *
  * <ol>
  *   <li>If the app is connected, we check if the {@link
  *       com.mapmyfitness.android.mmfremote.AppPackage} in the parameter is equal to the one set
  *       in this class. In that case, we ask the MMF app to send us the state. If the package in
  *       the parameter is different than the one held by this page, we check to see if it is
  *       installed or just not connected.
  *   <li>If the app is not connected, we check if the package is installed, if so, we return
  *       {@link com.mapmyfitness.android.mmfremote.AppState#APP_NOT_CONNECTED}
  *   <li>If none of the above qualify, we return {@link
  *       com.mapmyfitness.android.mmfremote.AppState#APP_NOT_INSTALLED}
  * </ol>
  *
  * You can also use the {@link #getAppInfo(com.mapmyfitness.android.mmfremote.AppPackage)} method
  * and the returned {@link com.mapmyfitness.android.mmfremote.AppInfo} object to see if the app is
  * installed or not.
  *
  * @param appPackage
  */
 public void requestAppState(AppPackage appPackage) {
   if (isAppConnected()) {
     if (mAppPackage != null && mAppPackage.equals(appPackage)) {
       mRemoteCommunication.getCurrentStateCommand();
     } else if (mAppPackage != null && getAppInfo(appPackage).isInstalled()) {
       if (mRemoteCommunication.getCommandListener() != null) {
         mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_CONNECTED);
       }
     } else {
       if (mRemoteCommunication.getCommandListener() != null) {
         mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_INSTALLED);
       }
     }
   } else if (getAppInfo(appPackage).isInstalled()) {
     if (mRemoteCommunication.getCommandListener() != null) {
       mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_CONNECTED);
     }
   } else {
     if (mRemoteCommunication.getCommandListener() != null) {
       mRemoteCommunication.getCommandListener().onAppStateEvent(AppState.APP_NOT_INSTALLED);
     }
   }
 }
 /**
  * Check if app is currently connected to one of the MMF apps. You can then use {@link
  * #getAppPackage()} to check which app it is.
  *
  * @return whether this is bound to an MMF's app service
  */
 public boolean isAppConnected() {
   return mRemoteCommunication.isBound();
 }
 /**
  * Tell the MMF app to cancel a workout start. This may happen if there was no GPS and the app is
  * waiting for the user to confirm that he wants to start the workout withou it . This method will
  * check if the app is connected. If it is not it will throw a {@link
  * com.mapmyfitness.android.mmfremote.RemoteException} with {@link
  * com.mapmyfitness.android.mmfremote.RemoteException.Code#APP_NOT_CONNECTED}
  *
  * @throws com.mapmyfitness.android.mmfremote.RemoteException if app is not connected.
  */
 public void cancelWorkoutStart() throws RemoteException {
   checkIsBound();
   mRemoteCommunication.cancelWorkoutStartCommand();
 }
 /**
  * Tell the MMF app to start a workout, whether or not a GPS fix is available. This method will
  * check if the app is connected. If it is not it will throw a {@link
  * com.mapmyfitness.android.mmfremote.RemoteException} with {@link
  * com.mapmyfitness.android.mmfremote.RemoteException.Code#APP_NOT_CONNECTED}
  *
  * @throws com.mapmyfitness.android.mmfremote.RemoteException if app is not connected.
  */
 public void startWithoutGpsCommand() throws RemoteException {
   checkIsBound();
   mRemoteCommunication.startWithoutGpsCommand();
 }
 /**
  * Tell the MMF app to save a workout. Saving means this workout will be saved to our servers
  * under the account the user is logged in the MMF app. If the phone currently does not have a
  * data connection, the workout will be saved locally to the phone and the MMF app will try to
  * upload it later. This method will check if the app is connected. If it is not it will throw a
  * {@link com.mapmyfitness.android.mmfremote.RemoteException} with {@link
  * com.mapmyfitness.android.mmfremote.RemoteException.Code#APP_NOT_CONNECTED}
  *
  * @throws com.mapmyfitness.android.mmfremote.RemoteException if app is not connected.
  */
 public void saveWorkoutCommand() throws RemoteException {
   checkIsBound();
   mRemoteCommunication.saveCommand();
 }
 /**
  * Tell the MMF app to discard a workout. This will erase all data from the workout that was
  * recorded permanently. If user triggers this method you may want to have him confirm his action.
  * This method will check if the app is connected. If it is not it will throw a {@link
  * com.mapmyfitness.android.mmfremote.RemoteException} with {@link
  * com.mapmyfitness.android.mmfremote.RemoteException.Code#APP_NOT_CONNECTED}
  *
  * @throws com.mapmyfitness.android.mmfremote.RemoteException if app is not connected.
  */
 public void discardWorkoutCommand() throws RemoteException {
   checkIsBound();
   mRemoteCommunication.discardCommand();
 }
 /**
  * Disconnect from the current MMF app. If the app is not currently connected this method does
  * nothing. You can check if the app is connected by calling {@link #isAppConnected()};
  */
 public void disconnectFromMmfApp() {
   if (isAppConnected()) {
     mRemoteCommunication.onConnectionClosed(mContext);
   }
   requestAppState();
 }
 /**
  * Set the {@link com.mapmyfitness.android.mmfremote.RemoteCommandListener} to receive callbacks
  * for commands from the phone. Whenever you send a command to the MMF app, it will take the
  * appropriate action and send the command back. That is how you can be sure that the MMF app
  * responded property.
  *
  * <p>For example, if you use the {@link #saveWorkoutCommand()} it will send it to th phone. The
  * MMF app on the phone will try to start, it it is successful it will send a callback to {@link
  * com.mapmyfitness.android.mmfremote.RemoteCommandListener#onStartWorkoutEvent(Boolean, Boolean,
  * Boolean, Boolean) onStartWorkoutEvent(Boolean metric, Boolean hasHeartRate, Boolean
  * calculatesCalories, Boolean isSpeed);}
  *
  * <p>This can be null, but then you won't be notified of events from the phone.
  *
  * @param remoteCommandListener the listener to receive the data callbacks
  */
 public void setCommandListener(RemoteCommandListener remoteCommandListener) {
   mRemoteCommunication.setCommandListener(remoteCommandListener);
 }
 /**
  * Set the {@link com.mapmyfitness.android.mmfremote.RemoteDataListener} to receive callbacks for
  * data changes during the lifetime of a workout. This can be set to null, in which case you will
  * not be notified when the changes happen.
  *
  * @param remoteDataListener the listener to receive the data callbacks
  */
 public void setDataListener(RemoteDataListener remoteDataListener) {
   mRemoteCommunication.setDataListener(remoteDataListener);
 }
 /**
  * Get the @{link StatsCache}, this can return null if none is set.
  *
  * @return return the cache
  */
 public StatsCache getStatsCache() {
   return mRemoteCommunication.getStatsCache();
 }
 /**
  * Set a {@link StatsCache} to use during the session. This can be null and no data will be cached
  * automatically. You can also set it during the building of this class.
  *
  * @param statsCache set a cache
  */
 public void setStatsCache(StatsCache statsCache) {
   mRemoteCommunication.setStatsCache(statsCache);
 }