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  *      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.android.horologist.compose.material
18 
19 import androidx.annotation.DrawableRes
20 import androidx.compose.foundation.layout.size
21 import androidx.compose.runtime.Composable
22 import androidx.compose.ui.Alignment
23 import androidx.compose.ui.Modifier
24 import androidx.compose.ui.graphics.vector.ImageVector
25 import androidx.compose.ui.unit.Dp
26 import androidx.wear.compose.material.Button
27 import androidx.wear.compose.material.ButtonColors
28 import androidx.wear.compose.material.ButtonDefaults
29 import androidx.wear.compose.material.ButtonDefaults.DefaultButtonSize
30 import androidx.wear.compose.material.ButtonDefaults.DefaultIconSize
31 import androidx.wear.compose.material.ButtonDefaults.LargeButtonSize
32 import androidx.wear.compose.material.ButtonDefaults.LargeIconSize
33 import androidx.wear.compose.material.ButtonDefaults.SmallButtonSize
34 import androidx.wear.compose.material.ButtonDefaults.SmallIconSize
35 import com.google.android.horologist.annotations.ExperimentalHorologistApi
36 
37 /**
38  * This component is an alternative to [Button], providing the following:
39  * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
40  * by the Wear guidelines;
41  */
42 @ExperimentalHorologistApi
43 @Composable
Buttonnull44 public fun Button(
45     imageVector: ImageVector,
46     contentDescription: String,
47     onClick: () -> Unit,
48     modifier: Modifier = Modifier,
49     colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
50     buttonSize: ButtonSize = ButtonSize.Default,
51     iconRtlMode: IconRtlMode = IconRtlMode.Default,
52     enabled: Boolean = true,
53 ) {
54     Button(
55         icon = imageVector,
56         contentDescription = contentDescription,
57         onClick = onClick,
58         modifier = modifier,
59         colors = colors,
60         buttonSize = buttonSize,
61         iconRtlMode = iconRtlMode,
62         enabled = enabled,
63     )
64 }
65 
66 /**
67  * This component is an alternative to [Button], providing the following:
68  * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
69  * by the Wear guidelines;
70  */
71 @ExperimentalHorologistApi
72 @Composable
Buttonnull73 public fun Button(
74     @DrawableRes id: Int,
75     contentDescription: String,
76     onClick: () -> Unit,
77     modifier: Modifier = Modifier,
78     colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
79     buttonSize: ButtonSize = ButtonSize.Default,
80     iconRtlMode: IconRtlMode = IconRtlMode.Default,
81     enabled: Boolean = true,
82 ) {
83     Button(
84         icon = id,
85         contentDescription = contentDescription,
86         onClick = onClick,
87         modifier = modifier,
88         colors = colors,
89         buttonSize = buttonSize,
90         iconRtlMode = iconRtlMode,
91         enabled = enabled,
92     )
93 }
94 
95 @OptIn(ExperimentalHorologistApi::class)
96 @Composable
Buttonnull97 internal fun Button(
98     icon: Any,
99     contentDescription: String,
100     onClick: () -> Unit,
101     modifier: Modifier = Modifier,
102     colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
103     buttonSize: ButtonSize = ButtonSize.Default,
104     iconRtlMode: IconRtlMode = IconRtlMode.Default,
105     enabled: Boolean = true,
106 ) {
107     Button(
108         onClick = onClick,
109         modifier = modifier.size(buttonSize.tapTargetSize),
110         enabled = enabled,
111         colors = colors,
112     ) {
113         val iconModifier = Modifier
114             .size(buttonSize.iconSize)
115             .align(Alignment.Center)
116 
117         Icon(
118             icon = icon,
119             contentDescription = contentDescription,
120             modifier = iconModifier,
121             rtlMode = iconRtlMode,
122         )
123     }
124 }
125 
126 @ExperimentalHorologistApi
127 public sealed class ButtonSize(
128     public val iconSize: Dp,
129     public val tapTargetSize: Dp,
130 ) {
131     public object Default :
132         ButtonSize(iconSize = DefaultIconSize, tapTargetSize = DefaultButtonSize)
133 
134     public object Large : ButtonSize(iconSize = LargeIconSize, tapTargetSize = LargeButtonSize)
135     public object Small : ButtonSize(iconSize = SmallIconSize, tapTargetSize = SmallButtonSize)
136 
137     /**
138      * Custom sizes should follow the [accessibility principles and guidance for touch targets](https://developer.android.com/training/wearables/accessibility#set-minimum).
139      */
140     public data class Custom(val customIconSize: Dp, val customTapTargetSize: Dp) :
141         ButtonSize(iconSize = customIconSize, tapTargetSize = customTapTargetSize)
142 }
143 
144