Begin re-architecture of calling screen.

This commit is contained in:
Alex Hart
2024-08-19 15:32:11 -03:00
committed by mtang-signal
parent 71b5a9f865
commit 26e79db057
29 changed files with 2860 additions and 245 deletions

View File

@@ -0,0 +1,16 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.ui
import android.content.res.Configuration
import androidx.compose.ui.tooling.preview.Preview
/**
* Only generates a dark preview. Useful for screens that
* are only ever rendered in dark mode (like calling)
*/
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
annotation class DarkPreview()

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import org.signal.core.ui.copied.androidx.compose.material3.IconButtonColors
import org.signal.core.ui.copied.androidx.compose.material3.IconToggleButtonColors
object IconButtons {
@Composable
fun iconButtonColors(
containerColor: Color = Color.Transparent,
contentColor: Color = LocalContentColor.current,
disabledContainerColor: Color = Color.Transparent,
disabledContentColor: Color =
contentColor.copy(alpha = 0.38f)
): IconButtonColors =
IconButtonColors(
containerColor = containerColor,
contentColor = contentColor,
disabledContainerColor = disabledContainerColor,
disabledContentColor = disabledContentColor
)
@Composable
fun iconToggleButtonColors(
containerColor: Color = Color.Transparent,
contentColor: Color = LocalContentColor.current,
disabledContainerColor: Color = Color.Transparent,
disabledContentColor: Color =
contentColor.copy(alpha = 0.38f),
checkedContainerColor: Color = Color.Transparent,
checkedContentColor: Color = MaterialTheme.colorScheme.primary
): IconToggleButtonColors =
IconToggleButtonColors(
containerColor = containerColor,
contentColor = contentColor,
disabledContainerColor = disabledContainerColor,
disabledContentColor = disabledContentColor,
checkedContainerColor = checkedContainerColor,
checkedContentColor = checkedContentColor
)
@Composable
fun IconButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
size: Dp = 40.dp,
shape: Shape = CircleShape,
enabled: Boolean = true,
colors: IconButtonColors = iconButtonColors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable () -> Unit
) {
Box(
modifier = modifier
.minimumInteractiveComponentSize()
.size(size)
.clip(shape)
.background(color = colors.containerColor(enabled).value)
.clickable(
onClick = onClick,
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple(
bounded = false,
radius = size / 2
)
),
contentAlignment = Alignment.Center
) {
val contentColor = colors.contentColor(enabled).value
CompositionLocalProvider(LocalContentColor provides contentColor, content = content)
}
}
@Composable
fun IconToggleButton(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
size: Dp = 40.dp,
shape: Shape = CircleShape,
enabled: Boolean = true,
colors: IconToggleButtonColors = iconToggleButtonColors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable () -> Unit
) {
@Suppress("DEPRECATION_ERROR")
(
Box(
modifier = modifier
.minimumInteractiveComponentSize()
.size(size)
.clip(shape)
.background(color = colors.containerColor(enabled, checked).value)
.toggleable(
value = checked,
onValueChange = onCheckedChange,
enabled = enabled,
role = Role.Checkbox,
interactionSource = interactionSource,
indication = androidx.compose.material.ripple.rememberRipple(
bounded = false,
radius = size / 2
)
),
contentAlignment = Alignment.Center
) {
val contentColor = colors.contentColor(enabled, checked).value
CompositionLocalProvider(LocalContentColor provides contentColor, content = content)
}
)
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.ui.copied.androidx.compose.material3
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.State
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.graphics.Color
@Immutable
class IconButtonColors internal constructor(
private val containerColor: Color,
private val contentColor: Color,
private val disabledContainerColor: Color,
private val disabledContentColor: Color
) {
/**
* Represents the container color for this icon button, depending on [enabled].
*
* @param enabled whether the icon button is enabled
*/
@Composable
internal fun containerColor(enabled: Boolean): State<Color> {
return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor)
}
/**
* Represents the content color for this icon button, depending on [enabled].
*
* @param enabled whether the icon button is enabled
*/
@Composable
internal fun contentColor(enabled: Boolean): State<Color> {
return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || other !is IconButtonColors) return false
if (containerColor != other.containerColor) return false
if (contentColor != other.contentColor) return false
if (disabledContainerColor != other.disabledContainerColor) return false
if (disabledContentColor != other.disabledContentColor) return false
return true
}
override fun hashCode(): Int {
var result = containerColor.hashCode()
result = 31 * result + contentColor.hashCode()
result = 31 * result + disabledContainerColor.hashCode()
result = 31 * result + disabledContentColor.hashCode()
return result
}
}
@Immutable
class IconToggleButtonColors internal constructor(
private val containerColor: Color,
private val contentColor: Color,
private val disabledContainerColor: Color,
private val disabledContentColor: Color,
private val checkedContainerColor: Color,
private val checkedContentColor: Color
) {
/**
* Represents the container color for this icon button, depending on [enabled] and [checked].
*
* @param enabled whether the icon button is enabled
* @param checked whether the icon button is checked
*/
@Composable
internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> {
val target = when {
!enabled -> disabledContainerColor
!checked -> containerColor
else -> checkedContainerColor
}
return rememberUpdatedState(target)
}
/**
* Represents the content color for this icon button, depending on [enabled] and [checked].
*
* @param enabled whether the icon button is enabled
* @param checked whether the icon button is checked
*/
@Composable
internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> {
val target = when {
!enabled -> disabledContentColor
!checked -> contentColor
else -> checkedContentColor
}
return rememberUpdatedState(target)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || other !is IconToggleButtonColors) return false
if (containerColor != other.containerColor) return false
if (contentColor != other.contentColor) return false
if (disabledContainerColor != other.disabledContainerColor) return false
if (disabledContentColor != other.disabledContentColor) return false
if (checkedContainerColor != other.checkedContainerColor) return false
if (checkedContentColor != other.checkedContentColor) return false
return true
}
override fun hashCode(): Int {
var result = containerColor.hashCode()
result = 31 * result + contentColor.hashCode()
result = 31 * result + disabledContainerColor.hashCode()
result = 31 * result + disabledContentColor.hashCode()
result = 31 * result + checkedContainerColor.hashCode()
result = 31 * result + checkedContentColor.hashCode()
return result
}
}