1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.navigation.dynamicfeatures
18 
19 import android.content.Context
20 import android.util.AttributeSet
21 import androidx.core.content.withStyledAttributes
22 import androidx.navigation.ActivityNavigator
23 import androidx.navigation.NavBackStackEntry
24 import androidx.navigation.NavOptions
25 import androidx.navigation.Navigator
26 import androidx.navigation.NavigatorProvider
27 
28 /** Dynamic feature navigator for Activity destinations. */
29 @Navigator.Name("activity")
30 public class DynamicActivityNavigator(
31     context: Context,
32     private val installManager: DynamicInstallManager
33 ) : ActivityNavigator(context) {
34 
35     internal val packageName: String = context.packageName
36 
navigatenull37     override fun navigate(
38         entries: List<NavBackStackEntry>,
39         navOptions: NavOptions?,
40         navigatorExtras: Navigator.Extras?
41     ) {
42         for (entry in entries) {
43             navigate(entry, navOptions, navigatorExtras)
44         }
45     }
46 
navigatenull47     private fun navigate(
48         entry: NavBackStackEntry,
49         navOptions: NavOptions?,
50         navigatorExtras: Navigator.Extras?
51     ) {
52         val destination = entry.destination
53         val extras = navigatorExtras as? DynamicExtras
54         if (destination is Destination) {
55             val moduleName = destination.moduleName
56             if (moduleName != null && installManager.needsInstall(moduleName)) {
57                 installManager.performInstall(entry, extras, moduleName)
58                 return
59             }
60         }
61         super.navigate(
62             listOf(entry),
63             navOptions,
64             if (extras != null) extras.destinationExtras else navigatorExtras
65         )
66     }
67 
createDestinationnull68     override fun createDestination(): Destination = Destination(this)
69 
70     /** Destination for [DynamicActivityNavigator]. */
71     public class Destination : ActivityNavigator.Destination {
72         /**
73          * The module name of this [Destination]'s dynamic feature module. This has to be the same
74          * as defined in the dynamic feature module's AndroidManifest.xml file.
75          */
76         public var moduleName: String? = null
77 
78         /**
79          * Create a new [Destination] with a [NavigatorProvider].
80          *
81          * @see ActivityNavigator.Destination
82          */
83         public constructor(navigatorProvider: NavigatorProvider) : super(navigatorProvider)
84 
85         /**
86          * Create a new [Destination] with an [ActivityNavigator.Destination].
87          *
88          * @param activityNavigator The Navigator to use for this [Destination].
89          */
90         public constructor(
91             activityNavigator: Navigator<out ActivityNavigator.Destination>
92         ) : super(activityNavigator)
93 
94         override fun onInflate(context: Context, attrs: AttributeSet) {
95             super.onInflate(context, attrs)
96             context.withStyledAttributes(attrs, R.styleable.DynamicActivityNavigator) {
97                 moduleName = getString(R.styleable.DynamicActivityNavigator_moduleName)
98             }
99         }
100 
101         override fun equals(other: Any?): Boolean {
102             if (this === other) return true
103             if (other == null || other !is Destination) return false
104             return super.equals(other) && moduleName == other.moduleName
105         }
106 
107         override fun hashCode(): Int {
108             var result = super.hashCode()
109             result = 31 * result + moduleName.hashCode()
110             return result
111         }
112     }
113 }
114