लिखने की सुविधा के साथ नेविगेशन

नेविगेशन कॉम्पोनेंट Jetpack के लिए सहायता देता है ऐप्लिकेशन लिखें. अलग-अलग कंपोज़ेबल के बीच नेविगेट किया जा सकता है नेविगेशन कॉम्पोनेंट के इन्फ़्रास्ट्रक्चर का इस्तेमाल करके, सुविधाएँ.

सेटअप

कंपोज़ की सुविधा के साथ काम करने के लिए, अपने ऐप्लिकेशन मॉड्यूल में इस डिपेंडेंसी का इस्तेमाल करें build.gradle फ़ाइल:

ग्रूवी

dependencies {
    def nav_version = "2.7.7"

    implementation "androidx.navigation:navigation-compose:$nav_version"
}

Kotlin

dependencies {
    val nav_version = "2.7.7"

    implementation("androidx.navigation:navigation-compose:$nav_version")
}

शुरू करें

किसी ऐप्लिकेशन में नेविगेशन लागू करते समय, नेविगेशन होस्ट लागू करें. देखें. ज़्यादा जानकारी के लिए, नेविगेशन की खास जानकारी देखें.

Compose में NavController बनाने का तरीका जानने के लिए, ‘लिखें’ देखें नेविगेशन कंट्रोलर बनाएं सेक्शन में जाएं.

NavHost बनाएं

'लिखें' विंडो में NavHost बनाने का तरीका जानने के लिए, ‘लिखें’ सेक्शन देखें अपना नेविगेशन ग्राफ़ डिज़ाइन करें.

किसी कंपोज़ेबल में नेविगेट करने के बारे में जानकारी के लिए, डेस्टिनेशन आर्किटेक्चर दस्तावेज़.

नेविगेशन कंपोज़िशन, कंपोज़ेबल के बीच आर्ग्युमेंट पास करने पर भी काम करता है गंतव्य. ऐसा करने के लिए, आपको अपने रूट तय करता है, जैसा कि डीप में आर्ग्युमेंट जोड़ने के लिए किया जाता है लिंक: नेविगेशन लाइब्रेरी:

NavHost(startDestination = "profile/{userId}") {
    ...
    composable("profile/{userId}") {...}
}

डिफ़ॉल्ट रूप से, सभी आर्ग्युमेंट को स्ट्रिंग के तौर पर पार्स किया जाता है. इसका arguments पैरामीटर composable(), NamedNavArgument ऑब्जेक्ट की सूची स्वीकार करता है. आप navArgument() तरीके का इस्तेमाल करके, तेज़ी से NamedNavArgument बनाएं और फिर उसका सटीक type बताएं:

NavHost(startDestination = "profile/{userId}") {
    ...
    composable(
        "profile/{userId}",
        arguments = listOf(navArgument("userId") { type = NavType.StringType })
    ) {...}
}

आपको NavBackStackEntry से आर्ग्युमेंट एक्सट्रैक्ट करने चाहिए composable() फ़ंक्शन के लैम्डा में उपलब्ध है.

composable("profile/{userId}") { backStackEntry ->
    Profile(navController, backStackEntry.arguments?.getString("userId"))
}

डेस्टिनेशन में आर्ग्युमेंट को पास करने के लिए, आपको इसे रूट में जोड़ना होगा navigate पर कॉल करने पर:

navController.navigate("profile/user1234")

काम करने वाले टाइप की सूची देखने के लिए, इनके बीच डेटा पास करें डेस्टिनेशन.

नेविगेट करते समय जटिल डेटा वापस पाएं

यह सलाह दी जाती है कि नेविगेट करते समय, जटिल डेटा ऑब्जेक्ट के आस-पास न जाएं, लेकिन इसके बजाय, हम यूनीक आइडेंटिफ़ायर जैसी सबसे ज़रूरी जानकारी पास करते हैं या किसी अन्य तरह के आईडी की मदद से, नेविगेशन से जुड़ी कार्रवाइयां करते समय आर्ग्युमेंट के तौर पर:

// Pass only the user ID when navigating to a new destination as argument
navController.navigate("profile/user1234")

