• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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  *      https://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 com.google.accompanist.swiperefresh
18 
19 import androidx.compose.runtime.getValue
20 import androidx.compose.runtime.mutableStateOf
21 import androidx.compose.runtime.setValue
22 import androidx.compose.ui.geometry.Offset
23 import androidx.compose.ui.geometry.Rect
24 import androidx.compose.ui.geometry.Size
25 import androidx.compose.ui.geometry.center
26 import androidx.compose.ui.graphics.Color
27 import androidx.compose.ui.graphics.Path
28 import androidx.compose.ui.graphics.PathFillType
29 import androidx.compose.ui.graphics.StrokeCap
30 import androidx.compose.ui.graphics.drawscope.DrawScope
31 import androidx.compose.ui.graphics.drawscope.Stroke
32 import androidx.compose.ui.graphics.drawscope.rotate
33 import androidx.compose.ui.graphics.painter.Painter
34 import androidx.compose.ui.unit.dp
35 import kotlin.math.min
36 
37 /**
38  * A private class to do all the drawing of SwipeRefreshIndicator, which includes progress spinner
39  * and the arrow. This class is to separate drawing from animation.
40  * Adapted from CircularProgressDrawable.
41  */
42 internal class CircularProgressPainter : Painter() {
43     var color by mutableStateOf(Color.Unspecified)
44     var alpha by mutableStateOf(1f)
45     var arcRadius by mutableStateOf(0.dp)
46     var strokeWidth by mutableStateOf(5.dp)
47     var arrowEnabled by mutableStateOf(false)
48     var arrowWidth by mutableStateOf(0.dp)
49     var arrowHeight by mutableStateOf(0.dp)
50     var arrowScale by mutableStateOf(1f)
51 
<lambda>null52     private val arrow: Path by lazy {
53         Path().apply { fillType = PathFillType.EvenOdd }
54     }
55 
56     var startTrim by mutableStateOf(0f)
57     var endTrim by mutableStateOf(0f)
58     var rotation by mutableStateOf(0f)
59 
60     override val intrinsicSize: Size
61         get() = Size.Unspecified
62 
applyAlphanull63     override fun applyAlpha(alpha: Float): Boolean {
64         this.alpha = alpha
65         return true
66     }
67 
onDrawnull68     override fun DrawScope.onDraw() {
69         rotate(degrees = rotation) {
70             val arcRadius = arcRadius.toPx() + strokeWidth.toPx() / 2f
71             val arcBounds = Rect(
72                 size.center.x - arcRadius,
73                 size.center.y - arcRadius,
74                 size.center.x + arcRadius,
75                 size.center.y + arcRadius
76             )
77             val startAngle = (startTrim + rotation) * 360
78             val endAngle = (endTrim + rotation) * 360
79             val sweepAngle = endAngle - startAngle
80             drawArc(
81                 color = color,
82                 alpha = alpha,
83                 startAngle = startAngle,
84                 sweepAngle = sweepAngle,
85                 useCenter = false,
86                 topLeft = arcBounds.topLeft,
87                 size = arcBounds.size,
88                 style = Stroke(
89                     width = strokeWidth.toPx(),
90                     cap = StrokeCap.Square
91                 )
92             )
93             if (arrowEnabled) {
94                 drawArrow(startAngle, sweepAngle, arcBounds)
95             }
96         }
97     }
98 
DrawScopenull99     private fun DrawScope.drawArrow(startAngle: Float, sweepAngle: Float, bounds: Rect) {
100         arrow.reset()
101         arrow.moveTo(0f, 0f)
102         arrow.lineTo(
103             x = arrowWidth.toPx() * arrowScale,
104             y = 0f
105         )
106         arrow.lineTo(
107             x = arrowWidth.toPx() * arrowScale / 2,
108             y = arrowHeight.toPx() * arrowScale
109         )
110         val radius = min(bounds.width, bounds.height) / 2f
111         val inset = arrowWidth.toPx() * arrowScale / 2f
112         arrow.translate(
113             Offset(
114                 x = radius + bounds.center.x - inset,
115                 y = bounds.center.y + strokeWidth.toPx() / 2f
116             )
117         )
118         arrow.close()
119         rotate(degrees = startAngle + sweepAngle) {
120             drawPath(
121                 path = arrow,
122                 color = color,
123                 alpha = alpha
124             )
125         }
126     }
127 }
128