blob: 2e438307d55ee048c3b8590151a966c8bc425b9b [file] [log] [blame]
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.glance.appwidget
import androidx.annotation.RestrictTo
import androidx.compose.runtime.Composable
import androidx.glance.Emittable
import androidx.glance.EmittableCheckable
import androidx.glance.ExperimentalGlanceApi
import androidx.glance.GlanceModifier
import androidx.glance.GlanceNode
import androidx.glance.GlanceTheme
import androidx.glance.action.Action
import androidx.glance.action.ActionModifier
import androidx.glance.action.action
import androidx.glance.appwidget.action.CompoundButtonAction
import androidx.glance.appwidget.unit.CheckableColorProvider
import androidx.glance.appwidget.unit.CheckedUncheckedColorProvider.Companion.createCheckableColorProvider
import androidx.glance.appwidget.unit.ResourceCheckableColorProvider
import androidx.glance.color.DynamicThemeColorProviders
import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider
/** Set of colors to apply to a Switch depending on the checked state. */
sealed class SwitchColors {
internal abstract val thumb: CheckableColorProvider
internal abstract val track: CheckableColorProvider
}
internal data class SwitchColorsImpl(
override val thumb: CheckableColorProvider,
override val track: CheckableColorProvider
) : SwitchColors()
/**
* Adds a switch view to the glance view.
*
* @param checked whether the switch is checked
* @param onCheckedChange the action to be run when the switch is clicked. The current value of
* checked is provided to this action in its ActionParameters, and can be retrieved using the
* [ToggleableStateKey]. If this action launches an activity, the current value of checked will be
* passed as an intent extra with the name [RemoteViews.EXTRA_CHECKED].
* In order to allow the Launcher to provide this extra on Android version S and later, we use a
* mutable PendingIntent ([android.app.PendingIntent.FLAG_MUTABLE]) when this action is not a
* lambda. Before S, and for lambda actions, this will be an immutable PendingIntent.
* @param modifier the modifier to apply to the switch
* @param text the text to display to the end of the switch
* @param style the style to apply to [text]
* @param colors the tint colors for the thumb and track of the switch
* @param maxLines An optional maximum number of lines for the text to span, wrapping if
* necessary. If the text exceeds the given number of lines, it will be truncated.
*/
@Composable
fun Switch(
checked: Boolean,
onCheckedChange: Action?,
modifier: GlanceModifier = GlanceModifier,
text: String = "",
style: TextStyle? = null,
colors: SwitchColors = SwitchDefaults.colors(),
maxLines: Int = Int.MAX_VALUE,
) = SwitchElement(checked, onCheckedChange, modifier, text, style, colors, maxLines)
/**
* Adds a switch view to the glance view.
*
* @param checked whether the switch is checked
* @param onCheckedChange the action to be run when the switch is clicked
* @param modifier the modifier to apply to the switch
* @param text the text to display to the end of the switch
* @param style the style to apply to [text]
* @param colors the tint colors for the thumb and track of the switch
* @param maxLines An optional maximum number of lines for the text to span, wrapping if
* necessary. If the text exceeds the given number of lines, it will be truncated.
*/
@Composable
fun Switch(
checked: Boolean,
onCheckedChange: () -> Unit,
modifier: GlanceModifier = GlanceModifier,
text: String = "",
style: TextStyle? = null,
colors: SwitchColors = SwitchDefaults.colors(),
maxLines: Int = Int.MAX_VALUE,
) = SwitchElement(
checked,
action(block = onCheckedChange),
modifier,
text,
style,
colors,
maxLines
)
/**
* Adds a switch view to the glance view.
*
* @param checked whether the switch is checked
* @param onCheckedChange the action to be run when the switch is clicked
* @param modifier the modifier to apply to the switch
* @param text the text to display to the end of the switch
* @param style the style to apply to [text]
* @param colors the tint colors for the thumb and track of the switch
* @param maxLines An optional maximum number of lines for the text to span, wrapping if
* necessary. If the text exceeds the given number of lines, it will be truncated.
* @param key A stable and unique key that identifies the action for this switch. This ensures
* that the correct action is triggered, especially in cases of items that change order. If not
* provided we use the key that is automatically generated by the Compose runtime, which is unique
* for every exact code location in the composition tree.
*/
@ExperimentalGlanceApi
@Composable
fun Switch(
checked: Boolean,
onCheckedChange: () -> Unit,
modifier: GlanceModifier = GlanceModifier,
text: String = "",
style: TextStyle? = null,
colors: SwitchColors = SwitchDefaults.colors(),
maxLines: Int = Int.MAX_VALUE,
key: String? = null,
) = SwitchElement(
checked,
action(key, onCheckedChange),
modifier,
text,
style,
colors,
maxLines
)
/**
* Contains the default values used by [Switch].
*/
object SwitchDefaults {
/**
* SwitchColors to tint the thumb and track of the [Switch] according to the checked state.
*
* @param checkedThumbColor the tint to apply to the thumb of the switch when it is checked
* @param uncheckedThumbColor the tint to apply to the thumb of the switch when it is not
* checked
* @param checkedTrackColor the tint to apply to the track of the switch when it is checked
* @param uncheckedTrackColor the tint to apply to the track of the switch when it is not
* checked
*/
@Composable
fun colors(
checkedThumbColor: ColorProvider,
uncheckedThumbColor: ColorProvider,
checkedTrackColor: ColorProvider,
uncheckedTrackColor: ColorProvider,
): SwitchColors = switchColors(
checkedThumbColor = checkedThumbColor,
uncheckedThumbColor = uncheckedThumbColor,
checkedTrackColor = checkedTrackColor,
uncheckedTrackColor = uncheckedTrackColor
)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun switchColors(
checkedThumbColor: ColorProvider,
uncheckedThumbColor: ColorProvider,
checkedTrackColor: ColorProvider,
uncheckedTrackColor: ColorProvider,
): SwitchColors {
return SwitchColorsImpl(
thumb = createCheckableColorProvider(
source = "SwitchColors",
checked = checkedThumbColor,
unchecked = uncheckedThumbColor,
),
track = createCheckableColorProvider(
source = "SwitchColors",
checked = checkedTrackColor,
unchecked = uncheckedTrackColor,
)
)
}
/**
*
* SwitchColors to tint the thumb and track of the [Switch] according to the checked state.
* @return a default set of [SwitchColors].
*/
@Composable
fun colors(): SwitchColors {
return if (GlanceTheme.colors == DynamicThemeColorProviders) {
SwitchColorsImpl(
thumb = ResourceCheckableColorProvider(R.color.glance_default_switch_thumb),
track = ResourceCheckableColorProvider(R.color.glance_default_switch_track)
)
} else {
colors(
checkedThumbColor = GlanceTheme.colors.onPrimary,
uncheckedThumbColor = GlanceTheme.colors.outline,
checkedTrackColor = GlanceTheme.colors.primary,
uncheckedTrackColor = GlanceTheme.colors.surfaceVariant,
)
}
}
}
@Composable
private fun SwitchElement(
checked: Boolean,
onCheckedChange: Action?,
modifier: GlanceModifier = GlanceModifier,
text: String = "",
style: TextStyle? = null,
colors: SwitchColors = SwitchDefaults.colors(),
maxLines: Int = Int.MAX_VALUE,
) {
val finalModifier = if (onCheckedChange != null) {
modifier.then(ActionModifier(CompoundButtonAction(onCheckedChange, checked)))
} else {
modifier
}
GlanceNode(
factory = { EmittableSwitch(colors) },
update = {
this.set(checked) { this.checked = it }
this.set(text) { this.text = it }
this.set(finalModifier) { this.modifier = it }
this.set(style) { this.style = it }
this.set(colors) { this.colors = it }
this.set(maxLines) { this.maxLines = it }
})
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class EmittableSwitch(
var colors: SwitchColors
) : EmittableCheckable() {
override var modifier: GlanceModifier = GlanceModifier
override fun copy(): Emittable = EmittableSwitch(colors = colors).also {
it.modifier = modifier
it.checked = checked
it.text = text
it.style = style
it.maxLines = maxLines
}
override fun toString(): String = "EmittableSwitch(" +
"$text, " +
"modifier=$modifier, " +
"checked=$checked, " +
"style=$style, " +
"colors=$colors, " +
"maxLines=$maxLines" +
")"
}