कॉम्प्लेक्स ऑब्जेक्ट को एक ही जगह पर डेटा के तौर पर सेव करना चाहिए, जैसे कि को ट्रैक किया जा सकता है. नेविगेट करने के बाद, डेस्टिनेशन पर पहुंचने के बाद, एक ही सोर्स से ज़रूरी जानकारी लोड करने के लिए, पास किया गया आईडी. अपने ViewModel में उन तर्कों को फिर से पाने के लिए जो इसके लिए ज़िम्मेदार हैं डेटा लेयर को ऐक्सेस करने के लिए, ViewModel के SavedStateHandle का इस्तेमाल करें:

class UserViewModel(
    savedStateHandle: SavedStateHandle,
    private val userInfoRepository: UserInfoRepository
) : ViewModel() {

    private val userId: String = checkNotNull(savedStateHandle["userId"])

    // Fetch the relevant user information from the data layer,
    // ie. userInfoRepository, based on the passed userId argument
    private val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(userId)

// …

}

इससे, कॉन्फ़िगरेशन में बदलावों के दौरान और किसी भी तरह के डेटा को नुकसान से बचाने में मदद मिलती है डेटा में अंतर दिख सकता है.

इस बारे में ज़्यादा जानकारी पाने के लिए कि आपको जटिल डेटा को और साथ ही काम करने वाले आर्ग्युमेंट टाइप की सूची देखें, तो इसके बीच डेटा पास करें डेस्टिनेशन.

वैकल्पिक आर्ग्युमेंट जोड़ें

नेविगेशन कंपोज़िशन, वैकल्पिक नेविगेशन आर्ग्युमेंट के साथ भी काम करता है. ज़रूरी नहीं है आर्ग्युमेंट और ज़रूरी आर्ग्युमेंट के बीच दो तरह से अंतर होता है:

  • उन्हें क्वेरी पैरामीटर सिंटैक्स ("?argName={argName}") का इस्तेमाल करके शामिल करना ज़रूरी है
  • उनके पास defaultValue सेट होना चाहिए या nullable = true (जो डिफ़ॉल्ट रूप से null पर सेट करता है)

इसका मतलब यह है कि सभी वैकल्पिक आर्ग्युमेंट को सूची के तौर पर composable() फ़ंक्शन:

composable(
    "profile?userId={userId}",
    arguments = listOf(navArgument("userId") { defaultValue = "user1234" })
) { backStackEntry ->
    Profile(navController, backStackEntry.arguments?.getString("userId"))
}

अब, भले ही गंतव्य, defaultValue, में कोई तर्क पास न किया गया हो, इसके बजाय, "user1234" का इस्तेमाल किया गया है.

रूट के ज़रिए आर्ग्युमेंट को हैंडल करने के स्ट्रक्चर का मतलब है कि कंपोज़ेबल, नेविगेशन से पूरी तरह अलग होते हैं और उन्हें सबसे बेहतर बनाते हैं जिसे टेस्ट किया जा सकता है.

नेविगेशन कंपोज़ की सुविधा, ऐसे इंप्लिसिट डीप लिंक के साथ काम करती है जिन्हें composable() फ़ंक्शन को भी शामिल कर सकते हैं. इसका deepLinks पैरामीटर, NavDeepLink ऐसे ऑब्जेक्ट हैं जिन्हें navDeepLink() तरीका:

val uri = "https://www.example.com"

composable(
    "profile?id={id}",
    deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })
) { backStackEntry ->
    Profile(navController, backStackEntry.arguments?.getString("id"))
}

ये डीप लिंक आपको किसी खास यूआरएल, कार्रवाई या MIME टाइप को कंपोज़ेबल. डिफ़ॉल्ट रूप से, ये डीप लिंक बाहरी ऐप्लिकेशन को सार्वजनिक नहीं किए जाते. यहां की यात्रा पर हूं इन डीप लिंक को बाहर से उपलब्ध कराएं, आपको <intent-filter> एलिमेंट आपके ऐप्लिकेशन की manifest.xml फ़ाइल में जोड़े जाते हैं. डीप लिंक को चालू करने के लिए, लिंक नहीं किया है, तो आपको मेनिफ़ेस्ट के <activity> एलिमेंट:

