2

Basically, I'm converting an application to a library module. The problem is that this application has Chromecast capability, and hence has its own CastOptionsProvider in the manifest, and the application that's going to use this library has its own CastOptionsProvider, leading to a manifest merger failure.

I know this is supposed to be a manifest merger problem, but this is more about the Chromecast part of the app. Is it possible for one app to have 2 CastOptionsProviders to cast into 2 different Receiver Apps?

Gensoukyou1337
  • 1,507
  • 1
  • 13
  • 31

1 Answers1

1

TL;DR You can declare an OptionsProvider class in your library, and if needed, your application can declare its own OptionsProvider class to use instead. To switch between different receiver apps while your application is running, you should use the CastContext.setReceiverApplicationId method to override the settings from the OptionsProvider.

Background

According to the Chromecast codelab, CastOptionsProvider is a class you write yourself. You can actually call it anything you want, as long as it implements com.google.android.gms.cast.framework.OptionsProvider. This also means that you could write more than one CastOptionsProvider class in the same app, as long as you put them in different packages or give them different names. You would declare each one of your OptionsProviders like this:

package your.pkg.goes.here;

import com.google.android.gms.cast.framework.OptionsProvider;
...

public class YourOptionsProvider implements OptionsProvider {

    @Override
    public CastOptions getCastOptions(Context context) {...}

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {...}
}

So far, so good. Because you can call your OptionsProvider whatever you want, you also need to declare your OPTIONS_PROVIDER_CLASS_NAME in the manifest so that the app knows how to get its cast options. That code belongs in your AndroidManifest.xml file, and it looks like this:

<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="your.pkg.goes.here.YourOptionsProvider" />

Here is where it gets tricky. meta-data is for declaring key-value pairs, and you can't have more than one value for the same key. You can have multiple OptionsProvider classes floating around in your code, that's fine, but only one of them can be used by the cast framework at a time. To change which one is used, you need to edit the manifest, which you can't do while the app is running. The closest thing you can do is to have different builds of the app that use different OptionsProviders.

So which OptionProvider should I use?

The behavior of an OptionsProvider is just defined by how it implements the functions, getCastOptions and getAdditionalSessionProviders. Figure out what behavior you need from your library, and write that behavior as library functions or classes that can be expanded on by your app. For example:

package your.library;

import com.google.android.gms.cast.framework.CastOptions;
import com.google.android.gms.cast.framework.OptionsProvider;
...

public class LibraryOptionsProvider implements OptionsProvider {

    public String YOUR_LIBRARY_APPLICATION_ID = "ASDFASDF";

    @Override
    public CastOptions getCastOptions(Context context) {
        return new CastOptions.Builder()
                .setReceiverApplicationId(YOUR_LIBRARY_APPLICATION_ID)
                // maybe set some other settings, too
                .build();
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {...}
}

For an app that doesn't need special behavior, you can just put LibraryOptionsProvider in the app's manifest:

<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="your.library.LibraryOptionsProvider" />

But for apps that need something special, you can declare your own ApplicationOptionsProvider that uses the LibraryOptionsProvider for some of its behavior.

package your.application;

import your.library.LibraryOptionsProvider;
...

public class ApplicationOptionsProvider extends LibraryOptionsProvider {

    @Override
    public CastOptions getCastOptions(Context context) {
        return new CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id)) // use a different application ID from the one in LibraryOptionsProvider
                .build();
    }

    // inherit getAdditionalSessionProviders from LibraryOptionsProvider, no need to override it unless we want different behavior
}

Then your manifest should point to ApplicationOptionsProvider instead of LibraryOptionsProvider:

<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="your.application.ApplicationOptionsProvider" />

Summary so far: It's up to the application to decide what behavior it needs from its OptionsProvider. If no special behavior is needed, the application should use an OptionsProvider that you declare in the library. When custom behavior is called for, such as when you need different cast options or a different cast application ID, then the application should declare its own OptionsProvider, which can depend on library code instead of re-implementing shared behavior.

Multiple Application IDs

The OptionsProvider is used to initialize the CastOptions, which is "a singleton class containing global context for the Cast SDK"(source). CastContext is initialized in the background when the app starts, but you can change the application ID whenever you want to, using CastContext.setReceiverApplicationId(String applicationId). By doing so, you will ignore the receiver application ID that was configured in your CastOptions. This is how you can switch between multiple receiver apps within a single build.

Kalinda Pride
  • 315
  • 5
  • 9