feat: revamp about screen

Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
Infi 2023-07-04 18:24:28 +02:00
parent 98aa10c39e
commit 5981f064b5
7 changed files with 140 additions and 24 deletions

View File

@ -180,6 +180,7 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha10"
implementation 'com.github.MikeOrtiz:TouchImageView:3.3'
implementation "androidx.appcompat:appcompat:1.7.0-alpha02"
implementation 'com.google.android.material:material:1.9.0'
// hCaptcha - captcha provider
implementation "com.github.hcaptcha:hcaptcha-android-sdk:3.8.1"

View File

@ -1,8 +1,12 @@
package chat.revolt
import android.app.Application
import com.google.android.material.color.DynamicColors
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class RevoltApplication : Application() {
init {
DynamicColors.applyToActivitiesIfAvailable(this)
}
}

View File

@ -28,6 +28,7 @@ import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
@ -108,7 +109,7 @@ object RevoltAPI {
var sessionToken: String = ""
private set
@OptIn(DelicateCoroutinesApi::class)
@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
val realtimeContext = newSingleThreadContext("RealtimeContext")
val wsFrameChannel = Channel<Any>(Channel.UNLIMITED)

View File

@ -2,32 +2,45 @@ package chat.revolt.screens.about
import android.os.Build
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
@ -38,23 +51,24 @@ import chat.revolt.api.REVOLT_BASE
import chat.revolt.api.RevoltJson
import chat.revolt.api.routes.misc.Root
import chat.revolt.api.routes.misc.getRootRoute
import chat.revolt.api.settings.GlobalState
import chat.revolt.components.generic.PageHeader
import chat.revolt.ui.theme.Theme
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import java.net.URI
class AboutViewModel(
) : ViewModel() {
private val _root = mutableStateOf<Root?>(null)
val root: State<Root?>
get() = _root
var root by mutableStateOf<Root?>(null)
var selectedTabIndex by mutableStateOf(0)
fun getDebugInformation(): Map<String, String> {
return mapOf(
"App ID" to BuildConfig.APPLICATION_ID,
"App Version" to BuildConfig.VERSION_NAME,
"API Host" to URI(REVOLT_BASE).host,
"API Version" to (root.value?.revolt ?: "Unknown"),
"API Version" to (root?.revolt ?: "Unknown"),
"Runtime SDK" to Build.VERSION.SDK_INT.toString(),
"Model" to "${Build.MANUFACTURER} ${
Build.DEVICE.replaceFirstChar {
@ -66,7 +80,7 @@ class AboutViewModel(
init {
viewModelScope.launch {
_root.value = getRootRoute().copy()
root = getRootRoute().copy()
}
}
}
@ -149,6 +163,63 @@ fun AboutScreen(
showBackButton = true,
onBackButtonClicked = { navController.popBackStack() })
// TODO this should be a reusable "tabs" component
when (GlobalState.theme) {
Theme.M3Dynamic -> AndroidView(
factory = {
com.google.android.material.tabs.TabLayout(it).apply {
tabMode = com.google.android.material.tabs.TabLayout.MODE_FIXED
tabGravity = com.google.android.material.tabs.TabLayout.GRAVITY_FILL
addTab(newTab().setText(it.getString(R.string.about_tab_version)))
addTab(newTab().setText(it.getString(R.string.about_tab_details)))
addOnTabSelectedListener(object :
com.google.android.material.tabs.TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: com.google.android.material.tabs.TabLayout.Tab?) {
viewModel.selectedTabIndex = tab?.position ?: 0
}
override fun onTabUnselected(tab: com.google.android.material.tabs.TabLayout.Tab?) {
}
override fun onTabReselected(tab: com.google.android.material.tabs.TabLayout.Tab?) {
}
})
}
},
update = {
it.getTabAt(viewModel.selectedTabIndex)?.select()
},
modifier = Modifier.fillMaxWidth()
)
else -> TabRow(selectedTabIndex = viewModel.selectedTabIndex) {
Tab(
selected = viewModel.selectedTabIndex == 0,
onClick = { viewModel.selectedTabIndex = 0 },
text = {
Text(
text = stringResource(R.string.about_tab_version),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
)
Tab(
selected = viewModel.selectedTabIndex == 1,
onClick = { viewModel.selectedTabIndex = 1 },
text = {
Text(
text = stringResource(R.string.about_tab_details),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
)
}
}
Column(
modifier = Modifier
.fillMaxWidth()
@ -157,22 +228,58 @@ fun AboutScreen(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (viewModel.root.value == null) {
Text(
text = stringResource(R.string.loading),
color = MaterialTheme.colorScheme.onBackground.copy(
alpha = 0.5f
),
style = MaterialTheme.typography.titleMedium.copy(
textAlign = TextAlign.Center,
fontWeight = FontWeight.Normal
)
if (viewModel.root == null) {
CircularProgressIndicator(
modifier = Modifier
.size(48.dp)
)
} else {
DebugInfo(viewModel)
TextButton(onClick = ::copyDebugInformation) {
Text(text = stringResource(id = R.string.copy))
when (viewModel.selectedTabIndex) {
0 -> {
Image(
painter = painterResource(R.drawable.revolt_logo_wide),
contentDescription = stringResource(R.string.about_full_name),
colorFilter = ColorFilter.tint(LocalContentColor.current)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.about_full_name),
style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = BuildConfig.VERSION_NAME,
style = MaterialTheme.typography.labelMedium.copy(
fontWeight = FontWeight.Normal
),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(32.dp))
Text(
text = stringResource(R.string.about_brought_to_you_by),
style = MaterialTheme.typography.labelSmall.copy(
fontWeight = FontWeight.Light
),
color = LocalContentColor.current.copy(
alpha = 0.5f
),
textAlign = TextAlign.Center
)
}
1 -> {
DebugInfo(viewModel)
TextButton(onClick = ::copyDebugInformation) {
Text(text = stringResource(id = R.string.copy))
}
}
}
}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Revolt" parent="Theme.AppCompat.DayNight.NoActionBar">
<style name="Theme.Revolt" parent="Theme.Material3.DayNight.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowSplashScreenBackground">@color/background</item>

View File

@ -71,7 +71,10 @@
<string name="mfa_password_lead">Enter your password to continue.</string>
<string name="about">About</string>
<string name="app_full_name">Revolt on Android</string>
<string name="about_full_name">Revolt on Android</string>
<string name="about_brought_to_you_by">Brought to you with ❤ by the Revolt team.</string>
<string name="about_tab_version">Version</string>
<string name="about_tab_details">Details</string>
<string name="oss_attribution">OSS Attribution</string>
<string name="oss_attribution_body">Revolt is built with the help of these awesome open-source projects.</string>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Revolt" parent="Theme.AppCompat.DayNight.NoActionBar">
<style name="Theme.Revolt" parent="Theme.Material3.DayNight.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>