Merge branch 'deeplink_utils' into 'master'

deeplink utils

See merge request touchinstinct/RoboSwag!5
This commit is contained in:
Grigorii Leontev 2023-03-30 11:45:28 +00:00
commit 32b6501c19
6 changed files with 162 additions and 0 deletions

1
deeplink-utils/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -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)
}
}
}
}

52
deeplink-utils/readme.md Normal file
View File

@ -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

View File

@ -0,0 +1 @@
<manifest package="ru.touchin.roboswag.core.deeplink_utils" />

View File

@ -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)
}
}

View File

@ -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() }
}