Skip to content

Commit d1d1b55

Browse files
committed
feature: about app screen redesign
1 parent 1d1b974 commit d1d1b55

3 files changed

Lines changed: 106 additions & 70 deletions

File tree

Lines changed: 83 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,128 @@
11
package com.redmadrobot.debug.plugin.aboutapp.ui
22

3+
import android.content.ClipData
4+
import androidx.compose.foundation.clickable
35
import androidx.compose.foundation.layout.Arrangement
4-
import androidx.compose.foundation.layout.Column
56
import androidx.compose.foundation.layout.PaddingValues
6-
import androidx.compose.foundation.layout.defaultMinSize
7+
import androidx.compose.foundation.layout.Row
78
import androidx.compose.foundation.layout.fillMaxSize
89
import androidx.compose.foundation.layout.fillMaxWidth
910
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.layout.systemBarsPadding
1012
import androidx.compose.foundation.lazy.LazyColumn
1113
import androidx.compose.foundation.lazy.items
12-
import androidx.compose.material.Card
13-
import androidx.compose.material.MaterialTheme
14-
import androidx.compose.material.Text
14+
import androidx.compose.material3.Scaffold
15+
import androidx.compose.material3.SnackbarHost
16+
import androidx.compose.material3.SnackbarHostState
17+
import androidx.compose.material3.Text
1518
import androidx.compose.runtime.Composable
19+
import androidx.compose.runtime.LaunchedEffect
1620
import androidx.compose.runtime.collectAsState
1721
import androidx.compose.runtime.getValue
1822
import androidx.compose.runtime.remember
23+
import androidx.compose.ui.Alignment
1924
import androidx.compose.ui.Modifier
20-
import androidx.compose.ui.text.font.FontWeight
25+
import androidx.compose.ui.draw.clip
26+
import androidx.compose.ui.platform.ClipEntry
27+
import androidx.compose.ui.platform.Clipboard
28+
import androidx.compose.ui.platform.LocalClipboard
29+
import androidx.compose.ui.res.stringResource
2130
import androidx.compose.ui.text.style.TextOverflow
22-
import androidx.compose.ui.tooling.preview.Preview
2331
import androidx.compose.ui.unit.dp
2432
import com.redmadrobot.debug.core.extension.getPlugin
2533
import com.redmadrobot.debug.core.extension.provideViewModel
2634
import com.redmadrobot.debug.plugin.aboutapp.AboutAppPlugin
2735
import com.redmadrobot.debug.plugin.aboutapp.AboutAppPluginContainer
36+
import com.redmadrobot.debug.plugin.aboutapp.R
2837
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo
38+
import com.redmadrobot.debug.uikit.theme.DebugPanelShapes
39+
import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
2940

3041
@Composable
3142
internal fun AboutAppScreen(
3243
viewModel: AboutAppViewModel = provideViewModel {
3344
getPlugin<AboutAppPlugin>()
3445
.getContainer<AboutAppPluginContainer>()
3546
.createAboutAppViewModel()
36-
}
47+
},
3748
) {
38-
val state by viewModel.state.collectAsState()
49+
val snackbarHostState = remember { SnackbarHostState() }
3950

40-
AboutAppLayout(state = state)
41-
}
51+
val state by viewModel.state.collectAsState()
52+
val clipboard = LocalClipboard.current
4253

