This library contains the basic infrastructure for routing deepLinks, activities and fragments within a multi-module application in a way that a feature module does not need to explicitly depend of another.
Not every project can follow the single Activity recommendation from Google, not every Activity has a public DeepLink to its screens.
Those are just some of the situations we find ourselves while working in a project that has been developed for many years.
We have screens that should only be accessed after the user is logged in, does Navigation Component help us with that?
So we developed this library.
There is no magic here, not even code generation, you will need to write code to make it work. This library just gives you access to Intents and Fragments instances, it does not start activities for you or commit fragments. We will just give you access to things that you would would not have access without direct access to the classes. Ok, Ok, we do start activities when routing DeepLinks, but this is usually fine since we need to start a whole stack of Activities for you.
The basic concept is the same for all routing, at the app startup we register all deeplink, activities and fragments mappers. And in a separated module (like :routes) we share basic implementations that works as indirections to the real implementations.
To make it possible to route your Activity into other modules we need to implement ActivityName
,
ActivityLink<ActivityName>
and ActivityNameMapper<ActivityName>
that reflects you needs.
ActivityName
acts as key when routing to your Activity #sharedActivityLink
binds theActivityName
with parameters we want to pass to that Activity and #sharedActivityNameMapper<ActivityName>
maps theActivityName
into an Activity class. #internal
The ActivityLink
and ActivityName
are used to route, so the client module needs to have access to that implementation.
You can place it in a :routes
module or in any other shared module that suits your needs (:mydomain:routes
, maybe?).
object MyActivityName : ActivityName
class MyActivityLink(
override val parameter: MyActivityParameter
) : ActivityLink<MyActivityName> {
override val activityName: MyActivityName = MyActivityName
}
@Parcelize
data class MyActivityParameter(val data: String) : ParcelableParameter
With that key we can implement an ActivityNameMapper<ActivityName>
in your feature module.
object MyActivityNameMapper : ActivityNameMapper<MyActivityName> {
override val supportedNames: Array<MyActivityName> = arrayOf(MyActivityName)
override fun map(activityLink: ActivityLink<MyActivityName>): Class<out Activity> {
return MyActivityName::class.java
}
}
If your module has multiple activities you can use an enum for your ActivityName
and only one implementation of ActivityNameMapper<ActivityName>
.
enum class MyFeatureModuleActivitiesNames : ActivityName {
MyActivityName,
MyOtherActivityName
}
and
object MyFeatureModuleActivityNameMapper :
ActivityNameMapper<MyFeatureModuleActivitiesNames> {
override val supportedNames: Array<MyFeatureModuleActivitiesNames> = MyFeatureModuleActivitiesNames.values()
override fun map(activityLink: ActivityLink<MyFeatureModuleActivitiesNames>): Class<out Activity> {
return when (activityLink.activityName) {
MyFeatureModuleActivitiesNames.MyActivityName -> MyActivity::class.java
MyFeatureModuleActivitiesNames.MyOtherActivityName -> MyOtherActivity::class.java
}
}
}
Activities and Fragments have similar abstractions, so if you know how to route Activities, you know how to route Fragments.
Activity | Fragment |
---|---|
ActivityNameMapper | FragmentNameMapper |
ActivityName | FragmentName |
To each DeepLink we need an implementation of DeepLinkMapper<DeepLink>
,
where we describe for which apps that DeepLink is supported, what is the DeepLink authority and
what stack of Activities should be created.
object MyDeepLinkMapper : DeepLinkMapper<UriDeepLink> {
override val supportedSchemes: Array<Scheme> = arrayOf(Schemes.publicAppSchemes)
override val supportedAuthority: String = "my schemne"
override fun stack(deepLink: UriDeepLink): Array<ActivityLink<ActivityName>> {
return arrayOf(
ExternalDeepLinkRouterActivityLink(deepLink.parameter)
)
}
override fun canHandle(deepLink: DeepLink): Boolean {
return super.canHandle(deepLink) && deepLink is UriDeepLink
}
}
You can use the Application.create()
method or Googles StartUp library to register your mappers.
Use RouterBuilder
to register ActivityNameMapper
s, FragmentNameMapper
s and DeepLinkMapper
s.
class MyFeatureModuleInitializer : AppStartUp<Unit>() {
override fun create(context: Context) {
with(GlobalRouterBuilder) {
add(MyActivityNameMapper)
add(MyFragmentNameMapper)
add(MyDeepLinkMapper)
}
}
}