<activity …>
  <intent-filter>
    ...
    <data android:scheme="https" android:host="www.example.com" />
  </intent-filter>
</activity>

डीप लिंक होने पर, नेविगेशन अपने-आप उस कंपोज़ेबल में डीप लिंक कर देता है किसी दूसरे ऐप्लिकेशन से ट्रिगर हुआ हो.

इन्हीं डीप लिंक का इस्तेमाल करके, PendingIntent को किसी कंपोज़ेबल से सही डीप लिंक जोड़ने के लिए:

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/$id".toUri(),
    context,
    MyActivity::class.java
)

val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

इसके बाद, अन्य PendingIntent की तरह इस deepLinkPendingIntent का इस्तेमाल करके, डीप लिंक डेस्टिनेशन पर अपना ऐप्लिकेशन खोलें.

नेस्ट किया गया नेविगेशन

नेस्ट किए गए नेविगेशन ग्राफ़ बनाने के तरीके के बारे में जानकारी के लिए, यह देखें नेस्ट किए गए ग्राफ़.

सबसे नीचे वाले नेविगेशन बार के साथ इंटिग्रेशन

कंपोज़ेबल हैरारकी में NavController को किसी बड़े लेवल पर सेट करके, नीचे नेविगेशन बार जैसे दूसरे कॉम्पोनेंट के साथ कॉम्पोनेंट. ऐसा करने पर, सबसे नीचे मौजूद आइकॉन को चुनकर, नेविगेट करने में मदद मिलती है बार.

BottomNavigation और BottomNavigationItem कॉम्पोनेंट का इस्तेमाल करने के लिए, अपने Android ऐप्लिकेशन पर androidx.compose.material निर्भरता जोड़ें.

ग्रूवी

dependencies {
    implementation "androidx.compose.material:material:1.6.8"
}