43-
@Composable
44-
private fun AboutAppLayout(state: AboutAppViewState) {
45-
LazyColumn(
46-
modifier = Modifier.fillMaxSize(),
47-
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
48-
contentPadding = PaddingValues(all = 16.dp),
49-
) {
50-
items(items = state.appInfoList, key = { it.id }) { item ->
51-
AboutAppItem(item = item)
54+
LaunchedEffect(Unit) {
55+
viewModel.events.collect { event ->
56+
copyToClipboard(clipboard = clipboard, item = event.item)
57+
val message = event.message.format(event.item.title)
58+
snackbarHostState.showSnackbar(message = message)
5259
}
5360
}
54-
}
5561

56-
@Composable
57-
private fun AboutAppItem(item: AboutAppInfo, modifier: Modifier = Modifier) {
58-
Card(
59-
modifier = modifier
60-
.fillMaxWidth()
61-
.defaultMinSize(minHeight = 56.dp),
62-
elevation = 4.dp
63-
) {
64-
Column(
62+
Scaffold(
63+
containerColor = DebugPanelTheme.colors.background.primary,
64+
snackbarHost = {
65+
SnackbarHost(
66+
modifier = Modifier.systemBarsPadding(),
67+
hostState = snackbarHostState
68+
)
69+
},
70+
) { paddingValues ->
71+
LazyColumn(
6572
modifier = Modifier
66-
.fillMaxWidth()
67-
.padding(all = 16.dp),
68-
verticalArrangement = Arrangement.spacedBy(space = 4.dp),
73+
.fillMaxSize()
74+
.padding(paddingValues = paddingValues),
75+
contentPadding = PaddingValues(vertical = 8.dp),
6976
) {
70-
Text(
71-
text = item.title,
72-
style = MaterialTheme.typography.caption,
73-
fontWeight = FontWeight.SemiBold,
74-
color = MaterialTheme.colors.primary,
75-
)
76-
Text(
77-
text = item.value,
78-
style = MaterialTheme.typography.body1,
79-
color = MaterialTheme.colors.onSurface,
80-
maxLines = 2,
81-
overflow = TextOverflow.Ellipsis,
82-
)
77+
items(items = state.appInfoList, key = { it.id }) { item ->
78+
InfoRow(
79+
label = item.title,
80+
value = item.value,
81+
onClick = { message ->
82+
viewModel.onInfoItemClicked(message = message, item = item)
83+
},
84+
)
85+
}
8386
}
8487
}
8588
}
8689

87-
@Preview(showBackground = true)
8890
@Composable
89-
private fun Preview() {
90-
val previewTitle = "Версия"
91-
val previewValue = "3,14"
92-
val state = remember {
93-
AboutAppViewState(
94-
appInfoList = listOf(
95-
AboutAppInfo(title = previewTitle, value = previewValue),
96-
AboutAppInfo(
97-
title = "Номер билда",
98-
value = "fgkdfjgkdfgjdfkgjdfkjgkdfjgkdfjgkdfjgkdjgskdjgkdgfjdsfgjdsfgdsfgjdsfgdskjfgdsjkfgdjfgdsfg"
99-
),
100-
AboutAppInfo(title = previewTitle, value = previewValue),
101-
AboutAppInfo(title = previewTitle, value = previewValue),
102-
AboutAppInfo(title = previewTitle, value = previewValue),
103-
AboutAppInfo(title = previewTitle, value = previewValue),
104-
AboutAppInfo(title = previewTitle, value = previewValue),
105-
AboutAppInfo(title = previewTitle, value = previewValue),
106-
AboutAppInfo(title = previewTitle, value = previewValue),
107-
)
91+
private fun InfoRow(
92+
label: String,
93+
value: String,
94+
onClick: (String) -> Unit,
95+
modifier: Modifier = Modifier
96+
) {
97+
val copiedMessage = stringResource(id = R.string.about_app_copied)
98+
99+
Row(
100+
modifier = modifier
101+
.fillMaxWidth()
102+
.clip(shape = DebugPanelShapes.medium)
103+
.clickable(onClick = { onClick.invoke(copiedMessage) })
104+
.padding(horizontal = 12.dp, vertical = 8.dp),
105+
verticalAlignment = Alignment.Top,
106+
horizontalArrangement = Arrangement.Center
107+
) {
108+
Text(
109+
text = label.uppercase(),
110+
style = DebugPanelTheme.typography.labelMedium,
111+
color = DebugPanelTheme.colors.content.tertiary,
112+
modifier = Modifier.padding(end = 12.dp),
113+
)
114+
Text(
115+
text = value,
116+
style = DebugPanelTheme.typography.bodySmall,
117+
color = DebugPanelTheme.colors.content.primary,
118+
modifier = Modifier.weight(weight = 1f),
119+
overflow = TextOverflow.Ellipsis,
108120
)
109121
}
122+
}
110123

111-
MaterialTheme {
112-
AboutAppLayout(state = state)
113-
}
124+
private suspend fun copyToClipboard(clipboard: Clipboard, item: AboutAppInfo) {
125+
val clipData = ClipData.newPlainText(item.title, item.value)
126+
127+
clipboard.setClipEntry(clipEntry = ClipEntry(clipData))
114128
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
package com.redmadrobot.debug.plugin.aboutapp.ui
22

3+
import androidx.lifecycle.viewModelScope
4+
import com.redmadrobot.debug.core.DebugEvent
35
import com.redmadrobot.debug.core.internal.PluginViewModel
46
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo
7+
import kotlinx.coroutines.flow.MutableSharedFlow
58
import kotlinx.coroutines.flow.MutableStateFlow
69
import kotlinx.coroutines.flow.StateFlow
10+
import kotlinx.coroutines.flow.asSharedFlow
711
import kotlinx.coroutines.flow.asStateFlow
12+
import kotlinx.coroutines.flow.distinctUntilChanged
13+
import kotlinx.coroutines.launch
814

915
internal class AboutAppViewModel(appInfoList: List<AboutAppInfo>) : PluginViewModel() {
1016
private val _state = MutableStateFlow(value = AboutAppViewState(appInfoList = appInfoList))
1117
val state: StateFlow<AboutAppViewState> = _state.asStateFlow()
18+
19+
private val _events = MutableSharedFlow<AppInfoSelectionEvent>()
20+
val events = _events.asSharedFlow().distinctUntilChanged()
21+
22+
fun onInfoItemClicked(message: String, item: AboutAppInfo) {
23+
viewModelScope.launch {
24+
_events.emit(AppInfoSelectionEvent(message = message, item = item))
25+
}
26+
}
1227
}
28+
29+
internal data class AppInfoSelectionEvent(
30+
val message: String,
31+
val item: AboutAppInfo
32+
) : DebugEvent
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<resources />
2+
<resources>
3+
<string name="about_app_copied">«%s» is copied</string>
4+
</resources>

0 commit comments

Comments
 (0)