diff --git a/app/src/main/java/com/geeksville/mesh/ui/DebugFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/DebugFragment.kt index 96f9acd87..6cdba9ad1 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/DebugFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/DebugFragment.kt @@ -23,7 +23,6 @@ import android.view.View import android.view.ViewGroup import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -36,13 +35,9 @@ import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.Button import androidx.compose.material.Card import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Scaffold import androidx.compose.material.Surface import androidx.compose.material.Text -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.outlined.CloudDownload import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -64,13 +59,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.geeksville.mesh.R import com.geeksville.mesh.database.entity.MeshLog import com.geeksville.mesh.model.DebugViewModel +import com.geeksville.mesh.ui.components.BaseScaffold import com.geeksville.mesh.ui.theme.AppTheme import dagger.hilt.android.AndroidEntryPoint import java.text.DateFormat @@ -85,36 +80,9 @@ class DebugFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setBackgroundColor(ContextCompat.getColor(context, R.color.colorAdvancedBackground)) setContent { - val viewModel: DebugViewModel = hiltViewModel() - AppTheme { - Scaffold( - topBar = { - TopAppBar( - title = { Text(stringResource(id = R.string.debug_panel)) }, - navigationIcon = { - IconButton(onClick = { parentFragmentManager.popBackStack() }) { - Icon( - Icons.AutoMirrored.Filled.ArrowBack, - stringResource(id = R.string.navigate_back), - ) - } - }, - actions = { - Button(onClick = viewModel::deleteAllLogs) { - Text(text = stringResource(R.string.clear)) - } - } - ) - }, - ) { innerPadding -> - DebugScreen( - viewModel = viewModel, - contentPadding = innerPadding, - ) - } + DebugScreen { parentFragmentManager.popBackStack() } } } } @@ -185,7 +153,7 @@ private fun Int.asNodeId(): String { @Composable internal fun DebugScreen( viewModel: DebugViewModel = hiltViewModel(), - contentPadding: PaddingValues, + navigateUp: () -> Unit ) { val listState = rememberLazyListState() val logs by viewModel.meshLog.collectAsStateWithLifecycle() @@ -199,13 +167,22 @@ internal fun DebugScreen( } } - SelectionContainer { - LazyColumn( - modifier = Modifier.fillMaxSize(), - state = listState, - contentPadding = contentPadding, - ) { - items(logs, key = { it.uuid }) { log -> DebugItem(annotateMeshLog(log)) } + BaseScaffold( + title = stringResource(id = R.string.debug_panel), + navigateUp = navigateUp, + actions = { + Button(onClick = viewModel::deleteAllLogs) { + Text(text = stringResource(R.string.clear)) + } + } + ) { + SelectionContainer { + LazyColumn( + modifier = Modifier.fillMaxSize(), + state = listState, + ) { + items(logs, key = { it.uuid }) { log -> DebugItem(annotateMeshLog(log)) } + } } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/NavGraph.kt b/app/src/main/java/com/geeksville/mesh/ui/NavGraph.kt index ace607905..6f7e5ba8e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NavGraph.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NavGraph.kt @@ -22,14 +22,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.StringRes -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Scaffold -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.Forward import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.automirrored.filled.Message @@ -61,7 +54,6 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.res.stringResource -import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels @@ -77,6 +69,7 @@ import com.geeksville.mesh.R import com.geeksville.mesh.android.Logging import com.geeksville.mesh.model.MetricsViewModel import com.geeksville.mesh.model.RadioConfigViewModel +import com.geeksville.mesh.ui.components.BaseScaffold import com.geeksville.mesh.ui.components.DeviceMetricsScreen import com.geeksville.mesh.ui.components.EnvironmentMetricsScreen import com.geeksville.mesh.ui.components.NodeMapScreen @@ -143,32 +136,26 @@ class NavGraphFragment : ScreenFragment("NavGraph"), Logging { return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setBackgroundColor(ContextCompat.getColor(context, R.color.colorAdvancedBackground)) setContent { val node by model.destNode.collectAsStateWithLifecycle() AppTheme { val navController: NavHostController = rememberNavController() - Scaffold( - topBar = { - MeshAppBar( - currentScreen = node?.user?.longName - ?: stringResource(R.string.unknown_username), - canNavigateBack = true, - navigateUp = { - if (navController.previousBackStackEntry != null) { - navController.navigateUp() - } else { - parentFragmentManager.popBackStack() - } - }, - ) - } - ) { innerPadding -> + BaseScaffold( + title = node?.user?.longName + ?: stringResource(R.string.unknown_username), + canNavigateBack = true, + navigateUp = { + if (navController.previousBackStackEntry != null) { + navController.navigateUp() + } else { + parentFragmentManager.popBackStack() + } + }, + ) { NavGraph( navController = navController, startDestination = startDestination, - modifier = Modifier.padding(innerPadding), ) } } @@ -294,29 +281,6 @@ sealed class ResponseState { fun isWaiting() = this !is Empty } -@Composable -private fun MeshAppBar( - currentScreen: String, - canNavigateBack: Boolean, - navigateUp: () -> Unit, - modifier: Modifier = Modifier, -) { - TopAppBar( - title = { Text(currentScreen) }, - modifier = modifier, - navigationIcon = { - if (canNavigateBack) { - IconButton(onClick = navigateUp) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.navigate_back), - ) - } - } - } - ) -} - @Suppress("LongMethod") @Composable fun NavGraph( diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt index 6d4eb103e..8a4e65298 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -@file:Suppress("TooManyFunctions", "LongMethod") +@file:Suppress("TooManyFunctions") package com.geeksville.mesh.ui @@ -108,7 +108,7 @@ import kotlin.math.ln fun NodeDetailScreen( modifier: Modifier = Modifier, viewModel: MetricsViewModel = hiltViewModel(), - onNavigate: (Any) -> Unit, + onNavigate: (Route) -> Unit, ) { val state by viewModel.state.collectAsStateWithLifecycle() @@ -135,7 +135,7 @@ private fun NodeDetailList( modifier: Modifier = Modifier, node: Node, metricsState: MetricsState, - onNavigate: (Any) -> Unit = {}, + onNavigate: (Route) -> Unit = {}, ) { LazyColumn( modifier = modifier.fillMaxSize(), @@ -320,7 +320,7 @@ private fun NodeDetailsContent( } @Composable -fun LogNavigationList(state: MetricsState, onNavigate: (Any) -> Unit) { +fun LogNavigationList(state: MetricsState, onNavigate: (Route) -> Unit) { NavCard( title = stringResource(R.string.device_metrics_log), icon = Icons.Default.ChargingStation, diff --git a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt index 2a512df54..5e38905cf 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt @@ -49,22 +49,22 @@ import androidx.compose.material.IconButton import androidx.compose.material.ListItem import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField -import androidx.compose.material.Scaffold import androidx.compose.material.Switch import androidx.compose.material.Text import androidx.compose.material.TextButton -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.FastForward import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusEvent import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalHapticFeedback @@ -76,13 +76,13 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.geeksville.mesh.R import com.geeksville.mesh.android.Logging import com.geeksville.mesh.database.entity.QuickChatAction import com.geeksville.mesh.model.UIViewModel +import com.geeksville.mesh.ui.components.BaseScaffold import com.geeksville.mesh.ui.components.dragContainer import com.geeksville.mesh.ui.components.dragDropItemsIndexed import com.geeksville.mesh.ui.components.rememberDragDropState @@ -98,28 +98,9 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat Settings"), Logging ): View { return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setBackgroundColor(ContextCompat.getColor(context, R.color.colorAdvancedBackground)) setContent { AppTheme { - Scaffold( - topBar = { - TopAppBar( - title = { Text(stringResource(id = R.string.quick_chat)) }, - navigationIcon = { - IconButton(onClick = { parentFragmentManager.popBackStack() }) { - Icon( - Icons.AutoMirrored.Filled.ArrowBack, - stringResource(id = R.string.navigate_back), - ) - } - }, - ) - }, - ) { innerPadding -> - QuickChatScreen( - modifier = Modifier.padding(innerPadding) - ) - } + QuickChatScreen { parentFragmentManager.popBackStack() } } } } @@ -129,7 +110,19 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat Settings"), Logging @Composable internal fun QuickChatScreen( viewModel: UIViewModel = hiltViewModel(), - modifier: Modifier = Modifier, + navigateUp: () -> Unit +) { + BaseScaffold( + title = stringResource(id = R.string.quick_chat), + navigateUp = navigateUp, + ) { + QuickChatContent(viewModel) + } +} + +@Composable +private fun QuickChatContent( + viewModel: UIViewModel = hiltViewModel(), ) { val actions by viewModel.quickChatActions.collectAsStateWithLifecycle() var showActionDialog by remember { mutableStateOf(null) } @@ -140,7 +133,7 @@ internal fun QuickChatScreen( viewModel.updateActionPositions(list) } - Box(modifier = modifier.fillMaxSize()) { + Box(modifier = Modifier.fillMaxSize()) { if (showActionDialog != null) { val action = showActionDialog ?: return EditQuickChatDialog( @@ -212,10 +205,17 @@ private fun EditQuickChatDialog( onDismiss: () -> Unit, ) { var actionInput by remember { mutableStateOf(action) } - val newQuickChat = action.uuid == 0L + val newQuickChat = remember { action.uuid == 0L } val isInstant = actionInput.mode == QuickChatAction.Mode.Instant val title = if (newQuickChat) R.string.quick_chat_new else R.string.quick_chat_edit + val focusRequester = remember { FocusRequester() } + LaunchedEffect(Unit) { + if (newQuickChat) { + focusRequester.requestFocus() + } + } + AlertDialog( onDismissRequest = onDismiss, shape = RoundedCornerShape(16.dp), @@ -246,9 +246,11 @@ private fun EditQuickChatDialog( OutlinedTextFieldWithCounter( label = stringResource(id = R.string.message), value = actionInput.message, - maxSize = 235, + maxSize = 200, getSize = { it.toByteArray().size + 1 }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester), ) { actionInput = actionInput.copy(message = it) if (newQuickChat) { diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/BaseScaffold.kt b/app/src/main/java/com/geeksville/mesh/ui/components/BaseScaffold.kt index 596d59f09..502ee49d3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/BaseScaffold.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/BaseScaffold.kt @@ -42,7 +42,7 @@ import androidx.lifecycle.compose.dropUnlessResumed import com.geeksville.mesh.R @Composable -fun BaseScaffold( +internal fun BaseScaffold( title: String, modifier: Modifier = Modifier, canNavigateBack: Boolean = true, @@ -82,7 +82,7 @@ fun BaseScaffold( } @Composable -fun BaseScaffold( +internal fun BaseScaffold( modifier: Modifier = Modifier, topBar: @Composable () -> Unit = {}, bottomBar: @Composable () -> Unit = {}, diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt b/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt index 77dab99bd..8266df0e9 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt @@ -40,7 +40,6 @@ import androidx.compose.material.ButtonDefaults import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.material.TextField @@ -80,7 +79,6 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp -import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -94,6 +92,7 @@ import com.geeksville.mesh.database.entity.QuickChatAction import com.geeksville.mesh.model.Node import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.model.getChannel +import com.geeksville.mesh.ui.components.BaseScaffold import com.geeksville.mesh.ui.components.NodeKeyStatusIcon import com.geeksville.mesh.ui.components.NodeMenuAction import com.geeksville.mesh.ui.message.components.MessageList @@ -139,7 +138,6 @@ class MessagesFragment : Fragment(), Logging { return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setBackgroundColor(ContextCompat.getColor(context, R.color.colorAdvancedBackground)) setContent { AppTheme { MessageScreen( @@ -213,7 +211,7 @@ internal fun MessageScreen( ) } - Scaffold( + BaseScaffold( topBar = { if (inSelectionMode) { ActionModeTopBar(selectedIds.value) { action -> @@ -269,13 +267,12 @@ internal fun MessageScreen( TextInput(isConnected, messageInput) { viewModel.sendMessage(it, contactKey) } } } - ) { innerPadding -> + ) { if (messages.isNotEmpty()) { MessageList( messages = messages, selectedIds = selectedIds, onUnreadChanged = { viewModel.clearUnreadCount(contactKey, it) }, - contentPadding = innerPadding, onSendReaction = { emoji, id -> viewModel.sendReaction(emoji, id, contactKey) }, ) { action -> when (action) { diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt index df8726e13..75cac4a56 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageList.kt @@ -18,7 +18,6 @@ package com.geeksville.mesh.ui.message.components import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.LazyColumn @@ -54,7 +53,6 @@ internal fun MessageList( messages: List, selectedIds: MutableState>, onUnreadChanged: (Long) -> Unit, - contentPadding: PaddingValues, onSendReaction: (String, Int) -> Unit, onNodeMenuAction: (NodeMenuAction) -> Unit = {} ) { @@ -89,7 +87,6 @@ internal fun MessageList( modifier = Modifier.fillMaxSize(), state = listState, reverseLayout = true, - contentPadding = contentPadding ) { items(messages, key = { it.uuid }) { msg -> val fromLocal = msg.node.user.id == DataPacket.ID_LOCAL