Özel DeepLinkMatcher Tarifi

Bu tarifte, özel istek ekstraları ve Kotlinx Serialization kullanarak Navigation 3'te özel bir DeepLinkMatcher nasıl oluşturulacağı gösterilmektedir.

İşleyiş şekli

Bu tarif iki etkinlikten oluşur:

  • CustomDeepLinkMatcherActivity: Kullanıcı girişini kabul eder, HomeKey örneğini JSON'a dönüştürür, RequestExtrasKey aracılığıyla Intent ekine iliştirir ve MainActivity'i başlatır.
  • MainActivity: DeepLinkRequest(intent) oluşturur, JsonDeepLinkMatcher ile değerlendirir, HomeKey kodunu çözer ve NavDisplay'de başlangıç rotası olarak ayarlar.

Temel Kavramlar

  1. Özel RequestExtrasKey: JsonDeepLinkMatcherKey, RequestExtrasKey<String>'yi uygulayan özel bir ek anahtar tanımlar. Bu anahtar, DeepLinkRequest.extras içinde serileştirilmiş JSON yüklerini tür güvenli bir şekilde depolamak ve okumak için kullanılır.

  2. Özel DeepLinkMatcher: JsonDeepLinkMatcher<T>, DeepLinkMatcher<T, MatchResult<T>> öğesini genişletir ve request.extras[JsonDeepLinkMatcherKey] öğesini ayıklayıp Kotlinx Serialization kullanarak kesin olarak türlenmiş bir NavKey öğesine kod çözmek için matchRequest(request) öğesini uygular.

package com.example.nav3recipes.deeplink.usecases.matcher

import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.dropUnlessResumed
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.deeplink.DeepLinkMatcher
import androidx.navigation3.runtime.deeplink.DeepLinkRequest
import androidx.navigation3.runtime.deeplink.RequestExtrasKey
import com.example.nav3recipes.common.deeplink.EntryScreen
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json

class CustomDeepLinkMatcherActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        setEdgeToEdgeConfig()
        super.onCreate(savedInstanceState)

        setContent {
            EntryScreen {
                Column(
                    verticalArrangement = Arrangement.spacedBy(32.dp),
                    horizontalAlignment = Alignment.CenterHorizontally,
                ) {
                    var text by remember { mutableStateOf("") }
                    OutlinedTextField(
                        placeholder = { Text("Your name...", color = Color.Black.copy(alpha = 0.5f)) },
                        value = text,
                        singleLine = true,
                        onValueChange = { text = it },
                    )

                    ElevatedButton(
                        onClick =
                            dropUnlessResumed {
                                val intent = Intent(
                                    this@CustomDeepLinkMatcherActivity,
                                    MainActivity::class.java
                                )
                                val json = Json.encodeToString(HomeKey.serializer(), HomeKey(text))
                                intent.putExtra(JsonDeepLinkMatcherKey.toString(), json)
                                startActivity(intent)
                        }
                    ) {
                        Text("Sign up")
                    }
                }
            }
        }
    }
}

internal data object JsonDeepLinkMatcherKey: RequestExtrasKey<String>

internal class JsonDeepLinkMatcher<T: NavKey>(val serializer: KSerializer<T>): DeepLinkMatcher<T, DeepLinkMatcher.MatchResult<T>>() {
    override fun matchRequest(request: DeepLinkRequest): MatchResult<T>? {
        val json = request.extras[JsonDeepLinkMatcherKey] ?: return null
        return try {
            val result = Json.decodeFromString(serializer, json)
            MatchResult(result)
        } catch (e: SerializationException) {
            Log.v("DeepLinkMatcher", "Failed to decode json", e)
            return null
        }
    }
}
package com.example.nav3recipes.deeplink.usecases.matcher

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.deeplink.DeepLinkRequest
import androidx.navigation3.runtime.deeplink.invoke
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import com.example.nav3recipes.common.deeplink.EntryScreen
import com.example.nav3recipes.common.deeplink.TextContent
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer

@Serializable
internal data class HomeKey(val name: String): NavKey

@Serializable
internal object FallbackKey: NavKey

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        setEdgeToEdgeConfig()
        super.onCreate(savedInstanceState)

        val request = DeepLinkRequest(intent)
        val deepLinkMatcher = createJsonDeepLinkMatcher<HomeKey>()

        val matchResult = deepLinkMatcher.match(request)
        val key = matchResult?.key ?: FallbackKey

        setContent {
            val backStack: NavBackStack<NavKey> = rememberNavBackStack(key)
            NavDisplay(
                backStack = backStack,
                onBack = backStack::removeLastOrNull,
                entryProvider = entryProvider {
                    entry<HomeKey> { key ->
                        EntryScreen("Welcome") {
                            TextContent(key.name)
                        }
                    }
                    entry<FallbackKey> { key ->
                        EntryScreen("Fallback Key") {
                            TextContent(
                                "Failed to deep link - DeepLinkRequest " +
                                        "did not match with any DeepLinkMatcher"
                            )
                        }
                    }
                }
            )
        }
    }
}

// Optional JsonDeepLinkMatcher factory function that automatically captures KSerializer for T.
private inline fun <reified T : NavKey> createJsonDeepLinkMatcher(): JsonDeepLinkMatcher<T> {
    val serializer = serializer<T>()
    return JsonDeepLinkMatcher(serializer)
}