ENavigation

A routing framework that uses Kotlin encapsulation, suitable for component-based development scenarios, currently supports automatic route registration, interceptors, child thread jumps, jump animations, etc. ENavigaiton can be used to jump between activities, internal and external schemes, system interfaces, etc. GitHub, Demo download

Jump processes

To solve the scene

In componentized development, it was necessary to jump to the specified Activity, but in many cases, the target Activity class was invisible and could not be jumped directly through startActivity. ENavigation was the solution to this scenario. Alternatively, visit a page, many times we need to determine whether the user has access to the page, usually it is at this time do judge a permission before the jump, but this, the project will be a lot more repeated access code, using ENavigation interceptors can be very good to solve this problem, You only need to define an interceptor class.

Depend on the configuration

You first need to configure host in the build.gradle file that uses the ENavigation module:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'com.ricky.enavigation.plugin'
}
android {
    compileSdk 30
    // ...
}
kapt {
    arguments {
        arg("host"."app")
    }
}

dependencies {
    ...
    / / core library
    implementation project(path: ':enavigation_impl')
    kapt project(path: ':enavigation_complier')}Copy the code

Directions for use

1. Initialization

ENavigation must be initialized before using ENavigation. It is recommended that the initialization code be placed in Application. Initialization method:

class BaseApplication : Application() {
    override fun onCreate(a) {
        super.onCreate()
        // Automatic registration component, note that you need to add automatic registration plug-in in the APP module
        ENavigation.init(this)
        // To register components manually, pass in the host configured in build.gradle for each module
        ENavigation.init(this."base"."app"."module1"."module1")}}Copy the code

Gradle = build.gradle = build.gradle = build.gradle = build.gradle = build.gradle

plugins {
    id 'com.ricky.enavigation.plugin'
}
/ / or
apply plugin: 'com.ricky.enavigation.plugin'
Copy the code

If the plugin is not found, check to see if any plugin dependencies are added to your project’s build.gradle:

buildscript {
    ...
    dependencies {
        classpath "com.ricky.enavigation:plugin:$lateast_plugin_version"}... }Copy the code

Second, basic jump

To use a route jump, add the @HoStandPathanno annotation to the target Activity:

@HostAndPathAnno("app/test")
class TestActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test1)
    }
}
Copy the code

Here app/test1 is the routing address for TestActivity, which is then used to jump to TestActivity. Note that the first word stands for host and must be the same as the host specified in build.gradle of the module in which the Activity is located. The second word can be configured according to the purpose of the Activity.

Jump to the same module

MainActivity jumps to TestActivity. Both activities are in the app module.

ENavigation. With (context).sethostandPath (" app/test ").navigate()Copy the code

Cross module jump

  • MainActivity visible to target Activity MainActivity jumps to TestActivity1, TestActivity1 is in the Module1 module, app module references the Module1 module, so TestActivity1 class is visible to MainActivity.
ENavigation. With (context).sethostandPath (" app/test1 ").navigate()Copy the code
  • MainActivity jumps to TestActivity2, TestActivity2 is in module2, app module references Base and Module2, TestActivity2 class is not visible to Base module, Perform the jump in the Base module.
ENavigation. With (context).sethostandPath (" app/test1 ").navigate()Copy the code

Child thread jump

Because ENavigation actually performs the jump in the interceptor, which in turn migrates to the UI thread, the jump interface can be implemented in child threads as well.

Thread {ENavigation. With (context).sethostandPath (" app/test1 ").navigate()}.start()Copy the code

However, it is usually impossible or difficult to get a context from a child thread, so you can jump without passing in the context:

Thread {ENavigation. With ().sethostandPath (" app/test1 ").navigate()}.start()Copy the code

A Scheme to jump

You can directly set the forward scheme using the setScheme method

/ / internal scheme
ENavigation.with(context)
    .setScheme("enavigation://test")
    .navigate()

