You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailscale-android/android/src/main/java/com/tailscale/ipn/ui/view/TailscaleLogoView.kt

137 lines
4.6 KiB
Kotlin

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package com.tailscale.ipn.ui.view
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.tailscale.ipn.ui.util.set
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlin.concurrent.timer
// DotsMatrix represents the state of the progress indicator.
typealias DotsMatrix = Array<Array<Boolean>>
// The initial DotsMatrix that represents the Tailscale logo (T-shaped).
val logoDotsMatrix: DotsMatrix =
arrayOf(
arrayOf(false, false, false),
arrayOf(true, true, true),
arrayOf(false, true, false),
)
@Composable
fun TailscaleLogoView(animated: Boolean = false, modifier: Modifier) {
val primaryColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f)
val secondaryColor: Color = primaryColor.copy(alpha = 0.1f)
val currentDotsMatrix: StateFlow<DotsMatrix> = MutableStateFlow(logoDotsMatrix)
var currentDotsMatrixIndex = 0
fun advanceToNextMatrix() {
currentDotsMatrixIndex = (currentDotsMatrixIndex + 1) % gameOfLife.size
val newMatrix =
if (animated) {
gameOfLife[currentDotsMatrixIndex]
} else {
logoDotsMatrix
}
currentDotsMatrix.set(newMatrix)
}
if (animated) {
timer(period = 300L) { advanceToNextMatrix() }
}
@Composable
fun EnabledDot(modifier: Modifier) {
Canvas(modifier = modifier, onDraw = { drawCircle(primaryColor) })
}
@Composable
fun DisabledDot(modifier: Modifier) {
Canvas(modifier = modifier, onDraw = { drawCircle(secondaryColor) })
}
BoxWithConstraints(modifier) {
val currentMatrix = currentDotsMatrix.collectAsState().value
Column(verticalArrangement = Arrangement.spacedBy(this@BoxWithConstraints.maxWidth.div(8))) {
for (y in 0..2) {
Row(horizontalArrangement = Arrangement.spacedBy(this@BoxWithConstraints.maxWidth.div(8))) {
for (x in 0..2) {
if (currentMatrix[y][x]) {
EnabledDot(Modifier.size(this@BoxWithConstraints.maxWidth.div(4)))
} else {
DisabledDot(Modifier.size(this@BoxWithConstraints.maxWidth.div(4)))
}
}
}
}
}
}
}
val gameOfLife: Array<DotsMatrix> =
arrayOf(
arrayOf(
arrayOf(false, true, true),
arrayOf(true, false, true),
arrayOf(false, false, true),
),
arrayOf(
arrayOf(false, true, true),
arrayOf(false, false, true),
arrayOf(false, true, false),
),
arrayOf(
arrayOf(false, true, true),
arrayOf(false, false, false),
arrayOf(false, false, true),
),
arrayOf(
arrayOf(false, false, true),
arrayOf(false, true, false),
arrayOf(false, false, false),
),
arrayOf(
arrayOf(false, true, false),
arrayOf(false, false, false),
arrayOf(false, false, false),
),
arrayOf(
arrayOf(false, false, false),
arrayOf(false, false, true),
arrayOf(false, false, false),
),
arrayOf(
arrayOf(false, false, true),
arrayOf(false, false, false),
arrayOf(false, false, false),
),
arrayOf(
arrayOf(false, false, false),
arrayOf(false, false, false),
arrayOf(true, false, false),
),
arrayOf(
arrayOf(false, false, false), arrayOf(false, false, false), arrayOf(true, true, false)),
arrayOf(
arrayOf(false, false, false), arrayOf(true, false, false), arrayOf(true, true, false)),
arrayOf(
arrayOf(false, false, false), arrayOf(true, true, false), arrayOf(false, true, false)),
arrayOf(
arrayOf(false, false, false), arrayOf(true, true, false), arrayOf(false, true, true)),
arrayOf(
arrayOf(false, false, false), arrayOf(true, true, true), arrayOf(false, false, true)),
arrayOf(arrayOf(false, true, false), arrayOf(true, true, true), arrayOf(true, false, true)))