Deep Link Synthetic BackStack Recipe

במתכון הזה נדגים איך להחיל את עקרונות הניווט בהקשר של קישורי עומק באמצעות ניהול של backStack סינתטי ושל ערימות משימות.

מבנה המתכון

המתכון הזה מדמה תרחיש מהעולם האמיתי שבו אפליקציה א' מקשרת באמצעות קישור עומק לאפליקציה ב'.

האפליקציה A מדומה על ידי המודול syntheticbackstack, שמכיל את SyntheticBackStackDeepLinkActivity שמאפשר ליצור intent של קישור עומק ולהפעיל אותו במשימה הקיימת או במשימה חדשה.

הסימולציה של 'אפליקציה ב' מתבצעת על ידי המודול syntheticbackstackapp, שמכיל את SyntheticBackStackAppActivity שאליו מתבצע קישור עומק. במודול הזה מוסבר איך ליצור backStack סינתטי ואיך לנהל את ערימת המשימות בצורה נכונה כדי לתמוך גם בלחצן 'הקודם' וגם בלחצן 'למעלה'.

איך משתמשים

מוודאים שגם app וגם syntheticbackstackapp מותקנים באמולטור או במכשיר המחובר. מוודאים שהקישור "www.nav3deeplink.com" נתמך על ידי syntheticbackstackapp שהותקן.

בדף הנחיתה של המתכון, בוחרים את המסננים ולוחצים על הלחצן כדי ליצור קישור עמוק. אמורה להיפתח הפעילות של syntheticbackstackapp.

איך זה עובד

המתכון פועל לפי ההנחיות לקישורי עומק שמסוכמות כאן.

כדי לראות את ההתנהגות של Existing Task:

  1. פתיחת קישור עומק באמצעות המשימה הנוכחית
  2. במכשיר, מחליקים למעלה כדי לראות את כל האפליקציות האחרונות.
  3. שימו לב שהפעילות החדשה נפתחת באפליקציית Nav3Recipes
  4. לוחצים על הכפתור "הקודם" כדי לחזור ל-Activity המקורית
  5. חוזרים על שלב 1.
  6. לוחצים על לחצן החץ למעלה כדי לעבור למסך האב
  7. במכשיר, מחליקים למעלה כדי לראות את כל האפליקציות האחרונות.
  8. שימו לב שהפעילות החדשה נפתחה עכשיו באפליקציית Nav3SyntheticBackStack

כדי לראות את ההתנהגות של New Task:

  1. פתיחת קישור עומק באמצעות משימה חדשה
  2. במכשיר, מחליקים למעלה כדי לראות את כל האפליקציות האחרונות.
  3. שימו לב שהפעילות החדשה נפתחת באפליקציה Nav3SyntheticBackStack
  4. לוחצים על הכפתור 'למעלה' או 'הקודם' כדי לעבור למסך ההורה

הטמעת ליבה

פונקציות העזר העיקריות של navigateUp ושל בניית synthetic backStack זמינות כאן

קריאה נוספת

במדריך לקישורי עומק מוסבר על העקרונות של קישורי עומק ואיך להשתמש בהם בגרסה 3 של Navigation.

package com.example.nav3recipes.deeplink.handlerequests.syntheticbackstack

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.core.net.toUri
import androidx.lifecycle.compose.dropUnlessResumed
import com.example.nav3recipes.common.deeplink.EntryScreen
import com.example.nav3recipes.common.deeplink.LIST_FIRST_NAMES
import com.example.nav3recipes.common.deeplink.LIST_LOCATIONS
import com.example.nav3recipes.common.deeplink.MenuDropDown
import com.example.nav3recipes.common.deeplink.PaddedButton
import com.example.nav3recipes.common.deeplink.TextContent
import com.example.nav3recipes.ui.setEdgeToEdgeConfig

internal const val ADVANCED_PATH_BASE = "https://www.nav3deeplink.com"

/**
 * The recipe entry point that allows users to create a deep link and make a request with it.
 *
 * **HOW THIS RECIPE WORKS** This recipe simulates a real-world scenario where "App A" deep links
 * into "App B".
 *
 * "App A" is simulated by this current module `syntheticbackstack`, which
 * contains the [SyntheticBackStackDeepLinkActivity] that allows you to create a deeplink intent and
 * trigger that in either the existing Task, or in a new Task.
 *
 * "App B" is simulated by the module `syntheticbackstackapp`, which contains
 * the SyntheticBackStackAppActivity that you deeplink into. That module shows you how to build a synthetic backStack
 * and how to manage the Task stack properly in order to support both Back and Up buttons.
 *
 * See the [README](README.md) file of current module for more info on advanced deep linking.
 */
class SyntheticBackStackDeepLinkActivity: ComponentActivity() {

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

        setContent {
            EntryScreen("Sandbox - Build Your Deeplink Intent") {
                val initFirstName = MENU_OPTIONS_FIRST_NAME.values.first().first()
                val initLocation = MENU_OPTIONS_LOCATION.values.last().first()
                val initTaskStack = MENU_OPTIONS_TASK_STACK.values.first().first()
                var firstName by remember { mutableStateOf(initFirstName) }
                var location by remember { mutableStateOf(initLocation) }
                var taskStack by remember { mutableStateOf(initTaskStack) }

                // select first name
                MenuDropDown(
                    menuOptions = MENU_OPTIONS_FIRST_NAME,
                ) { _, selected ->
                    firstName = selected
                }

                // select first name
                MenuDropDown(
                    menuOptions = MENU_OPTIONS_LOCATION,
                ) { _, selected ->
                    location = selected
                }

                // select current task stack or build new task stack
                MenuDropDown(
                    menuOptions = MENU_OPTIONS_TASK_STACK,
                ) { _, selected ->
                    taskStack = selected
                }

                // build final deeplink URL and Intent
                val finalUrl = "${ADVANCED_PATH_BASE}/user/$firstName/$location"

                // display Intent info
                val flagString = if (taskStack == TAG_NEW_TASK) {
                    "Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK"
                } else "<none>"
                val intentString = """
                    | Final Intent:
                    | data = "$finalUrl"
                    | action = Intent.ACTION_VIEW
                    | flags = $flagString
                """.trimMargin()

                TextContent(intentString)

                // deeplink to target
                PaddedButton("Deeplink Away!", onClick = dropUnlessResumed {
                    val intent = Intent().apply {
                        data = finalUrl.toUri()
                        action = Intent.ACTION_VIEW
                        if (taskStack == TAG_NEW_TASK) {
                            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                        }
                    }

                    startActivity(intent)
                })
            }
        }
    }
}

private const val TAG_FIRST_NAME = "firstName"
private const val TAG_LOCATION = "location"
private const val TAG_TASK_STACK = "Task stack"
private const val TAG_CURRENT_TASK = "Use Current Task Stack"
private const val TAG_NEW_TASK = "Start New Task Stack"

private val MENU_OPTIONS_FIRST_NAME = mapOf(
    TAG_FIRST_NAME to LIST_FIRST_NAMES
)

private val MENU_OPTIONS_LOCATION = mapOf(
    TAG_LOCATION to LIST_LOCATIONS
)

private val MENU_OPTIONS_TASK_STACK = mapOf(
    TAG_TASK_STACK to listOf(TAG_CURRENT_TASK, TAG_NEW_TASK),
)