fix deep link
This commit is contained in:
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = namespace
|
applicationId = namespace
|
||||||
minSdk = libs.versions.proj.min.get().toInt()
|
minSdk = libs.versions.proj.min.get().toInt()
|
||||||
targetSdk = libs.versions.proj.target.get().toInt()
|
targetSdk = libs.versions.proj.target.get().toInt()
|
||||||
versionCode = 48
|
versionCode = 49
|
||||||
versionName = "1.24.4"
|
versionName = "1.25.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
android:supportsPictureInPicture="true">
|
android:supportsPictureInPicture="true">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -56,10 +57,6 @@
|
|||||||
android:host="box.shadowsparky.ru"
|
android:host="box.shadowsparky.ru"
|
||||||
android:scheme="https" />
|
android:scheme="https" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.app.shortcuts"
|
|
||||||
android:resource="@xml/shortcuts" />
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity-alias
|
<activity-alias
|
||||||
@@ -95,10 +92,6 @@
|
|||||||
android:host="box.shadowsparky.ru"
|
android:host="box.shadowsparky.ru"
|
||||||
android:scheme="https" />
|
android:scheme="https" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.app.shortcuts"
|
|
||||||
android:resource="@xml/shortcuts" />
|
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
|
|||||||
+40
-18
@@ -4,6 +4,7 @@ import android.content.Intent
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.SystemBarStyle
|
import androidx.activity.SystemBarStyle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
@@ -29,19 +30,14 @@ class MainActivity : ComponentActivity() {
|
|||||||
edgeToEdge(false)
|
edgeToEdge(false)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
root = retainedComponent { componentContext ->
|
root = retainedComponent { componentContext ->
|
||||||
val routeFromIntent = obtainRouteOrNull(intent?.data)
|
koin.get<RootFactory>().create(componentContext, listOf(Route.default))
|
||||||
val route = if (routeFromIntent == null) {
|
|
||||||
listOf(Route.default)
|
|
||||||
} else {
|
|
||||||
listOf(Route.default, routeFromIntent)
|
|
||||||
}
|
|
||||||
koin.get<RootFactory>().create(componentContext, route)
|
|
||||||
}
|
}
|
||||||
if (openSavedIfNeeded(intent) || openUrlIfNeeded(intent.data)) {
|
if (openSavedIfNeeded(intent) || openUrlIfNeeded(intent.data)) {
|
||||||
intent = Intent(intent)
|
intent = Intent(intent)
|
||||||
.setData(null)
|
.setData(null)
|
||||||
.setAction(null)
|
.setAction(null)
|
||||||
}
|
}
|
||||||
|
log("onCreate $intent")
|
||||||
val isPiggyActivity = componentName?.className == PigReceiver.PIGGY_CLS
|
val isPiggyActivity = componentName?.className == PigReceiver.PIGGY_CLS
|
||||||
setContent {
|
setContent {
|
||||||
if (isPiggyActivity) {
|
if (isPiggyActivity) {
|
||||||
@@ -54,10 +50,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
edgeToEdge(isSystemInDarkTheme())
|
edgeToEdge(isSystemInDarkTheme())
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
root.onFinish.collect {
|
root.onFinish.collect { finishAndRemoveTask() }
|
||||||
finishAndRemoveTask()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (savedInstanceState == null) handleIntent(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun edgeToEdge(isDarkTheme: Boolean) {
|
private fun edgeToEdge(isDarkTheme: Boolean) {
|
||||||
@@ -84,15 +79,42 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
if (!openSavedIfNeeded(intent)) {
|
log("onNewIntent $intent")
|
||||||
val uri = intent.data ?: return
|
setIntent(intent)
|
||||||
val route = obtainRouteOrNull(uri)
|
handleIntent(intent)
|
||||||
if (route != null) {
|
}
|
||||||
root.nav(route, true)
|
|
||||||
} else {
|
private fun handleIntent(intent: Intent?) {
|
||||||
openUrlIfNeeded(uri)
|
intent ?: return
|
||||||
}
|
if (openSavedIfNeeded(intent)) {
|
||||||
|
clearIntent(intent)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
val uri = intent.data ?: return
|
||||||
|
val route = obtainRouteOrNull(uri)
|
||||||
|
if (route != null) {
|
||||||
|
val currentRoute = root.childStack.value.active.configuration
|
||||||
|
if (currentRoute != route) {
|
||||||
|
root.nav(route, true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
openUrlIfNeeded(uri)
|
||||||
|
}
|
||||||
|
clearIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearIntent(intent: Intent) {
|
||||||
|
intent.data = null
|
||||||
|
intent.action = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
log("onDestroy")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log(msg: String) {
|
||||||
|
Log.d("MainActivityDebug", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openUrlIfNeeded(uri: Uri?): Boolean {
|
private fun openUrlIfNeeded(uri: Uri?): Boolean {
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<shortcut
|
|
||||||
android:enabled="true"
|
|
||||||
android:icon="@drawable/baseline_saved_search_24"
|
|
||||||
android:shortcutId="saved_movies"
|
|
||||||
android:shortcutLongLabel="@string/saved_movies_long"
|
|
||||||
android:shortcutShortLabel="@string/saved_movies">
|
|
||||||
<intent
|
|
||||||
android:action="ru.shadowsparky.vbox.action.OPEN_SAVED_MOVIES"
|
|
||||||
android:targetClass="ru.shadowsparky.videobox.multiplatform.MainActivity"
|
|
||||||
android:targetPackage="ru.shadowsparky.videobox.multiplatform" />
|
|
||||||
</shortcut>
|
|
||||||
</shortcuts>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shortcuts></shortcuts>
|
|
||||||
+8
-1
@@ -102,6 +102,7 @@ object UrlRoutes {
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// "/chat
|
||||||
rule(
|
rule(
|
||||||
match = { it is Route.Chat },
|
match = { it is Route.Chat },
|
||||||
build = {
|
build = {
|
||||||
@@ -110,7 +111,13 @@ object UrlRoutes {
|
|||||||
emptyMap()
|
emptyMap()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
parse = { _, _ -> Route.Chat() }
|
parse = { segs, _ ->
|
||||||
|
if (segs.size == 1 && segs[0] == Seg.CHAT) {
|
||||||
|
Route.Chat()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
// "/search"
|
// "/search"
|
||||||
|
|||||||
+13
-11
@@ -10,7 +10,6 @@ import com.arkivanov.essenty.lifecycle.coroutines.repeatOnLifecycle
|
|||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
@@ -27,26 +26,29 @@ import ru.shadowsparky.vbox.shared.presentation.nav.Route
|
|||||||
class VBoxRootComponent(
|
class VBoxRootComponent(
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
tokenStorage: TokenStorage,
|
tokenStorage: TokenStorage,
|
||||||
override val initStack: List<Route>,
|
private val initRoute: Route,
|
||||||
private val healthCheck: HeathCheck,
|
private val healthCheck: HeathCheck,
|
||||||
val imageLoaderProvider: ImageLoaderProvider,
|
val imageLoaderProvider: ImageLoaderProvider,
|
||||||
updateComponentFactory: UpdateComponentFactory,
|
updateComponentFactory: UpdateComponentFactory,
|
||||||
private val remoteEventListener: RemoteEventListener,
|
private val remoteEventListener: RemoteEventListener,
|
||||||
|
override val initStack: List<Route> = listOf(initRoute),
|
||||||
override val serializer: KSerializer<Route> = Route.serializer(),
|
override val serializer: KSerializer<Route> = Route.serializer(),
|
||||||
override val koin: Koin = ru.shadowsparky.koin
|
override val koin: Koin = ru.shadowsparky.koin
|
||||||
) : RootComponent<Route>(componentContext), WebNavigationOwner {
|
) : RootComponent<Route>(componentContext), WebNavigationOwner {
|
||||||
val updateComponent = updateComponentFactory.create(childContext("UpdateComponentRoot"))
|
val updateComponent = updateComponentFactory.create(childContext("UpdateComponentRoot"))
|
||||||
private val scope = createComponentScope()
|
private val scope = createComponentScope()
|
||||||
val isLoggedIn = tokenStorage.token.map {
|
|
||||||
if (it != null) {
|
|
||||||
runCatching { healthCheck.check() }.getOrNull() ?: return@map false
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
scope.launch {
|
||||||
|
repeatOnLifecycle {
|
||||||
|
tokenStorage.token.collect {
|
||||||
|
if (it != null) {
|
||||||
|
val invalid = runCatching { healthCheck.check() }.getOrNull() == null
|
||||||
|
if (invalid) { navReplace(initRoute) }
|
||||||
|
}
|
||||||
|
} // i love you
|
||||||
|
}
|
||||||
|
}
|
||||||
scope.launch {
|
scope.launch {
|
||||||
repeatOnLifecycle {
|
repeatOnLifecycle {
|
||||||
tokenStorage.token.flatMapLatest {
|
tokenStorage.token.flatMapLatest {
|
||||||
@@ -103,7 +105,7 @@ class RootFactory(
|
|||||||
return VBoxRootComponent(
|
return VBoxRootComponent(
|
||||||
context,
|
context,
|
||||||
tokenStorage,
|
tokenStorage,
|
||||||
listOf(Route.Loading(next = initStack.toSet())),
|
Route.Loading(next = initStack.toSet()),
|
||||||
heathCheck,
|
heathCheck,
|
||||||
imageLoaderProvider,
|
imageLoaderProvider,
|
||||||
updateComponentFactory,
|
updateComponentFactory,
|
||||||
|
|||||||
+10
-6
@@ -33,6 +33,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
@@ -292,12 +293,15 @@ private fun OverviewDescription(
|
|||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
modifier = Modifier.padding(vertical = 8.dp)
|
||||||
)
|
)
|
||||||
Text(
|
|
||||||
text = details.desc.trim(),
|
SelectionContainer {
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
Text(
|
||||||
maxLines = if (showDetails.value) Int.MAX_VALUE else 3,
|
text = details.desc.trim(),
|
||||||
overflow = TextOverflow.Ellipsis
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
maxLines = if (showDetails.value) Int.MAX_VALUE else 3,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = !showDetails.value,
|
visible = !showDetails.value,
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawing
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -86,13 +84,6 @@ fun mainInternal() {
|
|||||||
r.onFinish.collect { r.navReplace(Route.Loading()) }
|
r.onFinish.collect { r.navReplace(Route.Loading()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val isLoggedIn by r.isLoggedIn.collectAsState(null)
|
|
||||||
LaunchedEffect(isLoggedIn) {
|
|
||||||
if (isLoggedIn == false) {
|
|
||||||
r.navReplace(Route.Loading())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(Modifier.fillMaxSize().padding(WindowInsets.safeDrawing.asPaddingValues())) {
|
Box(Modifier.fillMaxSize().padding(WindowInsets.safeDrawing.asPaddingValues())) {
|
||||||
Nav(r)
|
Nav(r)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user