feat: rip out more Views code

This commit is contained in:
infi 2026-07-30 23:29:33 +02:00
parent c94cfec0f0
commit 3ac331ac1f
7 changed files with 8 additions and 239 deletions

View File

@ -215,7 +215,6 @@ dependencies {
implementation(libs.android.palette)
implementation(libs.android.core.telecom)
implementation(libs.android.core.splashscreen)
implementation(libs.android.constraintlayout)
implementation(libs.android.constraintlayout.compose)
implementation(libs.android.appcompat)
implementation(libs.android.material)

View File

@ -49,10 +49,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@ -74,7 +71,6 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import chat.stoat.BuildConfig
import chat.stoat.R
import chat.stoat.StoatApplication
import chat.stoat.api.HitRateLimitException
import chat.stoat.api.StoatAPI
import chat.stoat.api.StoatHttp
@ -90,20 +86,19 @@ import chat.stoat.c2dm.NotificationDeepLink
import chat.stoat.composables.generic.HealthAlert
import chat.stoat.composables.voice.VoicePermissionSwitch
import chat.stoat.composables.voice.VoiceSheet
import chat.stoat.core.model.schemas.HealthNotice
import chat.stoat.internals.StoatWebLink
import chat.stoat.internals.toStoatWebLinkOrNull
import chat.stoat.voice.VoiceCallManager
import chat.stoat.core.model.schemas.HealthNotice
import chat.stoat.material.EasingTokens
import chat.stoat.persistence.KVStorage
import chat.stoat.screens.DefaultDestinationScreen
import chat.stoat.screens.about.AboutScreen
import chat.stoat.screens.about.AttributionScreen
import chat.stoat.screens.changelogs.ReadChangelogScreen
import chat.stoat.screens.chat.ChannelPinsScreen
import chat.stoat.screens.chat.ChannelSearchScreen
import chat.stoat.screens.chat.CHANNEL_MESSAGE_JUMP_CHANNEL_KEY
import chat.stoat.screens.chat.CHANNEL_MESSAGE_JUMP_MESSAGE_KEY
import chat.stoat.screens.chat.ChannelPinsScreen
import chat.stoat.screens.chat.ChannelSearchScreen
import chat.stoat.screens.chat.ChatRouterScreen
import chat.stoat.screens.chat.standalone.CatchUpScreen
import chat.stoat.screens.chat.views.channel.ChannelScreen
@ -134,7 +129,7 @@ import chat.stoat.screens.settings.channel.ChannelSettingsHome
import chat.stoat.screens.settings.channel.ChannelSettingsOverview
import chat.stoat.screens.settings.channel.ChannelSettingsPermissions
import chat.stoat.ui.theme.StoatTheme
import com.google.android.material.color.DynamicColors
import chat.stoat.voice.VoiceCallManager
import io.ktor.client.request.get
import io.sentry.android.core.SentryAndroid
import kotlinx.coroutines.flow.MutableStateFlow
@ -334,22 +329,18 @@ class MainActivityViewModel(
class MainActivity : AppCompatActivity() {
private val viewModel: MainActivityViewModel by viewModel()
// Fix for SDK >=31, where core-splashscreen accidentally removes dynamic colours
// The window can lose its transparent status bar after the activity is re-shown
// See the other one in DefaultDestinationScreen.kt
override fun onResume() {
super.onResume()
DynamicColors.applyToActivityIfAvailable(this)
DynamicColors.applyToActivitiesIfAvailable(StoatApplication.instance)
@Suppress("DEPRECATION") // We are fixing a bug in the splash screen
@Suppress("DEPRECATION") // no Compose-side equivalent for this window flag
window.statusBarColor = Color.Transparent.toArgb()
}
// Same as above for configuration changes (rotation, dark mode, etc.)
override fun onConfigurationChanged(newConfig: android.content.res.Configuration) {
super.onConfigurationChanged(newConfig)
DynamicColors.applyToActivityIfAvailable(this)
DynamicColors.applyToActivitiesIfAvailable(StoatApplication.instance)
@Suppress("DEPRECATION") // We are fixing a bug in the splash screen
@Suppress("DEPRECATION") // no Compose-side equivalent for this window flag
window.statusBarColor = Color.Transparent.toArgb()
}

View File

@ -1,142 +0,0 @@
package chat.stoat.api.internals
import android.graphics.LinearGradient
import android.graphics.Shader
import android.util.Log
import android.widget.TextView
import androidx.compose.ui.graphics.toArgb
import androidx.core.graphics.toColorInt
import chat.stoat.api.internals.colour.CSSColours
import com.google.android.material.color.MaterialColors
import com.google.android.material.elevation.SurfaceColors
object TextViewCompat {
private fun tryParseDirectColour(colour: String): Int {
val cssColour = CSSColours[colour]
if (cssColour != null) {
return cssColour.toArgb()
}
return colour.toColorInt()
}
private fun tryParseVariable(tv: TextView, varName: String): Int {
return when (varName) {
"--accent" -> MaterialColors.getColor(
tv,
androidx.appcompat.R.attr.colorPrimary
)
"--foreground" -> MaterialColors.getColor(
tv,
com.google.android.material.R.attr.colorOnBackground
)
"--background" -> SurfaceColors.SURFACE_0.getColor(tv.context)
"--error" -> MaterialColors.getColor(
tv,
androidx.appcompat.R.attr.colorError
)
else -> tv.currentTextColor
}
}
private fun tryParseSetLinearGradient(tv: TextView, gradient: String): Pair<Shader, Int> {
val stops = mutableListOf<Pair<Float, Int>>()
val parts = mutableListOf<String>()
var startIndex = 0
var openParenthesesCount = 0
for (i in gradient.indices) {
when (gradient[i]) {
'(' -> openParenthesesCount++
')' -> openParenthesesCount--
',' -> {
if (openParenthesesCount == 0) {
val part = gradient.substring(startIndex, i).trim()
parts.add(part)
startIndex = i + 1
}
}
}
}
val lastPart = gradient.substring(startIndex).trim()
if (lastPart.isNotEmpty()) {
parts.add(lastPart)
}
parts.forEachIndexed { index, part ->
if (part.startsWith("to") || part.endsWith("deg")) {
// we don't support any other direction / blocked on compose supporting them
// TODO could probably emulate this by swapping the values around
} else {
val splitPart = part.split(" ")
val colourPart = splitPart[0]
val colour = when {
colourPart.startsWith("var(") -> {
tryParseVariable(
tv,
colourPart.substringAfter("var(").substringBeforeLast(")")
)
}
else -> BrushCompat.parseFunctionColour(colourPart)?.toArgb()
?: tryParseDirectColour(colourPart)
}
val stop = if (splitPart.size == 2) {
splitPart[1].removeSuffix("%").toFloat() / 100f
} else {
index.toFloat() / (parts.size - 1)
}
stops.add(stop to colour)
}
}
val width = tv.paint.measureText(tv.text.toString())
return LinearGradient(
0f,
0f,
width,
0f,
stops.map { it.second }.toIntArray(),
stops.map { it.first }.toFloatArray(),
Shader.TileMode.CLAMP
) to stops.first().second
}
fun setColourFromRoleColour(tv: TextView, colour: String) {
when {
colour.startsWith("var(") -> {
val varName = colour.substringAfter("var(").substringBeforeLast(")")
val parsedColour = tryParseVariable(tv, varName)
tv.setTextColor(parsedColour)
}
colour.startsWith("linear-gradient(") || colour.startsWith("repeating-linear-gradient(") -> {
val gradient = colour.substringAfter("(").substringBeforeLast(")")
val shader = tryParseSetLinearGradient(tv, gradient)
tv.paint.shader = shader.first
}
else -> {
try {
val directColour = tryParseDirectColour(colour)
tv.setTextColor(directColour)
} catch (e: IllegalArgumentException) {
Log.d(
"TextViewCompat",
"Failed to parse colour $colour, not setting colour"
)
}
}
}
}
}

View File

@ -10,10 +10,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import chat.stoat.StoatApplication
import chat.stoat.api.internals.getComponentActivity
import chat.stoat.composables.screens.splash.DisconnectedScreen
import com.google.android.material.color.DynamicColors
@Composable
fun DefaultDestinationScreen(
@ -35,11 +33,9 @@ fun DefaultDestinationScreen(
LaunchedEffect(nextDestination) {
nextDestination?.let {
// Fix for SDK >=31, where core-splashscreen accidentally removes dynamic colours
// The window can lose its transparent status bar before we leave this destination
// See the other one in MainActivity.kt
val activity = context.getComponentActivity() as Activity
DynamicColors.applyToActivityIfAvailable(activity)
DynamicColors.applyToActivitiesIfAvailable(StoatApplication.instance)
activity.window.statusBarColor = Color.Transparent.toArgb()
navController.popBackStack(navController.graph.startDestinationRoute!!, true)

View File

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/drag_handle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tv_title"
style="@style/TextAppearance.Material3.HeadlineSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
tools:text="Changelog for 1.0.0" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<WebView
android:id="@+id/wv_changelog"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" />
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Code.TextAppearance" parent="">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@color/foreground</item>
<item name="android:fontFamily">@font/fragmentmono</item>
</style>
<style name="Code.TextAppearance.Comment" parent="">
<item name="android:textColor">#607d8b</item>
</style>
<style name="Code.TextAppearance.Literal" parent="">
<item name="android:textColor">#e91e63</item>
</style>
<style name="Code.TextAppearance.Keyword" parent="">
<item name="android:textColor">#2196f3</item>
<item name="android:textStyle">bold</item>
</style>
<style name="Code.TextAppearance.Identifier" parent="">
<item name="android:textColor">#cddc39</item>
</style>
<style name="Code.TextAppearance.Types" parent="">
<item name="android:textColor">#00e676</item>
</style>
<style name="Code.TextAppearance.Generics" parent="">
<item name="android:textColor">#ff5722</item>
</style>
<style name="Code.TextAppearance.Params" parent="">
<item name="android:textColor">#009688</item>
</style>
</resources>

View File

@ -39,7 +39,6 @@ android-webkit = { module = "androidx.webkit:webkit", version = "1.14.0" }
android-palette = { module = "androidx.palette:palette-ktx", version = "1.0.0" }
android-core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.2.0-beta02" }
android-core-telecom = { module = "androidx.core:core-telecom", version = "1.0.0" }
android-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.1" }
android-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version = "1.1.1" }
android-appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.1" }
android-material = { module = "com.google.android.material:material", version = "1.13.0" }