Add Advanced CAF Sender Features to your Android App

Add Custom Actions

A sender app can extend MediaIntentReceiver to handle custom actions or override its behavior. If you have implemented your own MediaIntentReceiver, you need to add it to the manifest, and also set its name in the CastMediaOptions. This example provides custom actions that override toggle remote media playback, pressing the media button and other types of actions.

// In AndroidManifest.xml
<receiver android:name="com.example.MyMediaIntentReceiver" />

// In your OptionsProvider
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
        .setMediaIntentReceiverClassName(MyMediaIntentReceiver.class.getName())
        .build();

// Implementation of MyMediaIntentReceiver
class MyMediaIntentReceiver extends MediaIntentReceiver {
    @Override
    protected void onReceiveActionTogglePlayback(Session currentSession) {
    }

    @Override
    protected void onReceiveActionMediaButton(Session currentSession,
                                              KeyEvent keyEvent) {
    }

    @Override
    protected void onReceiveOtherAction(String action, Intent intent) {
    }
}

How Volume Control works

The framework automatically manages the volume for the sender app. The framework automatically synchronizes the sender and receiver apps so that the sender UI always reports the volume specified by the receiver.

Volume control prior to Jelly Bean

To use the physical volume keys to control the receiver device volume on Android devices older than Jelly Bean, the sender app should override dispatchKeyEvent in their Activities, and call CastContext.onDispatchVolumeKeyEventBeforeJellyBean():

class MyActivity extends FragmentActivity {
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        return CastContext.getSharedInstance(this)
            .onDispatchVolumeKeyEventBeforeJellyBean(event)
            || super.dispatchKeyEvent(event);
    }
}

Add a Custom Channel

For the sender app to communicate with the receiver app, your app needs to create a custom channel. The sender can use the custom channel to send string messages to the receiver. Each custom channel is defined by a unique namespace and must start with the prefix urn:x-cast:, for example, urn:x-cast:com.example.custom. It is possible to have multiple custom channels, each with a unique namespace. The receiver app can also send and receive messages using the same namespace.

The custom channel is implemented with the Cast.MessageReceivedCallback interface:

class HelloWorldChannel implements Cast.MessageReceivedCallback {
  public String getNamespace() {
    return "urn:x-cast:com.example.custom";
  }
  @Override
  public void onMessageReceived(CastDevice castDevice, String namespace,
        String message) {
    Log.d(TAG, "onMessageReceived: " + message);
  }
}

Once the sender app is connected to the receiver app, the custom channel can be created using the setMessageReceivedCallbacks method:

try {
  myCastSession.setMessageReceivedCallbacks(
      mHelloWorldChannel.getNamespace(),
      mHelloWorldChannel);
} catch (IOException e) {
  Log.e(TAG, "Exception while creating channel", e);
}

Once the custom channel is created, the sender can use the sendMessage method to send string messages to the receiver over that channel:

private void sendMessage(String message) {
 if (mHelloWorldChannel != null) {
  try {
    myCastSession.sendMessage(mHelloWorldChannel.getNamespace(), message)
    .setResultCallback(
      new ResultCallback<Status>() {
        @Override
        public void onResult(Status result) {
          if (!result.isSuccess()) {
            Log.e(TAG, "Sending message failed");
          }
        }
      });
  } catch (Exception e) {
    Log.e(TAG, "Exception while sending message", e);
  }
 }
}

Supporting Autoplay

See the section Autoplay & Queueing APIs.

Override Image Selection for UX widgets

Various components of the framework (namely the Cast dialog, the mini controller, and the UIMediaController, if so configured) will display artwork for the currently casting media. The URLs to the image artwork are typically included in the MediaMetadata for the media, but the sender app may have an alternate source for the URLs.

The ImagePicker class defines a means for selecting an appropriate image from the list of images in a MediaMetadata, based on the use of the image, for example, notification thumbnail or full screen background. The default ImagePicker implementation always chooses the first image, or returns null if no image is available in the MediaMetadata. Your app can subclass ImagePicker and override the onPickImage(MediaMetadata, ImageHints) method to provide an alternate implementation, and then select that subclass with the setImagePicker method of CastMediaOptions.Builder. ImageHints provides hints to an ImagePicker about the type and size of an image to be selected for display in the UI.

Customizing Cast dialogs

SessionManager is the centural place for managing session lifecycle. SessionManager listens to Android MediaRouter route selection state changes to start, resume and end sessions. When a route is selected, SessionManager will create a Session object and tries to start or resume it. When a route is unselected, SessionManager will end the current session.

Therefore, to ensure SessionManager manages session lifecycles properly, you must make sure that:

Depending on how you create the Cast dialogs, additional actions may need to be done:

Next Steps

This concludes the features that you can add to your Android Sender app. You can now build a sender app for another platform (iOS or Chrome), or build a receiver app.