-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from hotwired/bridge-delegate-improvements
Improve the `BridgeDelegate` to handle all bridge/webview related functionality
- Loading branch information
Showing
16 changed files
with
384 additions
and
54 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
13 changes: 11 additions & 2 deletions
13
strada/src/main/kotlin/dev/hotwire/strada/BridgeComponent.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 |
---|---|---|
@@ -1,8 +1,17 @@ | ||
package dev.hotwire.strada | ||
|
||
abstract class BridgeComponent( | ||
abstract class BridgeComponent<in D : BridgeDestination>( | ||
val name: String, | ||
private val delegate: BridgeDelegate | ||
private val delegate: BridgeDelegate<D> | ||
) { | ||
abstract fun handle(message: Message) | ||
|
||
fun send(message: Message) { | ||
delegate.bridge?.send(message) ?: run { | ||
logEvent("bridgeMessageFailedToSend", "bridge is not available") | ||
} | ||
} | ||
|
||
open fun onStart() {} | ||
open fun onStop() {} | ||
} |
6 changes: 3 additions & 3 deletions
6
strada/src/main/kotlin/dev/hotwire/strada/BridgeComponentFactory.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 |
---|---|---|
@@ -1,8 +1,8 @@ | ||
package dev.hotwire.strada | ||
|
||
class BridgeComponentFactory<in D : BridgeDelegate, out C : BridgeComponent> constructor( | ||
class BridgeComponentFactory<D : BridgeDestination, out C : BridgeComponent<D>> constructor( | ||
val name: String, | ||
private val creator: (name: String, delegate: D) -> C | ||
private val creator: (name: String, delegate: BridgeDelegate<D>) -> C | ||
) { | ||
fun create(delegate: D) = creator(name, delegate) | ||
fun create(delegate: BridgeDelegate<D>) = creator(name, delegate) | ||
} |
100 changes: 96 additions & 4 deletions
100
strada/src/main/kotlin/dev/hotwire/strada/BridgeDelegate.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 |
---|---|---|
@@ -1,8 +1,100 @@ | ||
package dev.hotwire.strada | ||
|
||
abstract class BridgeDelegate( | ||
componentFactories: List<BridgeComponentFactory<*, *>> | ||
import androidx.lifecycle.DefaultLifecycleObserver | ||
import androidx.lifecycle.LifecycleOwner | ||
|
||
@Suppress("unused") | ||
class BridgeDelegate<D : BridgeDestination>( | ||
val destination: D, | ||
private val componentFactories: List<BridgeComponentFactory<D, BridgeComponent<D>>> | ||
) { | ||
abstract fun bridgeDidInitialize() | ||
abstract fun bridgeDidReceiveMessage(message: Message) | ||
internal var bridge: Bridge? = null | ||
private var destinationIsActive = true | ||
private val components = hashMapOf<String, BridgeComponent<D>>() | ||
|
||
val activeComponents: List<BridgeComponent<D>> | ||
get() = when (destinationIsActive) { | ||
true -> components.map { it.value } | ||
else -> emptyList() | ||
} | ||
|
||
init { | ||
observeLifeCycle() | ||
} | ||
|
||
fun loadBridgeInWebView() { | ||
bridge?.load() | ||
} | ||
|
||
fun resetBridge() { | ||
bridge?.reset() | ||
} | ||
|
||
fun onWebViewAttached(bridge: Bridge?) { | ||
this.bridge = bridge | ||
this.bridge?.delegate = this | ||
|
||
if (shouldReloadBridge()) { | ||
loadBridgeInWebView() | ||
} | ||
} | ||
|
||
fun onWebViewDetached() { | ||
bridge?.delegate = null | ||
bridge = null | ||
} | ||
|
||
internal fun bridgeDidInitialize() { | ||
bridge?.register(componentFactories.map { it.name }) | ||
} | ||
|
||
internal fun bridgeDidReceiveMessage(message: Message): Boolean { | ||
return if (destination.bridgeDestinationLocation() == message.metadata?.url) { | ||
logMessage("bridgeDidReceiveMessage", message) | ||
getOrCreateComponent(message.component)?.handle(message) | ||
true | ||
} else { | ||
logMessage("bridgeDidIgnoreMessage", message) | ||
false | ||
} | ||
} | ||
|
||
private fun shouldReloadBridge(): Boolean { | ||
return destination.bridgeWebViewIsReady() && bridge?.isReady() == false | ||
} | ||
|
||
// Lifecycle events | ||
|
||
private fun observeLifeCycle() { | ||
destination.bridgeDestinationLifecycleOwner().lifecycle.addObserver(object : | ||
DefaultLifecycleObserver { | ||
override fun onStart(owner: LifecycleOwner) { onStart() } | ||
override fun onStop(owner: LifecycleOwner) { onStop() } | ||
}) | ||
} | ||
|
||
private fun onStart() { | ||
destinationIsActive = true | ||
activeComponents.forEach { it.onStart() } | ||
} | ||
|
||
private fun onStop() { | ||
destinationIsActive = false | ||
activeComponents.forEach { it.onStop() } | ||
} | ||
|
||
// Retrieve component(s) by type | ||
|
||
inline fun <reified C> component(): C? { | ||
return activeComponents.filterIsInstance<C>().firstOrNull() | ||
} | ||
|
||
inline fun <reified C> forEachComponent(action: (C) -> Unit) { | ||
activeComponents.filterIsInstance<C>().forEach { action(it) } | ||
} | ||
|
||
private fun getOrCreateComponent(name: String): BridgeComponent<D>? { | ||
val factory = componentFactories.firstOrNull { it.name == name } ?: return null | ||
return components.getOrPut(name) { factory.create(this) } | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
strada/src/main/kotlin/dev/hotwire/strada/BridgeDestination.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,9 @@ | ||
package dev.hotwire.strada | ||
|
||
import androidx.lifecycle.LifecycleOwner | ||
|
||
interface BridgeDestination { | ||
fun bridgeDestinationLocation(): String | ||
fun bridgeDestinationLifecycleOwner(): LifecycleOwner | ||
fun bridgeWebViewIsReady(): Boolean | ||
} |
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
19 changes: 16 additions & 3 deletions
19
strada/src/main/kotlin/dev/hotwire/strada/JsonExtensions.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 |
---|---|---|
@@ -1,17 +1,30 @@ | ||
package dev.hotwire.strada | ||
|
||
import kotlinx.serialization.decodeFromString | ||
import kotlinx.serialization.encodeToString | ||
import kotlinx.serialization.json.Json | ||
import kotlinx.serialization.json.JsonElement | ||
import kotlinx.serialization.json.decodeFromJsonElement | ||
import kotlinx.serialization.json.encodeToJsonElement | ||
|
||
internal inline fun <reified T> T.toJsonElement() = Json.encodeToJsonElement(this) | ||
internal fun String.parseToJsonElement() = json.parseToJsonElement(this) | ||
|
||
internal inline fun <reified T> T.toJson() = Json.encodeToString(this) | ||
internal inline fun <reified T> T.toJsonElement() = json.encodeToJsonElement(this) | ||
|
||
internal inline fun <reified T> T.toJson() = json.encodeToString(this) | ||
|
||
internal inline fun <reified T> JsonElement.decode(): T? = try { | ||
Json.decodeFromJsonElement<T>(this) | ||
json.decodeFromJsonElement<T>(this) | ||
} catch (e: Exception) { | ||
StradaLog.e("jsonElementDecodeException: ${e.stackTraceToString()}") | ||
null | ||
} | ||
|
||
internal inline fun <reified T> String.decode(): T? = try { | ||
json.decodeFromString<T>(this) | ||
} catch (e: Exception) { | ||
StradaLog.e("jsonStringDecodeException: ${e.stackTraceToString()}") | ||
null | ||
} | ||
|
||
private val json = Json { ignoreUnknownKeys = true } |
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
Oops, something went wrong.