/ / external scheme
ENavigation.with(context)
    .setScheme("taobao://item? id=97896794126846128")
    .navigate()
Copy the code

Schemes for internal and external apps are supported.

System Interface

Here is an example of skipping system album to select photos

ENavigation.with(context)
    .setAction(Intent.ACTION_GET_CONTENT)
    .setType("image/*")
    .onResult { _, _, data ->
        // The onResult callback is used to retrieve the returned value, as discussed below
        Toast.makeText(this."uri=${data? .data}", Toast.LENGTH_SHORT).show()
    }
    .navigate()
Copy the code

ENavigation encapsulates other methods such as setData, setFlags, and setCategories, which act on an internal intent and pass in the startActivity method when a jump occurs.

Jump to monitor

ENavigation supports monitoring before and after jumps, facilitating related operations.

ENavigation.with(context)
    .setHostAndPath(PathConfig.Module1.Test4.PATH)
    .beforeAction { activity ->
        // Before jumping to the page
    }
    .afterAction { activity ->
        // Jump to the page
    }
    .navigate()
Copy the code

Note that both callbacks are currently executed in the UI thread.

Parameter transfer

Parameters of the incoming

ENavigation encapsulates a series of methods for setting incoming parameters that act on internal intEnts, as described in the systematic Intent class. Here is an example of skipping to a page to pass a string.

ENavigation.with(context)
    .setHostAndPath("app/test")
    .putString("text"."hello world")
    .navigate()
Copy the code

The Activity corresponding to the app/test route is passed a key text value with a value of Hello World. The value is set in the same manner as the intent.

The data returned

The value returned from the previous page can be retrieved via the encapsulated onResult callback.

ENavigation.with(context)
    .setHostAndPath("app/test")
    .onResult { requestCode, resultCode, data ->
        val text = data? .getStringExtra("result")
        Toast.makeText(this."requestCode=$requestCode\nresultCode=$resultCode\n Received the returned value:$text", Toast.LENGTH_SHORT).show()
    }
    .navigate()
Copy the code

4. Interceptor

Interceptors act on the target Activity and can be used to restrict the permissions of the target Activity, such as pages that need to be logged in to be accessed.

Global interceptor

Global interceptors apply to all page jumps. Implementing a global interceptor requires a custom class that inherits from INavigationInterceptor, overwrites the Intercept method, and annotates the custom class with @GlobalInterceptoranno.

@GlobalInterceptorAnno
class MyGlobalInterceptor : INavigationInterceptor {
    override fun intercept(chain: INavigationInterceptor.Chain) {
        if (isLogin) {
            // If not, proceed with chain.proceed
            chain.proceed(chain.request)
        }
    }
}
Copy the code

If you have more than one global interceptor, you can set the priority of the interceptor

@GlobalInterceptorAnno(priority = 1)
class MyGlobalInterceptor : INavigationInterceptor {
    override fun intercept(chain: INavigationInterceptor.Chain) {
        if (isLogin) {
            // If not, proceed with chain.proceed
            chain.proceed(chain.request)
        }
    }
}
Copy the code

The higher the value of priority, the higher the priority.

Annotation interceptor

Annotation interceptors work on individual page jumps and also require a custom interceptor class

@InterceptorAnno(name = "app/test_interceptor")
class TestInterceptor1 : INavigationInterceptor {
    override fun intercept(chain: INavigationInterceptor.Chain) {
        Toast.makeText(chain.request.activity, "Interceptor 1", Toast.LENGTH_SHORT).show()
        chain.proceed(chain.request)
    }
}
Copy the code

As with the route address, the interceptor needs to give a name, with the first word corresponding to host and the second corresponding to name. It is also possible to define without annotations

class TestInterceptor2 : INavigationInterceptor {
    override fun intercept(chain: INavigationInterceptor.Chain) {
        Toast.makeText(chain.request.activity, "Interceptor 2", Toast.LENGTH_SHORT).show()
        chain.proceed(chain.request)
    }
}
Copy the code

