Merge branch 'deeplink_utils' into 'master'
deeplink utils See merge request touchinstinct/RoboSwag!5
This commit is contained in:
commit
32b6501c19
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apply from: "../android-configs/lib-config.gradle"
|
||||
|
||||
dependencies {
|
||||
def coroutinesVersion = '1.6.4'
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
|
||||
|
||||
constraints {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core") {
|
||||
version {
|
||||
require(coroutinesVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
# Сущность для централизованнной обработки deeplink'ов
|
||||
|
||||
### Поддерживает
|
||||
- Добавление диплинка в очередь
|
||||
- Очистка очереди и отмена всех активных операций
|
||||
- Асинхронная обработка диплинка
|
||||
- Цепочка обработчиков для диплинка
|
||||
- Поиск обработчика в иерархии экранов
|
||||
|
||||
### Использование
|
||||
|
||||
Корневая навигация
|
||||
|
||||
```kotlin
|
||||
DeepLinkHandler
|
||||
.observeDeeplink(
|
||||
observerOrder = 0,
|
||||
navigateAction = { deeplink ->
|
||||
binding.bottomNavigation.selectedItemId = deeplink.getBottomNavigationItem().menuId
|
||||
})
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
```
|
||||
|
||||
|
||||
Навигация на втором уровне
|
||||
|
||||
```kotlin
|
||||
DeepLinkHandler
|
||||
.observeDeeplink(
|
||||
observerOrder = 1,
|
||||
canHandle = { it.getBottomNavigationItem() == args.tabType },
|
||||
isFinalObserver = { !it.isListDeeplink() },
|
||||
navigateAction = { it.navigateToListScreen() }
|
||||
)
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
```
|
||||
|
||||
Навигация на третьем уровне
|
||||
|
||||
```kotlin
|
||||
DeepLinkHandler
|
||||
.observeDeeplink(
|
||||
observerOrder = 2,
|
||||
canHandle = { it.getBottomNavigationItem() == args.tabType && it.isListDeeplink() },
|
||||
navigateCondition = { it.getItemId() != null },
|
||||
navigateAction = { it.navigateToDetailsScreen() }
|
||||
)
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
```
|
||||
|
||||
Пример проекта можно посмотреть в: https://github.com/duwna/Deeplinks-test
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.roboswag.core.deeplink_utils" />
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package ru.touchin.roboswag.deeplink_utils
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
/**
|
||||
* Support observing deepliks with different conditions with queue and cancellation.
|
||||
* Must be used as Singleton for the App.
|
||||
* */
|
||||
class DeeplinkHandler {
|
||||
|
||||
/**
|
||||
* @param observerOrder
|
||||
* Entity index that can handle deeplink
|
||||
* E.g. 0 - can be root bottom navigation
|
||||
* 1 - first screen that handles chain of deeplink screens ("home" screen)
|
||||
* 2 - second screen ("list" screen)
|
||||
* */
|
||||
class CurrentDeeplink(
|
||||
val observerOrder: Int,
|
||||
val deeplink: DeeplinkModel
|
||||
)
|
||||
|
||||
private val currentDeeplink = MutableStateFlow<CurrentDeeplink?>(null)
|
||||
private val deeplinkQueue = ArrayDeque<DeeplinkModel>()
|
||||
|
||||
fun handleDeeplink(url: String) {
|
||||
val deeplink = DeeplinkModel(url)
|
||||
deeplinkQueue.add(deeplink)
|
||||
|
||||
if (currentDeeplink.value == null) {
|
||||
currentDeeplink.value = CurrentDeeplink(observerOrder = 0, deeplink = deeplink)
|
||||
}
|
||||
}
|
||||
|
||||
fun observeDeeplink(
|
||||
observerOrder: Int = 0,
|
||||
canHandle: (DeeplinkModel) -> Boolean = { true },
|
||||
isFinalObserver: (DeeplinkModel) -> Boolean = { true },
|
||||
navigateCondition: (DeeplinkModel) -> Boolean = { true },
|
||||
navigateAction: suspend (DeeplinkModel) -> Unit = { }
|
||||
): Flow<DeeplinkModel> =
|
||||
currentDeeplink
|
||||
.filterNotNull()
|
||||
.filter { it.observerOrder == observerOrder }
|
||||
.map { it.deeplink }
|
||||
.filter { canHandle(it) }
|
||||
.onEach { if (navigateCondition(it) && currentDeeplink.value != null) navigateAction(it) }
|
||||
.onEach { if (isFinalObserver(it)) markDeeplinkHandled() else it.sendToNextObserver(observerOrder) }
|
||||
|
||||
fun cancel(deeplink: DeeplinkModel) {
|
||||
deeplinkQueue.remove(deeplink)
|
||||
if (currentDeeplink.value?.deeplink == deeplink) currentDeeplink.value = null
|
||||
}
|
||||
|
||||
fun cancelAll() {
|
||||
deeplinkQueue.clear()
|
||||
currentDeeplink.value = null
|
||||
}
|
||||
|
||||
private fun markDeeplinkHandled() {
|
||||
deeplinkQueue.removeFirstOrNull()
|
||||
|
||||
currentDeeplink.value = deeplinkQueue.firstOrNull()?.let {
|
||||
CurrentDeeplink(observerOrder = 0, deeplink = it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeeplinkModel.sendToNextObserver(order: Int) {
|
||||
currentDeeplink.value = CurrentDeeplink(observerOrder = order + 1, deeplink = this)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package ru.touchin.roboswag.deeplink_utils
|
||||
|
||||
import java.net.URL
|
||||
|
||||
data class DeeplinkModel(
|
||||
val url: String
|
||||
) {
|
||||
|
||||
val parsedUrl: URL? by lazy(LazyThreadSafetyMode.NONE) {
|
||||
runCatching { URL(url) }.getOrNull()
|
||||
}
|
||||
|
||||
val pathParts: List<String>?
|
||||
get() = parsedUrl?.path
|
||||
?.split("/")
|
||||
?.filter { it.isNotEmpty() }
|
||||
}
|
||||
Loading…
Reference in New Issue