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.compose.ui.autofill
18 
19 import androidx.compose.ui.ComposeUiFlags
20 import androidx.compose.ui.ExperimentalComposeUiApi
21 import androidx.compose.ui.geometry.Rect
22 import androidx.compose.ui.platform.makeSynchronizedObject
23 import androidx.compose.ui.platform.synchronized
24 import androidx.compose.ui.semantics.generateSemanticsId
25 
26 /**
27  * Autofill API.
28  *
29  * This interface is available to all composables via a CompositionLocal. The composable can then
30  * request or cancel autofill as required. For instance, the TextField can call
31  * [requestAutofillForNode] when it gains focus, and [cancelAutofillForNode] when it loses focus.
32  */
33 @Deprecated(
34     """
35         You no longer have to call these apis when focus changes. They will be called
36         automatically when you Use the new semantics based APIs for autofill. Use the
37         androidx.compose.ui.autofill.ContentType and androidx.compose.ui.autofill.ContentDataType
38         semantics properties instead.
39         """
40 )
41 interface Autofill {
42 
43     /**
44      * Request autofill for the specified node.
45      *
46      * @param autofillNode The node that needs to be auto-filled.
47      *
48      * This function is usually called when an autofill-able component gains focus.
49      */
requestAutofillForNodenull50     fun requestAutofillForNode(autofillNode: @Suppress("Deprecation") AutofillNode)
51 
52     /**
53      * Cancel a previously supplied autofill request.
54      *
55      * @param autofillNode The node that needs to be auto-filled.
56      *
57      * This function is usually called when an autofill-able component loses focus.
58      */
59     fun cancelAutofillForNode(autofillNode: @Suppress("Deprecation") AutofillNode)
60 }
61 
62 /**
63  * Every autofill-able composable will have an [AutofillNode]. (An autofill node will be created for
64  * every semantics node that adds autofill properties). This node is used to request/cancel
65  * autofill, and it holds the [onFill] lambda which is called by the autofill framework.
66  *
67  * @property autofillTypes A list of autofill types for this node. These types are conveyed to the
68  *   autofill framework and it is used to call [onFill] with the appropriate value. If you don't set
69  *   this property, the autofill framework will use heuristics to guess the type. This property is a
70  *   list because some fields can have multiple types. For instance, userid in a login form can
71  *   either be a username or an email address. TODO(b/138731416): Check with the autofill service
72  *   team if the order matters, and how duplicate types are handled.
73  * @property boundingBox The screen coordinates of the composable being auto-filled. This data is
74  *   used by the autofill framework to decide where to show the autofill popup.
75  * @property onFill The callback that is called by the autofill framework to perform autofill.
76  * @property id A virtual id that is automatically generated for each node.
77  */
78 @Deprecated(
79     """
80         Use the new semantics-based Autofill APIs androidx.compose.ui.autofill.ContentType and
81         androidx.compose.ui.autofill.ContentDataType instead.
82         """
83 )
84 class AutofillNode(
85     val autofillTypes: List<@Suppress("Deprecation") AutofillType> = listOf(),
86     var boundingBox: Rect? = null,
87     val onFill: ((String) -> Unit)?
88 ) {
89     internal companion object {
90         /*@GuardedBy("this")*/
91         private var previousId = 0
92 
93         private val lock = makeSynchronizedObject(this)
94 
95         private fun generateId() = synchronized(lock) { ++previousId }
96     }
97 
98     val id: Int =
99         @OptIn(ExperimentalComposeUiApi::class)
100         if (ComposeUiFlags.isSemanticAutofillEnabled) generateSemanticsId() else generateId()
101 
102     override fun equals(other: Any?): Boolean {
103         if (this === other) return true
104         if (other !is @Suppress("Deprecation") AutofillNode) return false
105 
106         if (autofillTypes != other.autofillTypes) return false
107         if (boundingBox != other.boundingBox) return false
108         if (onFill !== other.onFill) return false
109 
110         return true
111     }
112 
113     override fun hashCode(): Int {
114         var result = autofillTypes.hashCode()
115         result = 31 * result + (boundingBox?.hashCode() ?: 0)
116         result = 31 * result + (onFill?.hashCode() ?: 0)
117         return result
118     }
119 }
120