You then need to add interceptors to the annotations of the target Activity

@HostAndPathAnno(
    PathConfig.Module1.Test6.PATH,
    interceptors = [TestInterceptor2::class],
    interceptorNames = ["app/test_interceptor1"],)
class TestActivity6 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test6)
    }
}
Copy the code

You can add interceptors to the target Activity as interceptors or interceptorNames. Note that an interceptor class provided by interceptors does not need the @Interceptoranno annotation, otherwise it will intercept twice.

Dynamically add interceptors

Interceptors can also be added at jump time

ENavigation.with(context)
    .setHostAndPath("app/test")
    .setInterceptors(TestInterceptor())
    .navigate()
Copy the code

Set it by setInterceptors, if you need more than one

ENavigation.with(context)
    .setHostAndPath("app/test")
    .setInterceptors(TestInterceptor1(), TestInterceptor2(), TestInterceptor3())
    .navigate()
Copy the code

Interceptor classes added when jumping also do not need the @interceptoranno annotation.

Five, jump animation

ENavigation encapsulates a series of jump animations, such as the Activity popping from below

ENavigation.with(this)
    .setHostAndPath("app/test")
    .bottom()
    .navigate()
Copy the code

Jump animation method description

The method name The entrance appearance
fade() Fade in fade
top() The above into fade
right() The right to enter fade
bottom() At the bottom of the enter fade
left() On the left to enter fade
fadeIn() Fade in
topIn() The above into
rightIn() The right to enter
bottomIn() At the bottom of the enter
leftIn() On the left to enter
expandTopLeftIn() Top left corner expansion
expandTopCenterIn() The top
expandTopRightIn() Top right expansion
expandCenterLeftIn() The left open
expandCenterIn() In the middle a
expandCenterRightIn() The right on
expandBottomLeftIn() Lower left corner expansion
expandBottomCenterIn() The following a
expandBottomRightIn() Lower right corner expansion
fadeOut() fade
topOut() At the top of the exit
rightOut() The right exit
bottomOut() The exit
leftOut() The left out
shrinkTopLeftOut() Exit upper right corner
shrinkTopCenterOut() The exit
shrinkTopRightOut() Exit upper right corner
shrinkCenterLeftOut() The left out
shrinkCenterOut() In the middle of exit
shrinkCenterRightOut() The right exit
shrinkBottomLeftOut() Lower left exit
shrinkBottomCenterOut() The exit
shrinkBottomRightOut() Lower right exit

You can also use custom animations

ENavigation.with(this)
    .setHostAndPath("app/test")
    .animateIn(R.anim.custom_in)
    .animateOut(R.anim.custom_out)
    .navigate()
Copy the code

AnimateIn applies to the jumping Activity, and animateOut to the current Activity.

Vi. Exception handling

ENavigation encapsulates some common exceptions, which are placed in NavigationException class, when the Activity was destroyed when the route was jumped

abnormal instructions
NavigationException.ActivityDetachedException The Activity is destroyed
NavigationException.NullTargetException The route or Scheme does not exist
NavigationException.NullActivityException An Activity is empty, such as jumping in a Fragment
NavigationException.InvalidActivityException The Activity can only be FragmentActivity
NavigationException.InvalidCodeException resultCode ! = Activity.RESULT_OK

Listen for exceptions through the onError callback

ENavigation.with(this)
    .setScheme("app/test")
    .onError { exception ->
        when (exception) {
            is NavigationException.ActivityDetachedException -> {
            }
            is NavigationException.NullActivityException -> {
            }
            is NavigationException.NullTargetException -> {
            }
            is NavigationException.InvalidCodeException -> {
            }
            is NavigationException.InvalidActivityException -> {
            }
            else- > {// Other exceptions
            }
        }
    }
    .navigate()
Copy the code