/**
  * Returns a (singleton) instance of this class. Clients should call this method in order to get a
  * hold of this singleton instance. If it is not initialized yet, a {@link CastException} will be
  * thrown.
  */
 public static DataCastManager getInstance() {
   if (sInstance == null) {
     String msg = "No DataCastManager instance was found, did you forget to initialize it?";
     LOGE(TAG, msg);
     throw new IllegalStateException(msg);
   }
   return sInstance;
 }
 @Override
 public void onConnectivityRecovered() {
   try {
     attachDataChannels();
   } catch (IOException | IllegalStateException e) {
     LOGE(TAG, "onConnectivityRecovered(): Failed to reattach data channels", e);
   }
   super.onConnectivityRecovered();
 }
 /*
  * Remove namespaces
  *
  * @throws NoConnectionException If no connectivity to the device exists
  * @throws TransientNetworkDisconnectionException If framework is still trying to recover from a
  * possibly transient loss of network
  */
 private void detachDataChannels() {
   if (mApiClient == null) {
     return;
   }
   for (String namespace : mNamespaceList) {
     try {
       Cast.CastApi.removeMessageReceivedCallbacks(mApiClient, namespace);
     } catch (IOException | IllegalArgumentException e) {
       LOGE(TAG, "detachDataChannels() Failed to remove namespace: " + namespace, e);
     }
   }
 }
 /**
  * Initializes the DataCastManager for clients. Before clients can use DataCastManager, they need
  * to initialize it by calling this static method. Then clients can obtain an instance of this
  * singleton class by calling {@link DataCastManager#getInstance()}. Failing to initialize this
  * class before requesting an instance will result in a {@link CastException} exception.
  *
  * @param context
  * @param applicationId the application ID for your application
  * @param namespaces Namespaces to be set up for this class.
  */
 public static synchronized DataCastManager initialize(
     Context context, String applicationId, String... namespaces) {
   if (sInstance == null) {
     LOGD(TAG, "New instance of DataCastManager is created");
     if (ConnectionResult.SUCCESS
         != GooglePlayServicesUtil.isGooglePlayServicesAvailable(context)) {
       String msg = "Couldn't find the appropriate version of Google Play Services";
       LOGE(TAG, msg);
       throw new RuntimeException(msg);
     }
     sInstance = new DataCastManager(context, applicationId, namespaces);
   }
   return sInstance;
 }
 public void onApplicationStatusChanged() {
   String appStatus;
   if (!isConnected()) {
     return;
   }
   try {
     appStatus = Cast.CastApi.getApplicationStatus(mApiClient);
     LOGD(TAG, "onApplicationStatusChanged() reached: " + appStatus);
     for (DataCastConsumer consumer : mDataConsumers) {
       consumer.onApplicationStatusChanged(appStatus);
     }
   } catch (IllegalStateException e) {
     LOGE(TAG, "onApplicationStatusChanged(): Failed", e);
   }
 }
  @Override
  public void onApplicationConnected(
      WebAppSession webAppSession, WebAppSession.WebAppStatus status) {
    LOGD(
        TAG,
        "onApplicationConnected() reached with sessionId: "
            + webAppSession.launchSession.getSessionId());

    // saving session for future retrieval; we only save the last session info
    mPreferenceAccessor.saveStringToPreference(
        PREFS_KEY_SESSION_ID, webAppSession.launchSession.getSessionId());
    if (mReconnectionStatus == RECONNECTION_STATUS_IN_PROGRESS) {
      // we have tried to reconnect and successfully launched the app, so
      // it is time to select the route and make the cast icon happy :-)
      List<RouteInfo> routes = mMediaRouter.getRoutes();
      if (routes != null) {
        String routeId = mPreferenceAccessor.getStringFromPreference(PREFS_KEY_ROUTE_ID);
        boolean found = false;
        for (RouteInfo routeInfo : routes) {
          if (routeId.equals(routeInfo.getId())) {
            // found the right route
            LOGD(TAG, "Found the correct route during reconnection attempt");
            found = true;
            mReconnectionStatus = RECONNECTION_STATUS_FINALIZED;
            mMediaRouter.selectRoute(routeInfo);
            break;
          }
        }
        if (!found) {
          // we were hoping to have the route that we wanted, but we
          // didn't so we deselect the device
          onDeviceSelected(null);
          mReconnectionStatus = RECONNECTION_STATUS_INACTIVE;
          return;
        }
      }
    }
    // registering namespaces, if any
    try {
      attachDataChannels();
      mSessionId = webAppSession.launchSession.getSessionId();
      for (DataCastConsumer consumer : mDataConsumers) {
        consumer.onApplicationConnected(webAppSession, status);
      }
    } catch (IllegalStateException | IOException e) {
      LOGE(TAG, "Failed to attach namespaces", e);
    }
  }
 /**
  * Unregisters a namespace. If namespace is not already registered, it returns <code>false</code>,
  * otherwise a successful removal returns <code>true</code>.
  *
  * @throws NoConnectionException If no connectivity to the device exists
  * @throws TransientNetworkDisconnectionException If framework is still trying to recover from a
  *     possibly transient loss of network
  */
 public boolean removeNamespace(String namespace)
     throws TransientNetworkDisconnectionException, NoConnectionException {
   checkConnectivity();
   if (TextUtils.isEmpty(namespace)) {
     throw new IllegalArgumentException("namespace cannot be empty");
   }
   if (!mNamespaceList.contains(namespace)) {
     LOGD(TAG, "Ignoring to remove a namespace that is not registered.");
     return false;
   }
   try {
     Cast.CastApi.removeMessageReceivedCallbacks(mApiClient, namespace);
     mNamespaceList.remove(namespace);
     return true;
   } catch (IOException | IllegalStateException e) {
     LOGE(TAG, String.format("removeNamespace(%s)", namespace), e);
   }
   return false;
 }
  /**
   * Sends the <code>message</code> on the data channel for the <code>namespace</code>. If fails, it
   * will call <code>onMessageSendFailed</code>
   *
   * @throws IllegalArgumentException If the the message is null, empty, or too long; or if the
   *     namespace is null or too long.
   * @throws IllegalStateException If there is no active service connection.
   * @throws IOException
   */
  public void sendDataMessage(String message, String namespace)
      throws IllegalArgumentException, IllegalStateException, IOException {
    checkConnectivity();
    if (TextUtils.isEmpty(namespace)) {
      throw new IllegalArgumentException("namespace cannot be empty");
    }

    LOGE(TAG, "sendDataMessage not yet implemented");
    //        Cast.CastApi.sendMessage(mApiClient, namespace, message).
    //                setResultCallback(new ResultCallback<Status>() {
    //
    //                    @Override
    //                    public void onResult(Status result) {
    //                        if (!result.isSuccess()) {
    //                            DataCastManager.this.onMessageSendFailed(result);
    //                        }
    //                    }
    //                });
  }