1 /*
<lambda>null2  * Copyright (C) 2018 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 package com.android.example.text.styling.roundedbg
17 
18 import android.graphics.Canvas
19 import android.graphics.drawable.Drawable
20 import android.text.Annotation
21 import android.text.Layout
22 import android.text.Spanned
23 
24 /**
25  * Helper class to draw multi-line rounded background to certain parts of a text. The start/end
26  * positions of the backgrounds are annotated with [android.text.Annotation] class. Each annotation
27  * should have the annotation key set to **rounded**.
28  *
29  * i.e.:
30  * ```
31  *    <!--without the quotes at the begining and end Android strips the whitespace and also starts
32  *        the annotation at the wrong position-->
33  *    <string name="ltr">"this is <annotation key="rounded">a regular</annotation> paragraph."</string>
34  * ```
35  *
36  * **Note:** BiDi text is not supported.
37  *
38  * @param horizontalPadding the padding to be applied to left & right of the background
39  * @param verticalPadding the padding to be applied to top & bottom of the background
40  * @param drawable the drawable used to draw the background
41  * @param drawableLeft the drawable used to draw left edge of the background
42  * @param drawableMid the drawable used to draw for whole line
43  * @param drawableRight the drawable used to draw right edge of the background
44  */
45 class TextRoundedBgHelper(
46     val horizontalPadding: Int,
47     verticalPadding: Int,
48     drawable: Drawable,
49     drawableLeft: Drawable,
50     drawableMid: Drawable,
51     drawableRight: Drawable
52 ) {
53 
54     private val singleLineRenderer: TextRoundedBgRenderer by lazy {
55         SingleLineRenderer(
56             horizontalPadding = horizontalPadding,
57             verticalPadding = verticalPadding,
58             drawable = drawable
59         )
60     }
61 
62     private val multiLineRenderer: TextRoundedBgRenderer by lazy {
63         MultiLineRenderer(
64             horizontalPadding = horizontalPadding,
65             verticalPadding = verticalPadding,
66             drawableLeft = drawableLeft,
67             drawableMid = drawableMid,
68             drawableRight = drawableRight
69         )
70     }
71 
72     /**
73      * Call this function during onDraw of another widget such as TextView.
74      *
75      * @param canvas Canvas to draw onto
76      * @param text
77      * @param layout Layout that contains the text
78      */
79     fun draw(canvas: Canvas, text: Spanned, layout: Layout) {
80         // ideally the calculations here should be cached since they are not cheap. However, proper
81         // invalidation of the cache is required whenever anything related to text has changed.
82         val spans = text.getSpans(0, text.length, Annotation::class.java)
83         spans.forEach { span ->
84             if (span.value.equals("rounded")) {
85                 val spanStart = text.getSpanStart(span)
86                 val spanEnd = text.getSpanEnd(span)
87                 val startLine = layout.getLineForOffset(spanStart)
88                 val endLine = layout.getLineForOffset(spanEnd)
89 
90                 // start can be on the left or on the right depending on the language direction.
91                 val startOffset = (layout.getPrimaryHorizontal(spanStart)
92                     + -1 * layout.getParagraphDirection(startLine) * horizontalPadding).toInt()
93                 // end can be on the left or on the right depending on the language direction.
94                 val endOffset = (layout.getPrimaryHorizontal(spanEnd)
95                     + layout.getParagraphDirection(endLine) * horizontalPadding).toInt()
96 
97                 val renderer = if (startLine == endLine) singleLineRenderer else multiLineRenderer
98                 renderer.draw(canvas, layout, startLine, endLine, startOffset, endOffset)
99             }
100         }
101     }
102 }