This is an example project for dynamic features in a service-oriented
approach. There are multiple core modules which are included through
the app module. Also there are multiple feature modules, which
depend on the app module as satellites.
The app module doesn't know anything about the specific features.
Instead, the core-feature module exposes certain base capabilities
that define for example possible entry points into features.
The goal of this approach is to explore a service-oriented architecture
for Android applications. Each service should expose a certain API -
in this approach available as a set of ServiceCapability objects -
which allows the application to make use of the service.
The service object is kept minimalistic at the moment. It only consists
of a String representing the name and a List<ServiceCapability> to
describe what the service offers.
interface ServiceConfiguration {
    val name: String
    val capabilities: List<ServiceCapability>
}
Each of these ServiceCapability objects defines a certain entry-
point into the service.
interface ServiceCapability
Capabilities can provide UI elements like Activity, Fragment or
simply View objects - or other Android related objects like a
(implicit) BroadcastReceiver.
Outside of the UI and Android sphere, they can simply serve as processors for data, access databases or webservices or execute other tasks.
In the sample application there are currently three different
implementations of the ServiceCapability interface to showcase
different use-cases. Of course the examples don't show every possible
use-case, but rather a small subset.
Inflates and returns a custom layout that includes a button. Clicking
this button starts the DashboardActivity to guide the user into the
service / feature.
Inflates and returns a Button. This capability also adds a
View.OnAttachStateChangeListener to the returned View
allowing self-contained managing of for example Animations.
When clicking the button, the ProfileActivity will be started.
This capability extends ImageProcessorCapability which provides a
function with the signature
process(bitmap: Bitmap): Bitmap
This capability reduces the image saturation to 0, creating a greyscale effect. It's an example of a service that doesn't provide, expose or inject any UI elements but instead simply offers a service.
The app module acts as the host in this scenario. Each service has
a dependency on the app module and exposes its configuration to
the ServiceRegistry whenever the service is available.
interface ServiceRegistry {
    fun register(service: ServiceConfiguration)
    fun getList(): List<ServiceConfiguration>
}
At the moment (since it's basically just a proof of concept) the registry is kept as simple as possible. The implementation simply keeps reference of the list and returns it, as well as offers a static instance of the registry.
Since the services can be loaded during runtime - i.e. they are unknown
to the app module at compile time - they can not be referenced in a
traditional way.
To ensure that each service is created and injected into the
ServiceRegistry as soon as possible, a ContentProvider is created
in each service that does exactly this:
class ServiceProvider : ContentProvider() {
    override fun onCreate(): Boolean {
        ServiceRegistry.getInstance().register(DashboardServiceConfiguration())
        return true
    }
}
The ContentProvider is added to the AndroidManifest.xml file,
which will be merged into the final AndroidManifest.xml once the
module has been loaded. This ensures a seamless and early instantiation.
(This is also how Firebase is initialized on Android)
To extend the sample project, simply create a new (dynamic-) feature
module. Create a ContentProvider (see: ServiceProvider.kt), add
it to the AndroidManifest.xml of your new module (and make sure
that the authority is unique) and inject the configuration
of your newly created service into the ServiceRegistry. Done.
If you extended existing ServiceCapability interfaces, they
will automatically be used. If you created new interfaces, make sure
you initialized them in the core-feature module. You also
have to find the right spot in the app module to utilize them.
Add an example that uses Deep Linking to facilitate the navigation. Allow services to register themselves for certain scopes in the application's URI scheme.
Build a more complex application with this approach:
- Allow services to inject items into a bottom navigation bar
 - Allow services to provide renderers for certain content types in a 
RecyclerView - Real-life scenario with navigation, backstack, passing data between services
 


