-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: android activity routing integration
- Loading branch information
1 parent
ee41a13
commit b4dc27d
Showing
19 changed files
with
966 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,61 @@ | ||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | ||
|
||
plugins { | ||
alias(libs.plugins.android.library) | ||
id("com.android.library") | ||
kotlin("android") | ||
id("org.jlleitschuh.gradle.ktlint") | ||
id("org.jetbrains.kotlinx.kover") | ||
alias(libs.plugins.maven.publish) | ||
} | ||
|
||
android { | ||
namespace = "dev.programadorthi.routing.android" | ||
compileSdk = 34 | ||
namespace = "dev.programadorthi.routing.android" | ||
|
||
defaultConfig { | ||
minSdk = 23 | ||
consumerProguardFiles("consumer-rules.pro") | ||
|
||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||
} | ||
|
||
buildTypes { | ||
release { | ||
isMinifyEnabled = false | ||
proguardFiles( | ||
getDefaultProguardFile("proguard-android-optimize.txt"), | ||
"proguard-rules.pro", | ||
) | ||
} | ||
} | ||
|
||
compileOptions { | ||
sourceCompatibility = JavaVersion.VERSION_11 | ||
targetCompatibility = JavaVersion.VERSION_11 | ||
sourceCompatibility = JavaVersion.VERSION_1_8 | ||
targetCompatibility = JavaVersion.VERSION_1_8 | ||
} | ||
|
||
testOptions { | ||
unitTests { | ||
isIncludeAndroidResources = true | ||
} | ||
} | ||
} | ||
|
||
tasks.withType<KotlinCompile>().configureEach { | ||
kotlinOptions { | ||
jvmTarget = JavaVersion.VERSION_1_8.toString() | ||
freeCompilerArgs += listOf("-Xexplicit-api=strict") | ||
} | ||
} | ||
|
||
dependencies { | ||
api(projects.resources) | ||
} | ||
implementation(libs.androidx.startup) | ||
|
||
testImplementation(kotlin("test")) | ||
testImplementation(libs.test.junit) | ||
testImplementation(libs.test.coroutines) | ||
testImplementation(libs.test.coroutines.debug) | ||
testImplementation(libs.test.kotlin.test.junit) | ||
testImplementation(libs.test.robolectric) | ||
} |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,18 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest /> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools"> | ||
|
||
<application> | ||
<provider | ||
android:name="androidx.startup.InitializationProvider" | ||
android:authorities="${applicationId}.androidx-startup" | ||
android:exported="false" | ||
tools:node="merge"> | ||
<meta-data | ||
android:name="dev.programadorthi.routing.android.ActivityManager" | ||
android:value="androidx.startup" /> | ||
</provider> | ||
|
||
</application> | ||
|
||
</manifest> |
71 changes: 71 additions & 0 deletions
71
integration/android/src/main/kotlin/dev/programadorthi/routing/android/ActivityManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package dev.programadorthi.routing.android | ||
|
||
import android.app.Activity | ||
import android.app.Application | ||
import android.content.Intent | ||
import android.os.Bundle | ||
import dev.programadorthi.routing.core.RouteMethod | ||
import dev.programadorthi.routing.core.application.ApplicationCall | ||
import dev.programadorthi.routing.core.application.call | ||
import io.ktor.util.pipeline.PipelineContext | ||
import java.lang.ref.WeakReference | ||
|
||
@PublishedApi | ||
internal interface ActivityManager : Application.ActivityLifecycleCallbacks { | ||
fun currentActivity(): Activity | ||
|
||
fun start( | ||
pipelineContext: PipelineContext<Unit, ApplicationCall>, | ||
intent: Intent, | ||
) | ||
} | ||
|
||
internal class AndroidActivityManager : ActivityManager { | ||
private var currentActivity = WeakReference<Activity>(null) | ||
|
||
override fun onActivityCreated( | ||
activity: Activity, | ||
savedInstanceState: Bundle?, | ||
) { | ||
} | ||
|
||
override fun onActivityResumed(activity: Activity) {} | ||
|
||
override fun onActivityPaused(activity: Activity) {} | ||
|
||
override fun onActivityStopped(activity: Activity) {} | ||
|
||
override fun onActivitySaveInstanceState( | ||
activity: Activity, | ||
outState: Bundle, | ||
) { | ||
} | ||
|
||
override fun onActivityDestroyed(activity: Activity) {} | ||
|
||
override fun onActivityStarted(activity: Activity) { | ||
currentActivity = WeakReference(activity) | ||
} | ||
|
||
override fun currentActivity(): Activity = | ||
currentActivity.get() ?: error( | ||
"Activity manager not started. Please, install AndroidActivities plugin to your route", | ||
) | ||
|
||
override fun start( | ||
pipelineContext: PipelineContext<Unit, ApplicationCall>, | ||
intent: Intent, | ||
) = with(pipelineContext) { | ||
val activity = currentActivity() | ||
val options = call.activityOptions | ||
when (val requestCode = call.requestCode) { | ||
null -> activity.startActivity(intent, options) | ||
else -> activity.startActivityForResult(intent, requestCode, options) | ||
} | ||
|
||
when (call.routeMethod) { | ||
RouteMethod.Replace -> activity.finishAfterTransition() | ||
RouteMethod.ReplaceAll -> activity.finishAffinity() | ||
} | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
integration/android/src/main/kotlin/dev/programadorthi/routing/android/AndroidActivities.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package dev.programadorthi.routing.android | ||
|
||
import android.app.Activity | ||
import android.content.Context | ||
import android.content.ContextWrapper | ||
import dev.programadorthi.routing.core.Routing | ||
import dev.programadorthi.routing.core.application | ||
import dev.programadorthi.routing.core.application.Application | ||
import dev.programadorthi.routing.core.application.ApplicationCall | ||
import dev.programadorthi.routing.core.application.RouteScopedPlugin | ||
import dev.programadorthi.routing.core.application.createRouteScopedPlugin | ||
import dev.programadorthi.routing.core.application.plugin | ||
import dev.programadorthi.routing.core.application.pluginOrNull | ||
import dev.programadorthi.routing.core.asRouting | ||
import io.ktor.util.AttributeKey | ||
import io.ktor.utils.io.KtorDsl | ||
import android.app.Application as AndroidApplication | ||
|
||
internal val AndroidActivityManagerKey = | ||
AttributeKey<ActivityManager>("AndroidActivityManagerKey") | ||
|
||
public val AndroidActivities: RouteScopedPlugin<AndroidActivitiesConfig> = | ||
createRouteScopedPlugin("AndroidActivities", ::AndroidActivitiesConfig) { | ||
|
||
val context = pluginConfig.context | ||
val parentRouting = application.pluginOrNull(Routing) | ||
|
||
var manager = | ||
generateSequence(seed = parentRouting) { it.parent?.asRouting } | ||
.mapNotNull { it.attributes.getOrNull(AndroidActivityManagerKey) } | ||
.firstOrNull() | ||
|
||
if (manager == null) { | ||
val applicationContext = | ||
when (context) { | ||
is AndroidApplication -> context | ||
else -> context.applicationContext | ||
} | ||
val application = applicationContext as AndroidApplication | ||
manager = pluginConfig.manager | ||
application.registerActivityLifecycleCallbacks(manager) | ||
} | ||
|
||
val activity = | ||
generateSequence(seed = context) { (it as? ContextWrapper)?.baseContext } | ||
.mapNotNull { it as? Activity } | ||
.firstOrNull() | ||
|
||
if (activity != null) { | ||
manager.onActivityStarted(activity) | ||
} | ||
|
||
application.attributes.put(AndroidActivityManagerKey, manager) | ||
|
||
onCall { call -> | ||
call.attributes.put(AndroidActivityManagerKey, manager) | ||
} | ||
} | ||
|
||
/** | ||
* A configuration for the [AndroidActivities] plugin. | ||
*/ | ||
@KtorDsl | ||
public class AndroidActivitiesConfig { | ||
public lateinit var context: Context | ||
|
||
internal var manager: ActivityManager = AndroidActivityManager() | ||
} | ||
|
||
@PublishedApi | ||
internal val ApplicationCall.activityManager: ActivityManager | ||
get() = application.activityManager() | ||
|
||
internal val Routing.activityManager: ActivityManager | ||
get() = application.activityManager() | ||
|
||
private fun Application.activityManager(): ActivityManager { | ||
return when (val manager = attributes.getOrNull(AndroidActivityManagerKey)) { | ||
null -> { | ||
plugin(AndroidActivities) | ||
error("There is no started activity manager. Please, install AndroidActivities plugin") | ||
} | ||
|
||
else -> manager | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
integration/android/src/main/kotlin/dev/programadorthi/routing/android/AndroidRouting.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package dev.programadorthi.routing.android | ||
|
||
import android.os.Bundle | ||
import dev.programadorthi.routing.core.RouteMethod | ||
import dev.programadorthi.routing.core.Routing | ||
import dev.programadorthi.routing.core.application | ||
import dev.programadorthi.routing.core.application.ApplicationCall | ||
import io.ktor.http.Parameters | ||
import io.ktor.util.Attributes | ||
|
||
public fun Routing.pushActivity( | ||
name: String = "", | ||
path: String = "", | ||
requestCode: Int? = null, | ||
activityOptions: Bundle? = null, | ||
attributes: Attributes = Attributes(), | ||
parameters: Parameters = Parameters.Empty, | ||
) { | ||
execute( | ||
routeMethod = RouteMethod.Push, | ||
name = name, | ||
path = path, | ||
requestCode = requestCode, | ||
activityOptions = activityOptions, | ||
attributes = attributes, | ||
parameters = parameters, | ||
) | ||
} | ||
|
||
public fun Routing.replaceActivity( | ||
name: String = "", | ||
path: String = "", | ||
requestCode: Int? = null, | ||
activityOptions: Bundle? = null, | ||
attributes: Attributes = Attributes(), | ||
parameters: Parameters = Parameters.Empty, | ||
) { | ||
execute( | ||
routeMethod = RouteMethod.Replace, | ||
name = name, | ||
path = path, | ||
requestCode = requestCode, | ||
activityOptions = activityOptions, | ||
attributes = attributes, | ||
parameters = parameters, | ||
) | ||
} | ||
|
||
public fun Routing.replaceAllActivity( | ||
name: String = "", | ||
path: String = "", | ||
requestCode: Int? = null, | ||
activityOptions: Bundle? = null, | ||
attributes: Attributes = Attributes(), | ||
parameters: Parameters = Parameters.Empty, | ||
) { | ||
execute( | ||
routeMethod = RouteMethod.ReplaceAll, | ||
name = name, | ||
path = path, | ||
requestCode = requestCode, | ||
activityOptions = activityOptions, | ||
attributes = attributes, | ||
parameters = parameters, | ||
) | ||
} | ||
|
||
private fun Routing.execute( | ||
routeMethod: RouteMethod, | ||
name: String = "", | ||
path: String = "", | ||
requestCode: Int?, | ||
activityOptions: Bundle?, | ||
attributes: Attributes = Attributes(), | ||
parameters: Parameters = Parameters.Empty, | ||
) { | ||
val call = | ||
ApplicationCall( | ||
application = application, | ||
name = name, | ||
uri = path, | ||
routeMethod = routeMethod, | ||
attributes = attributes, | ||
parameters = parameters, | ||
) | ||
call.requestCode = requestCode | ||
call.activityOptions = activityOptions | ||
execute(call) | ||
} |
Oops, something went wrong.