1 /*
2  * Copyright 2023 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.text
18 
19 /**
20  * An annotation that represents a clickable part of the text.
21  *
22  * @sample androidx.compose.ui.text.samples.AnnotatedStringWithLinkSample
23  */
24 abstract class LinkAnnotation private constructor() : AnnotatedString.Annotation {
25     /**
26      * Interaction listener triggered when user interacts with this link.
27      *
28      * @sample androidx.compose.ui.text.samples.AnnotatedStringWithListenerSample
29      */
30     abstract val linkInteractionListener: LinkInteractionListener?
31     /**
32      * Style configuration for this link in different states.
33      *
34      * @sample androidx.compose.ui.text.samples.AnnotatedStringWithHoveredLinkStylingSample
35      */
36     abstract val styles: TextLinkStyles?
37 
38     /**
39      * An annotation that contains a [url] string. When clicking on the text to which this
40      * annotation is attached, the app will try to open the url using
41      * [androidx.compose.ui.platform.UriHandler]. However, if [linkInteractionListener] is provided,
42      * its [LinkInteractionListener.onClick] method will be called instead and so you need to then
43      * handle opening url manually (for example by calling
44      * [androidx.compose.ui.platform.UriHandler]).
45      *
46      * @see LinkAnnotation
47      */
48     class Url(
49         val url: String,
50         override val styles: TextLinkStyles? = null,
51         override val linkInteractionListener: LinkInteractionListener? = null
52     ) : LinkAnnotation() {
53 
54         /** Returns a copy of this [Url], optionally overriding some of the values. */
55         @Suppress("ExecutorRegistration")
copynull56         fun copy(
57             url: String = this.url,
58             styles: TextLinkStyles? = this.styles,
59             linkInteractionListener: LinkInteractionListener? = this.linkInteractionListener
60         ) = Url(url, styles, linkInteractionListener)
61 
62         override fun equals(other: Any?): Boolean {
63             if (this === other) return true
64             if (other !is Url) return false
65 
66             if (url != other.url) return false
67             if (styles != other.styles) return false
68             if (linkInteractionListener != other.linkInteractionListener) return false
69 
70             return true
71         }
72 
hashCodenull73         override fun hashCode(): Int {
74             var result = url.hashCode()
75             result = 31 * result + (styles?.hashCode() ?: 0)
76             result = 31 * result + (linkInteractionListener?.hashCode() ?: 0)
77             return result
78         }
79 
toStringnull80         override fun toString(): String {
81             return "LinkAnnotation.Url(url=$url)"
82         }
83     }
84 
85     /**
86      * An annotation that contains a clickable marked with [tag]. When clicking on the text to which
87      * this annotation is attached, the app will trigger a [linkInteractionListener] listener.
88      *
89      * @see LinkAnnotation
90      */
91     class Clickable(
92         val tag: String,
93         override val styles: TextLinkStyles? = null,
94         // nullable for the save/restore purposes
95         override val linkInteractionListener: LinkInteractionListener?
96     ) : LinkAnnotation() {
97 
98         /** Returns a copy of this [Clickable], optionally overriding some of the values. */
99         @Suppress("ExecutorRegistration")
copynull100         fun copy(
101             tag: String = this.tag,
102             styles: TextLinkStyles? = this.styles,
103             linkInteractionListener: LinkInteractionListener? = this.linkInteractionListener
104         ) = Clickable(tag, styles, linkInteractionListener)
105 
106         override fun equals(other: Any?): Boolean {
107             if (this === other) return true
108             if (other !is Clickable) return false
109 
110             if (tag != other.tag) return false
111             if (styles != other.styles) return false
112             if (linkInteractionListener != other.linkInteractionListener) return false
113 
114             return true
115         }
116 
hashCodenull117         override fun hashCode(): Int {
118             var result = tag.hashCode()
119             result = 31 * result + (styles?.hashCode() ?: 0)
120             result = 31 * result + (linkInteractionListener?.hashCode() ?: 0)
121             return result
122         }
123 
toStringnull124         override fun toString(): String {
125             return "LinkAnnotation.Clickable(tag=$tag)"
126         }
127     }
128 }
129