1 /*
2  * Copyright 2022 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 @file:Suppress("UnstableApiUsage")
18 
19 package androidx.navigation.runtime.lint
20 
21 import com.android.SdkConstants.TAG_ACTIVITY
22 import com.android.SdkConstants.TAG_DEEP_LINK
23 import com.android.resources.ResourceFolderType
24 import com.android.tools.lint.detector.api.Category
25 import com.android.tools.lint.detector.api.Implementation
26 import com.android.tools.lint.detector.api.Incident
27 import com.android.tools.lint.detector.api.Issue
28 import com.android.tools.lint.detector.api.ResourceXmlDetector
29 import com.android.tools.lint.detector.api.Scope
30 import com.android.tools.lint.detector.api.Severity
31 import com.android.tools.lint.detector.api.XmlContext
32 import java.util.Collections
33 import org.w3c.dom.Element
34 
35 /** Lint check for detecting use of <deeplink> inside of <activity>. */
36 class DeepLinkInActivityDestinationDetector : ResourceXmlDetector() {
37 
appliesTonull38     override fun appliesTo(folderType: ResourceFolderType): Boolean {
39         return folderType == ResourceFolderType.NAVIGATION
40     }
41 
getApplicableElementsnull42     override fun getApplicableElements(): Collection<String>? = Collections.singleton(TAG_DEEP_LINK)
43 
44     override fun visitElement(context: XmlContext, element: Element) {
45         if (element.parentNode?.nodeName == TAG_ACTIVITY) {
46             val incident =
47                 Incident(context)
48                     .issue(DeepLinkInActivityDestination)
49                     .location(context.getLocation(element))
50                     .message(
51                         "Do not attach a <deeplink> to an <activity> destination. " +
52                             "Attach the deeplink directly to the second activity or the start " +
53                             "destination of a nav host in the second activity instead."
54                     )
55             context.report(incident)
56         }
57     }
58 
59     companion object {
60         val DeepLinkInActivityDestination =
61             Issue.create(
62                 id = "DeepLinkInActivityDestination",
63                 briefDescription =
64                     "A <deeplink> should not be attached to an <activity> destination",
65                 explanation =
66                     """Attaching a <deeplink> to an <activity> destination will never give \
67                 the right behavior when using an implicit deep link on another app's task \
68                 (where the system back should immediately take the user back to the app that \
69                 triggered the deep link). Instead, attach the deep link directly to \
70                 the second activity (either by manually writing the appropriate <intent-filter> \
71                 or by adding the <deeplink> to the start destination of a nav host in that second \
72                 activity).""",
73                 category = Category.CORRECTNESS,
74                 severity = Severity.WARNING,
75                 implementation =
76                     Implementation(
77                         DeepLinkInActivityDestinationDetector::class.java,
78                         Scope.RESOURCE_FILE_SCOPE
79                     ),
80                 androidSpecific = true
81             )
82     }
83 }
84