android {
    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.15"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Kotlin

dependencies {
    implementation("androidx.compose.material:material:1.6.8")
}

android {
    buildFeatures {
        compose = true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.15"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

नीचे के नेविगेशन बार में मौजूद आइटम को अपने नेविगेशन ग्राफ़ में मौजूद रास्तों से लिंक करने के लिए, हमारा सुझाव है कि सील की गई क्लास की जानकारी दें. जैसे, Screen यहां दिख रही है में डेस्टिनेशन के लिए रूट और स्ट्रिंग रिसॉर्स आईडी होता है.

sealed class Screen(val route: String, @StringRes val resourceId: Int) {
    object Profile : Screen("profile", R.string.profile)
    object FriendsList : Screen("friendslist", R.string.friends_list)
}

फिर उन आइटम को एक सूची में रखें, जिसका इस्तेमाल BottomNavigationItem:

val items = listOf(
   Screen.Profile,
   Screen.FriendsList,
)

अपने BottomNavigation कंपोज़ेबल में, मौजूदा NavBackStackEntry पाएं currentBackStackEntryAsState() फ़ंक्शन का इस्तेमाल करके. इस एंट्री से आपको मौजूदा NavDestination को ऐक्सेस करें. हर एक आइटम की स्थिति इसके बाद, आइटम के रूट की तुलना करके, BottomNavigationItem का पता लगाया जा सकता है मौजूदा गंतव्य के रूट और उसके मूल गंतव्यों के साथ आप स्थितियों को हैंडल करने के लिए नेस्ट किए गए नेविगेशन का इस्तेमाल करने पर NavDestination हैरारकी.

आइटम के रूट का इस्तेमाल, onClick Lambda को इस कॉल में कनेक्ट करने के लिए भी किया जाता है: navigate ताकि आइटम पर टैप करने से उस आइटम पर नेविगेट किया जा सके. इस्तेमाल करके saveState और restoreState फ़्लैग, उसकी स्थिति और पिछली गतिविधियां बॉटम नेविगेशन को आपस में बदलने पर, आइटम सही तरीके से सेव और वापस आ जाता है आइटम.

val navController = rememberNavController()
Scaffold(
  bottomBar = {
    BottomNavigation {
      val navBackStackEntry by navController.currentBackStackEntryAsState()
      val currentDestination = navBackStackEntry?.destination
      items.forEach { screen ->
        BottomNavigationItem(
          icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
          label = { Text(stringResource(screen.resourceId)) },
          selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
          onClick = {
            navController.navigate(screen.route) {
              // Pop up to the start destination of the graph to
              // avoid building up a large stack of destinations
              // on the back stack as users select items
              popUpTo(navController.graph.findStartDestination().id) {
                saveState = true
              }
              // Avoid multiple copies of the same destination when
              // reselecting the same item
              launchSingleTop = true
              // Restore state when reselecting a previously selected item
              restoreState = true
            }
          }
        )
      }
    }
  }
) { innerPadding ->
  NavHost(navController, startDestination = Screen.Profile.route, Modifier.padding(innerPadding)) {
    composable(Screen.Profile.route) { Profile(navController) }
    composable(Screen.FriendsList.route) { FriendsList(navController) }
  }
}

यहां आप NavController.currentBackStackEntryAsState() का फ़ायदा लेते हैं NavHost फ़ंक्शन से navController स्थिति को बाहर रखने का तरीका, और इसे BottomNavigation कॉम्पोनेंट के साथ शेयर करें. इसका मतलब है कि BottomNavigation में अपने-आप अप-टू-डेट जानकारी होती है.

नेविगेशन कंपोज़ में टाइप की सुरक्षा

इस पेज पर दिया गया कोड टाइप-सुरक्षित नहीं है. navigate() पर कॉल किया जा सकता है फ़ंक्शन के साथ काम करता है. हालांकि, आपके पास ये विकल्प हैं रनटाइम के दौरान, अपने नेविगेशन कोड को ऐसे व्यवस्थित करें कि वह टाइप-सुरक्षित न हो. ऐसा करके, क्रैश होने से बचें और पक्का करें कि:

  • किसी डेस्टिनेशन या नेविगेशन ग्राफ़ पर नेविगेट करते समय आपके दिए गए आर्ग्युमेंट सही टाइप हैं और सभी ज़रूरी आर्ग्युमेंट मौजूद हैं.
  • SavedStateHandle से हासिल किए गए आर्ग्युमेंट सही तरह के हैं.

इसके बारे में ज़्यादा जानकारी के लिए, Kotlin DSL और नेविगेशन में टाइप सेफ़्टी लिखें.

इंटरऑपरेबिलिटी (दूसरे सिस्टम के साथ काम करना)

अगर आपको लिखने के साथ नेविगेशन कॉम्पोनेंट का इस्तेमाल करना है, तो आपके पास दो विकल्प हैं:

  • फ़्रैगमेंट के लिए नेविगेशन कॉम्पोनेंट की मदद से, नेविगेशन ग्राफ़ तय करें.
  • Compose का इस्तेमाल करके नेविगेशन ग्राफ़ को NavHost की मदद से तय करें गंतव्य. ऐसा सिर्फ़ तब किया जा सकता है, जब नेविगेशन में सभी स्क्रीन पर ग्राफ़ कंपोज़ेबल हैं.

इसलिए, मिश्रित कंपोज़ और दृश्य ऐप्लिकेशन के लिए यह सुझाव दिया जाता है कि फ़्रैगमेंट पर आधारित नेविगेशन कॉम्पोनेंट. फ़्रैगमेंट, व्यू के आधार पर बने रहेंगे उन स्क्रीन, कंपोज़ की सुविधा वाली स्क्रीन, और ऐसी स्क्रीन जो व्यू और कंपोज़, दोनों का इस्तेमाल करती हैं. एक बार फ़्रैगमेंट का कॉन्टेंट 'लिखें' विंडो में मौजूद है. अगला चरण उन सभी स्क्रीन को जोड़ना है नेविगेशन कंपोज़ की सुविधा के साथ मिलकर सभी फ़्रैगमेंट हटाएं.

Compose कोड में डेस्टिनेशन बदलने के लिए, ऐसे इवेंट दिखाते हैं जो इन्हें क्रम में मौजूद किसी भी कंपोज़ेबल को पास और ट्रिगर किया जाना चाहिए:

@Composable
fun MyScreen(onNavigate: (Int) -> Unit) {
    Button(onClick = { onNavigate(R.id.nav_profile) } { /* ... */ }
}

अपने फ़्रैगमेंट में, आपको Compose और फ़्रैगमेंट पर आधारित डेटा के बीच कनेक्शन बनाना होगा नेविगेशन कॉम्पोनेंट: NavController को ढूंढकर और गंतव्य:

override fun onCreateView( /* ... */ ) {
    setContent {
        MyScreen(onNavigate = { dest -> findNavController().navigate(dest) })
    }
}

इसके अलावा, NavController को कॉन्टेंट की हैरारकी में भी शामिल किया जा सकता है. हालांकि, आसान फ़ंक्शन को सार्वजनिक करने के बारे में ज़्यादा बार पता लगाया जा सकता है और उन्हें टेस्ट किया जा सकता है.

टेस्ट करना

टेस्टिंग चालू करने के लिए, कंपोज़ेबल डेस्टिनेशन से नेविगेशन कोड को हटाएं हर कंपोज़ेबल, NavHost कंपोज़ेबल से अलग होना चाहिए.

इसका मतलब यह है कि आपको navController को सीधे किसी composable को शामिल करने और इसके बजाय नेविगेशन कॉलबैक को पैरामीटर के तौर पर पास करते हैं. इससे आपको आपकी सभी कंपोज़ेबल को अलग-अलग टेस्ट किया जाना चाहिए, क्योंकि उनके लिए टेस्ट में navController का इंस्टेंस.

composable lambda से मिलने वाले इनडायरेक्ट लेवल की मदद से, आपको अपने नेविगेशन कोड को कंपोज़ेबल से अलग करें. यह दो स्थितियों में काम करता है दिशा-निर्देश:

  • अपने कंपोज़ेबल में सिर्फ़ पार्स किए गए आर्ग्युमेंट पास करें
  • उस Lambdas को पास करें जिसे नेविगेट करने के लिए, कंपोज़ेबल से ट्रिगर किया जाना चाहिए, ध्यान रखें, NavController.

उदाहरण के लिए, Profile कंपोज़ेबल जो इनपुट के तौर पर userId को लेता है और इसकी अनुमति देता है किसी दोस्त के प्रोफ़ाइल पेज पर जाने के लिए उपयोगकर्ताओं के हस्ताक्षर हो सकते हैं:

@Composable
fun Profile(
    userId: String,
    navigateToFriendProfile: (friendUserId: String) -> Unit
) {
 
}

इस तरह, Profile कंपोज़ेबल, नेविगेशन से अलग काम करता है, इससे उसे स्वतंत्र तौर पर टेस्ट किया जा सकेगा. composable लैम्डा यह कर पाता नेविगेशन के बीच के अंतर को कम करने के लिए, सबसे कम लॉजिक को एनकैप्सुलेट करें एपीआई और आपके कंपोज़ेबल:

composable(
    "profile?userId={userId}",
    arguments = listOf(navArgument("userId") { defaultValue = "user1234" })
) { backStackEntry ->
    Profile(backStackEntry.arguments?.getString("userId")) { friendUserId ->
        navController.navigate("profile?userId=$friendUserId")
    }
}

हमारा सुझाव है कि आप ऐसे टेस्ट लिखें जो आपके ऐप्लिकेशन नेविगेशन की ज़रूरी शर्तों को पूरा करते हों NavHost की जांच करके, नेविगेशन से जुड़ी कार्रवाइयां पूरी की गईं आपके कंपोज़ेबल और अलग-अलग स्क्रीन कंपोज़ेबल का ऐक्सेस मिल जाता है.

NavHost की जांच की जा रही है

अपने NavHost को टेस्ट करने के लिए , यहां दिया गया नेविगेशन टेस्टिंग टूल जोड़ें निर्भरता:

dependencies {
// ...
  androidTestImplementation "androidx.navigation:navigation-testing:$navigationVersion"
  // ...
}

NavHost टेस्ट सब्जेक्ट सेट अप किया जा सकता है और navController इंस्टेंस का इंस्टेंस. इसके लिए, नेविगेशन टेस्टिंग आर्टफ़ैक्ट TestNavHostController उपलब्ध कराता है. ऐसा यूज़र इंटरफ़ेस (यूआई) टेस्ट जो आपके ऐप्लिकेशन के शुरुआती डेस्टिनेशन की पुष्टि करता है और NavHost ऐसा दिखेगा:

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()
    lateinit var navController: TestNavHostController

    @Before
    fun setupAppNavHost() {
        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            AppNavHost(navController = navController)
        }
    }

    // Unit test
    @Test
    fun appNavHost_verifyStartDestination() {
        composeTestRule
            .onNodeWithContentDescription("Start Screen")
            .assertIsDisplayed()
    }
}

नेविगेशन से जुड़ी कार्रवाइयों की जांच करना

नेविगेशन लागू करने की प्रक्रिया की जांच कई तरीकों से की जा सकती है. इसके लिए, यूज़र इंटरफ़ेस (यूआई) एलिमेंट पर क्लिक करता है और फिर दिखाए गए डेस्टिनेशन की पुष्टि करता है या फिर अनुमानित रूट की तुलना मौजूदा रूट से करके.

अगर आपको अपने कॉन्क्रीट ऐप्लिकेशन के सही तरह से लागू होने की जांच करनी है, तो यूज़र इंटरफ़ेस (यूआई) को प्राथमिकता दी जाती है. इसे अलग-अलग कंपोज़ेबल के साथ टेस्ट करने का तरीका जानने के लिए फ़ंक्शन को अलग से देखना न भूलें, तो कोडलैब के लिए Jetpack Compose में टेस्टिंग करना.

navController का इस्तेमाल करके, यहां बताए गए दावों की जांच की जा सकती है इसका उपयोग करते हुए वर्तमान स्ट्रिंग रूट की अपेक्षित रूट से तुलना करना navController का currentBackStackEntry:

@Test
fun appNavHost_clickAllProfiles_navigateToProfiles() {
    composeTestRule.onNodeWithContentDescription("All Profiles")
        .performScrollTo()
        .performClick()

    val route = navController.currentBackStackEntry?.destination?.route
    assertEquals(route, "profiles")
}

Compose की सुविधा की जांच से जुड़ी बुनियादी बातों के बारे में ज़्यादा जानने के लिए, यहां जाएं Compose के लेआउट की जांच करना और Jetpack Compose में टेस्टिंग करना कोडलैब (कोड बनाना सीखना) शुरू करना. नेविगेशन कोड की बेहतर टेस्टिंग के बारे में ज़्यादा जानने के लिए, इस लिंक पर जाएं टेस्ट नेविगेशन गाइड.

ज़्यादा जानें

जेटपैक नेविगेशन के बारे में ज़्यादा जानने के लिए, नेविगेशन का इस्तेमाल शुरू करें कॉम्पोनेंट पर जाएं या Jetpack का इस्तेमाल करें नेविगेशन कोडलैब लिखें.

अपने ऐप्लिकेशन के नेविगेशन को डिज़ाइन करने का तरीका जानने के लिए, ताकि वह दूसरी स्क्रीन के हिसाब से एडजस्ट हो जाए साइज़, ओरिएंटेशन, और नाप या आकार से जुड़ी जानकारी देखें. रिस्पॉन्सिव यूज़र इंटरफ़ेस (यूआई) के लिए नेविगेशन.

नेविगेशन में, ज़्यादा बेहतर तरीके से कंपोज़ की सुविधा इस्तेमाल करने के बारे में जानने के लिए, मॉड्यूलराइज़ किया गया ऐप्लिकेशन, जिसमें नेस्ट किए गए ग्राफ़ और बॉटम नेविगेशन बार जैसे कॉन्सेप्ट शामिल हैं इंटिग्रेशन के लिए, GitHub पर Now Android ऐप्लिकेशन पर एक नज़र डालें.

सैंपल