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