feat: material3 (#1862)

This commit is contained in:
James Rich
2025-05-17 11:39:53 -05:00
committed by GitHub
parent 8db9665ff3
commit 4cba13ea14
99 changed files with 2134 additions and 1606 deletions

View File

@@ -136,7 +136,6 @@
<activity
android:name="com.geeksville.mesh.MainActivity"
android:launchMode="standard"
android:screenOrientation="unspecified"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>

View File

@@ -29,10 +29,7 @@ import android.os.Build
import android.os.Bundle
import android.os.RemoteException
import android.provider.Settings
import android.text.Html
import android.text.method.LinkMovementMethod
import android.view.MotionEvent
import android.widget.TextView
import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@@ -40,24 +37,24 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.setPadding
import com.geeksville.mesh.android.BindFailedException
import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.ServiceClient
import com.geeksville.mesh.android.dpToPx
import com.geeksville.mesh.android.getBluetoothPermissions
import com.geeksville.mesh.android.getNotificationPermissions
import com.geeksville.mesh.android.hasBluetoothPermission
import com.geeksville.mesh.android.hasNotificationPermission
import com.geeksville.mesh.android.permissionMissing
import com.geeksville.mesh.android.rationaleDialog
import com.geeksville.mesh.android.shouldShowRequestPermissionRationale
import com.geeksville.mesh.concurrent.handledLaunch
import com.geeksville.mesh.model.BluetoothViewModel
@@ -70,10 +67,10 @@ import com.geeksville.mesh.service.startService
import com.geeksville.mesh.ui.MainMenuAction
import com.geeksville.mesh.ui.MainScreen
import com.geeksville.mesh.ui.theme.AppTheme
import com.geeksville.mesh.ui.theme.MODE_DYNAMIC
import com.geeksville.mesh.util.Exceptions
import com.geeksville.mesh.util.LanguageUtils
import com.geeksville.mesh.util.getPackageInfoCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -127,10 +124,6 @@ class MainActivity : AppCompatActivity(), Logging {
val lang = prefs.getString("lang", LanguageUtils.SYSTEM_DEFAULT)
if (lang != LanguageUtils.SYSTEM_MANAGED) LanguageUtils.migrateLanguagePrefs(prefs)
info("in-app language is ${LanguageUtils.getLocale()}")
// Set theme
AppCompatDelegate.setDefaultNightMode(
prefs.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
)
// First run: show AppIntroduction
if (!prefs.getBoolean("app_intro_completed", false)) {
startActivity(Intent(this, AppIntroduction::class.java))
@@ -141,7 +134,19 @@ class MainActivity : AppCompatActivity(), Logging {
setContent {
Box(Modifier.safeDrawingPadding()) {
AppTheme {
val theme by model.theme.collectAsState()
val dynamic = theme == MODE_DYNAMIC
val dark = when (theme) {
AppCompatDelegate.MODE_NIGHT_YES -> true
AppCompatDelegate.MODE_NIGHT_NO -> false
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> isSystemInDarkTheme()
else -> isSystemInDarkTheme()
}
AppTheme(
dynamicColor = dynamic,
darkTheme = dark,
) {
MainScreen(viewModel = model, onAction = ::onMainMenuAction)
}
}
@@ -237,31 +242,6 @@ class MainActivity : AppCompatActivity(), Logging {
super.onDestroy()
}
/** Show an alert that may contain HTML */
private fun showAlert(titleText: Int, messageText: Int) {
// make links clickable per https://stackoverflow.com/a/62642807
// val messageStr = getText(messageText)
val builder = MaterialAlertDialogBuilder(this)
.setCancelable(false)
.setTitle(titleText)
.setMessage(messageText)
.setPositiveButton(R.string.okay) { _, _ ->
info("User acknowledged")
}
val dialog = builder.show()
// Make the textview clickable. Must be called after show()
val view = (dialog.findViewById(android.R.id.message) as TextView?)!!
// Linkify.addLinks(view, Linkify.ALL) // not needed with this method
view.movementMethod = LinkMovementMethod.getInstance()
debug("showAlert: $titleText")
showSettingsPage() // Default to the settings page in this case
}
// Called when we gain/lose a connection to our mesh radio
private fun onMeshConnectionChanged(newConnection: MeshService.ConnectionState) {
if (newConnection == MeshService.ConnectionState.CONNECTED) {
@@ -272,15 +252,20 @@ class MainActivity : AppCompatActivity(), Logging {
if (info != null) {
val isOld = info.minAppVersion > BuildConfig.VERSION_CODE
if (isOld) {
showAlert(R.string.app_too_old, R.string.must_update)
model.showAlert(
getString(R.string.app_too_old),
getString(R.string.must_update),
dismissable = false,
)
} else {
// If we are already doing an update don't put up a dialog or try to get device info
val isUpdating = service.updateStatus >= 0
if (!isUpdating) {
val curVer = DeviceVersion(info.firmwareVersion ?: "0.0.0")
if (curVer < MeshService.minDeviceVersion) {
showAlert(R.string.firmware_too_old, R.string.firmware_old)
val title = getString(R.string.firmware_too_old)
val message = getString(R.string.firmware_old)
model.showAlert(title, message, dismissable = false)
}
}
}
@@ -300,11 +285,17 @@ class MainActivity : AppCompatActivity(), Logging {
private fun checkNotificationPermissions() {
if (!hasNotificationPermission()) {
val notificationPermissions = getNotificationPermissions()
rationaleDialog(
shouldShowRequestPermissionRationale(notificationPermissions),
R.string.notification_required,
getString(R.string.why_notification_required),
) {
if (shouldShowRequestPermissionRationale(notificationPermissions)) {
val title = getString(R.string.notification_required)
val message = getString(R.string.why_notification_required)
model.showAlert(
title = title,
message = message,
onConfirm = {
notificationPermissionsLauncher.launch(notificationPermissions)
},
)
} else {
notificationPermissionsLauncher.launch(notificationPermissions)
}
}
@@ -324,29 +315,16 @@ class MainActivity : AppCompatActivity(), Logging {
intent.putExtra(Settings.EXTRA_CHANNEL_ID, "my_alerts")
startActivity(intent)
}
val message = Html.fromHtml(
getString(R.string.alerts_dnd_request_text),
Html.FROM_HTML_MODE_COMPACT
)
val messageTextView = TextView(this).also {
it.text = message
it.movementMethod = LinkMovementMethod.getInstance()
it.setPadding(dpToPx(16f))
}
MaterialAlertDialogBuilder(this)
.setTitle(R.string.alerts_dnd_request_title)
.setView(messageTextView)
.setNeutralButton(R.string.cancel) { dialog, _ ->
prefs.edit { putBoolean("dnd_rationale_shown", true) }
dialog.dismiss()
}
.setPositiveButton(R.string.channel_settings) { dialog, _ ->
model.showAlert(
title = getString(R.string.alerts_dnd_request_title),
html = getString(R.string.alerts_dnd_request_text),
onConfirm = {
showAlertAppNotificationSettings()
prefs.edit { putBoolean("dnd_rationale_shown", true) }
dialog.dismiss()
}
.setCancelable(false).show()
},
dismissable = true
).also {
prefs.edit { putBoolean("dnd_rationale_shown", true) }
}
}
}
}
@@ -446,21 +424,24 @@ class MainActivity : AppCompatActivity(), Logging {
bleRequestEnable.launch(enableBtIntent)
} else {
val bluetoothPermissions = getBluetoothPermissions()
rationaleDialog(shouldShowRequestPermissionRationale(bluetoothPermissions)) {
bluetoothPermissionsLauncher.launch(bluetoothPermissions)
}
val title = getString(R.string.required_permissions)
val message = permissionMissing
model.showAlert(
title = title,
message = message,
onConfirm = {
bluetoothPermissionsLauncher.launch(bluetoothPermissions)
},
)
}
}
}
model.tracerouteResponse.observe(this) { response ->
MaterialAlertDialogBuilder(this)
.setCancelable(false)
.setTitle(R.string.traceroute)
.setMessage(response ?: return@observe)
.setPositiveButton(R.string.okay) { _, _ -> }
.show()
model.showAlert(
title = getString(R.string.traceroute),
message = response ?: return@observe,
)
model.clearTracerouteResponse()
}
@@ -520,12 +501,8 @@ class MainActivity : AppCompatActivity(), Logging {
// Theme functions
private fun chooseThemeDialog() {
// Prepare dialog and its items
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(getString(R.string.choose_theme))
val styles = mapOf(
getString(R.string.dynamic) to MODE_DYNAMIC,
getString(R.string.theme_light) to AppCompatDelegate.MODE_NIGHT_NO,
getString(R.string.theme_dark) to AppCompatDelegate.MODE_NIGHT_YES,
getString(R.string.theme_system) to AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
@@ -535,43 +512,34 @@ class MainActivity : AppCompatActivity(), Logging {
val prefs = UIViewModel.getPreferences(this)
val theme = prefs.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
debug("Theme from prefs: $theme")
builder.setSingleChoiceItems(
styles.keys.toTypedArray(),
styles.values.indexOf(theme)
) { dialog, position ->
val selectedTheme = styles.values.elementAt(position)
debug("Set theme pref to $selectedTheme")
prefs.edit().putInt("theme", selectedTheme).apply()
AppCompatDelegate.setDefaultNightMode(selectedTheme)
dialog.dismiss()
}
val dialog = builder.create()
dialog.show()
// map theme keys to function to set theme
model.showAlert(
title = getString(R.string.choose_theme),
message = "",
choices = styles.mapValues { (_, value) ->
{
model.setTheme(value)
}
},
)
}
private fun chooseLangDialog() {
// Prepare dialog and its items
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(getString(R.string.preferences_language))
val languageTags = LanguageUtils.getLanguageTags(this)
// Load preferences and its value
val lang = LanguageUtils.getLocale()
debug("Lang from prefs: $lang")
builder.setSingleChoiceItems(
languageTags.keys.toTypedArray(),
languageTags.values.indexOf(lang)
) { dialog, position ->
val selectedLang = languageTags.values.elementAt(position)
debug("Set lang pref to $selectedLang")
LanguageUtils.setLocale(selectedLang)
dialog.dismiss()
// map lang keys to function to set locale
val langMap = languageTags.mapValues { (_, value) ->
{
LanguageUtils.setLocale(value)
}
}
val dialog = builder.create()
dialog.show()
model.showAlert(
title = getString(R.string.preferences_language),
message = "",
choices = langMap,
)
}
}

View File

@@ -27,8 +27,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
import com.geeksville.mesh.repository.network.NetworkRepository
import com.geeksville.mesh.repository.network.NetworkRepository.Companion.toAddressString
@@ -42,6 +42,8 @@ import com.hoho.android.usbserial.driver.UsbSerialDriver
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
@@ -63,11 +65,8 @@ class BTScanModel @Inject constructor(
val devices = MutableLiveData<MutableMap<String, DeviceListEntry>>(mutableMapOf())
val errorText = MutableLiveData<String?>(null)
private val showMockInterface = MutableStateFlow(radioInterfaceService.isMockInterface)
fun showMockInterface() {
showMockInterface.value = true
}
private val showMockInterface: StateFlow<Boolean> get() =
MutableStateFlow(radioInterfaceService.isMockInterface()).asStateFlow()
init {
combine(
@@ -77,7 +76,9 @@ class BTScanModel @Inject constructor(
showMockInterface,
) { ble, tcp, usb, showMockInterface ->
devices.value = mutableMapOf<String, DeviceListEntry>().apply {
fun addDevice(entry: DeviceListEntry) { this[entry.fullAddress] = entry }
fun addDevice(entry: DeviceListEntry) {
this[entry.fullAddress] = entry
}
// Include a placeholder for "None"
addDevice(DeviceListEntry(context.getString(R.string.none), "n", true))
@@ -87,7 +88,8 @@ class BTScanModel @Inject constructor(
}
// Include paired Bluetooth devices
ble.bondedDevices.map(::BLEDeviceListEntry).sortedBy { it.name }.forEach(::addDevice)
ble.bondedDevices.map(::BLEDeviceListEntry).sortedBy { it.name }
.forEach(::addDevice)
// Include Network Service Discovery
tcp.forEach { service ->
@@ -155,7 +157,7 @@ class BTScanModel @Inject constructor(
val selectedAddress get() = radioInterfaceService.getDeviceAddress()
val selectedBluetooth: Boolean get() = selectedAddress?.getOrNull(0) == 'x'
/// Use the string for the NopInterface
// / Use the string for the NopInterface
val selectedNotNull: String get() = selectedAddress ?: "n"
val scanResult = MutableLiveData<MutableMap<String, DeviceListEntry>>(mutableMapOf())
@@ -186,23 +188,23 @@ class BTScanModel @Inject constructor(
_spinner.value = true
scanJob = bluetoothRepository.scan()
.onEach { result ->
val fullAddress = radioInterfaceService.toInterfaceAddress(
InterfaceId.BLUETOOTH,
result.device.address
)
// prevent log spam because we'll get lots of redundant scan results
val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED
val oldDevs = scanResult.value!!
val oldEntry = oldDevs[fullAddress]
// Don't spam the GUI with endless updates for non changing nodes
if (oldEntry == null || oldEntry.bonded != isBonded) {
val entry = DeviceListEntry(result.device.name, fullAddress, isBonded)
oldDevs[entry.fullAddress] = entry
scanResult.value = oldDevs
}
}.catch { ex ->
serviceRepository.setErrorMessage("Unexpected Bluetooth scan failure: ${ex.message}")
}.launchIn(viewModelScope)
val fullAddress = radioInterfaceService.toInterfaceAddress(
InterfaceId.BLUETOOTH,
result.device.address
)
// prevent log spam because we'll get lots of redundant scan results
val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED
val oldDevs = scanResult.value!!
val oldEntry = oldDevs[fullAddress]
// Don't spam the GUI with endless updates for non changing nodes
if (oldEntry == null || oldEntry.bonded != isBonded) {
val entry = DeviceListEntry(result.device.name, fullAddress, isBonded)
oldDevs[entry.fullAddress] = entry
scanResult.value = oldDevs
}
}.catch { ex ->
serviceRepository.setErrorMessage("Unexpected Bluetooth scan failure: ${ex.message}")
}.launchIn(viewModelScope)
}
private fun changeDeviceAddress(address: String) {

View File

@@ -22,7 +22,8 @@ import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.RemoteException
import androidx.compose.material.SnackbarHostState
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.material3.SnackbarHostState
import androidx.core.content.edit
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@@ -105,6 +106,7 @@ fun getInitials(nameIn: String): String {
}
if (nm.length >= nchars) nm else name
}
else -> words.map { it.first() }.joinToString("")
}
return initials.take(nchars)
@@ -128,18 +130,19 @@ internal fun getChannelList(
if (old.getOrNull(i) != new.getOrNull(i)) {
add(
channel {
role = when (i) {
0 -> ChannelProtos.Channel.Role.PRIMARY
in 1..new.lastIndex -> ChannelProtos.Channel.Role.SECONDARY
else -> ChannelProtos.Channel.Role.DISABLED
}
index = i
settings = new.getOrNull(i) ?: channelSettings { }
}
role = when (i) {
0 -> ChannelProtos.Channel.Role.PRIMARY
in 1..new.lastIndex -> ChannelProtos.Channel.Role.SECONDARY
else -> ChannelProtos.Channel.Role.DISABLED
}
index = i
settings = new.getOrNull(i) ?: channelSettings { }
}
)
}
}
}
data class NodesUiState(
val sort: NodeSortOption = NodeSortOption.LAST_HEARD,
val filter: String = "",
@@ -179,11 +182,60 @@ class UIViewModel @Inject constructor(
private val preferences: SharedPreferences
) : ViewModel(), Logging {
private val _theme =
MutableStateFlow(preferences.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM))
val theme: StateFlow<Int> = _theme.asStateFlow()
fun setTheme(theme: Int) {
_theme.value = theme
preferences.edit { putInt("theme", theme) }
}
data class AlertData(
val title: String,
val message: String? = null,
val html: String? = null,
val onConfirm: (() -> Unit)? = null,
val onDismiss: (() -> Unit)? = null,
val choices: Map<String, () -> Unit> = emptyMap()
)
private val _currentAlert: MutableStateFlow<AlertData?> = MutableStateFlow(null)
val currentAlert = _currentAlert.asStateFlow()
fun showAlert(
title: String,
message: String? = null,
html: String? = null,
onConfirm: (() -> Unit)? = {},
dismissable: Boolean = true,
choices: Map<String, () -> Unit> = emptyMap()
) {
_currentAlert.value =
AlertData(
title = title,
message = message,
html = html,
onConfirm = {
onConfirm?.invoke()
if (dismissable) dismissAlert()
},
onDismiss = {
if (dismissable) dismissAlert()
},
choices = choices
)
}
private fun dismissAlert() {
_currentAlert.value = null
}
private val _title = MutableStateFlow("")
val title: StateFlow<String> = _title.asStateFlow()
fun setTitle(title: String) {
_title.value = title
}
val receivingLocationUpdates: StateFlow<Boolean> get() = locationRepository.receivingLocationUpdates
val meshService: IMeshService? get() = radioConfigRepository.meshService
@@ -194,15 +246,17 @@ class UIViewModel @Inject constructor(
val localConfig: StateFlow<LocalConfig> = _localConfig
val config get() = _localConfig.value
private val _moduleConfig = MutableStateFlow<LocalModuleConfig>(LocalModuleConfig.getDefaultInstance())
private val _moduleConfig =
MutableStateFlow<LocalModuleConfig>(LocalModuleConfig.getDefaultInstance())
val moduleConfig: StateFlow<LocalModuleConfig> = _moduleConfig
val module get() = _moduleConfig.value
private val _channels = MutableStateFlow(channelSet {})
val channels: StateFlow<AppOnlyProtos.ChannelSet> get() = _channels
val quickChatActions get() = quickChatActionRepository.getAllActions()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
val quickChatActions
get() = quickChatActionRepository.getAllActions()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
private val nodeFilterText = MutableStateFlow("")
private val nodeSortOption = MutableStateFlow(NodeSortOption.LAST_HEARD)
@@ -554,15 +608,16 @@ class UIViewModel @Inject constructor(
if (config.lora != newConfig.lora) setConfig(newConfig)
}
val provideLocation = object : MutableLiveData<Boolean>(preferences.getBoolean("provide-location", false)) {
override fun setValue(value: Boolean) {
super.setValue(value)
val provideLocation =
object : MutableLiveData<Boolean>(preferences.getBoolean("provide-location", false)) {
override fun setValue(value: Boolean) {
super.setValue(value)
preferences.edit {
this.putBoolean("provide-location", value)
preferences.edit {
this.putBoolean("provide-location", value)
}
}
}
}
fun setOwner(name: String) {
val user = ourNodeInfo.value?.user?.copy {
@@ -603,78 +658,86 @@ class UIViewModel @Inject constructor(
// Packets are ordered by time, we keep most recent position of
// our device in localNodePosition.
val dateFormat = SimpleDateFormat("\"yyyy-MM-dd\",\"HH:mm:ss\"", Locale.getDefault())
meshLogRepository.getAllLogsInReceiveOrder(Int.MAX_VALUE).first().forEach { packet ->
// If we get a NodeInfo packet, use it to update our position data (if valid)
packet.nodeInfo?.let { nodeInfo ->
positionToPos.invoke(nodeInfo.position)?.let {
nodePositions[nodeInfo.num] = nodeInfo.position
val dateFormat =
SimpleDateFormat("\"yyyy-MM-dd\",\"HH:mm:ss\"", Locale.getDefault())
meshLogRepository.getAllLogsInReceiveOrder(Int.MAX_VALUE).first()
.forEach { packet ->
// If we get a NodeInfo packet, use it to update our position data (if valid)
packet.nodeInfo?.let { nodeInfo ->
positionToPos.invoke(nodeInfo.position)?.let {
nodePositions[nodeInfo.num] = nodeInfo.position
}
}
packet.meshPacket?.let { proto ->
// If the packet contains position data then use it to update, if valid
packet.position?.let { position ->
positionToPos.invoke(position)?.let {
nodePositions[proto.from.takeIf { it != 0 } ?: myNodeNum] =
position
}
}
// Filter out of our results any packet that doesn't report SNR. This
// is primarily ADMIN_APP.
if (proto.rxSnr != 0.0f) {
val rxDateTime = dateFormat.format(packet.received_date)
val rxFrom = proto.from.toUInt()
val senderName = nodes[proto.from]?.user?.longName ?: ""
// sender lat & long
val senderPosition = nodePositions[proto.from]
val senderPos = positionToPos.invoke(senderPosition)
val senderLat = senderPos?.latitude ?: ""
val senderLong = senderPos?.longitude ?: ""
// rx lat, long, and elevation
val rxPosition = nodePositions[myNodeNum]
val rxPos = positionToPos.invoke(rxPosition)
val rxLat = rxPos?.latitude ?: ""
val rxLong = rxPos?.longitude ?: ""
val rxAlt = rxPos?.altitude ?: ""
val rxSnr = proto.rxSnr
// Calculate the distance if both positions are valid
val dist = if (senderPos == null || rxPos == null) {
""
} else {
positionToMeter(
rxPosition!!, // Use rxPosition but only if rxPos was valid
senderPosition!! // Use senderPosition but only if senderPos was valid
).roundToInt().toString()
}
val hopLimit = proto.hopLimit
val payload = when {
proto.decoded.portnumValue !in setOf(
Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
Portnums.PortNum.RANGE_TEST_APP_VALUE,
) -> "<${proto.decoded.portnum}>"
proto.hasDecoded() -> proto.decoded.payload.toStringUtf8()
.replace("\"", "\"\"")
proto.hasEncrypted() -> "${proto.encrypted.size()} encrypted bytes"
else -> ""
}
// date,time,from,sender name,sender lat,sender long,rx lat,rx long,rx elevation,rx snr,distance,hop limit,payload
writer.appendLine("$rxDateTime,\"$rxFrom\",\"$senderName\",\"$senderLat\",\"$senderLong\",\"$rxLat\",\"$rxLong\",\"$rxAlt\",\"$rxSnr\",\"$dist\",\"$hopLimit\",\"$payload\"")
}
}
}
packet.meshPacket?.let { proto ->
// If the packet contains position data then use it to update, if valid
packet.position?.let { position ->
positionToPos.invoke(position)?.let {
nodePositions[proto.from.takeIf { it != 0 } ?: myNodeNum] = position
}
}
// Filter out of our results any packet that doesn't report SNR. This
// is primarily ADMIN_APP.
if (proto.rxSnr != 0.0f) {
val rxDateTime = dateFormat.format(packet.received_date)
val rxFrom = proto.from.toUInt()
val senderName = nodes[proto.from]?.user?.longName ?: ""
// sender lat & long
val senderPosition = nodePositions[proto.from]
val senderPos = positionToPos.invoke(senderPosition)
val senderLat = senderPos?.latitude ?: ""
val senderLong = senderPos?.longitude ?: ""
// rx lat, long, and elevation
val rxPosition = nodePositions[myNodeNum]
val rxPos = positionToPos.invoke(rxPosition)
val rxLat = rxPos?.latitude ?: ""
val rxLong = rxPos?.longitude ?: ""
val rxAlt = rxPos?.altitude ?: ""
val rxSnr = proto.rxSnr
// Calculate the distance if both positions are valid
val dist = if (senderPos == null || rxPos == null) {
""
} else {
positionToMeter(
rxPosition!!, // Use rxPosition but only if rxPos was valid
senderPosition!! // Use senderPosition but only if senderPos was valid
).roundToInt().toString()
}
val hopLimit = proto.hopLimit
val payload = when {
proto.decoded.portnumValue !in setOf(
Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
Portnums.PortNum.RANGE_TEST_APP_VALUE,
) -> "<${proto.decoded.portnum}>"
proto.hasDecoded() -> proto.decoded.payload.toStringUtf8()
.replace("\"", "\"\"")
proto.hasEncrypted() -> "${proto.encrypted.size()} encrypted bytes"
else -> ""
}
// date,time,from,sender name,sender lat,sender long,rx lat,rx long,rx elevation,rx snr,distance,hop limit,payload
writer.appendLine("$rxDateTime,\"$rxFrom\",\"$senderName\",\"$senderLat\",\"$senderLong\",\"$rxLat\",\"$rxLong\",\"$rxAlt\",\"$rxSnr\",\"$dist\",\"$hopLimit\",\"$payload\"")
}
}
}
}
}
}
private suspend inline fun writeToUri(uri: Uri, crossinline block: suspend (BufferedWriter) -> Unit) {
private suspend inline fun writeToUri(
uri: Uri,
crossinline block: suspend (BufferedWriter) -> Unit
) {
withContext(Dispatchers.IO) {
try {
app.contentResolver.openFileDescriptor(uri, "wt")?.use { parcelFileDescriptor ->

View File

@@ -134,8 +134,8 @@ class RadioInterfaceService @Inject constructor(
return interfaceFactory.toInterfaceAddress(interfaceId, rest)
}
val isMockInterface: Boolean by lazy {
BuildUtils.isEmulator || (context as GeeksvilleApplication).isInTestLab
fun isMockInterface(): Boolean {
return BuildUtils.isEmulator || (context as GeeksvilleApplication).isInTestLab
}
/** Return the device we are configured to use, or null for none
@@ -151,7 +151,7 @@ class RadioInterfaceService @Inject constructor(
var address = prefs.getString(DEVADDR_KEY, null)
// If we are running on the emulator we default to the mock interface, so we can have some data to show to the user
if (address == null && isMockInterface) {
if (address == null && isMockInterface()) {
address = mockInterfaceAddress
}

View File

@@ -19,9 +19,9 @@ package com.geeksville.mesh.ui
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -60,12 +60,12 @@ fun BatteryInfo(
modifier = Modifier.height(18.dp),
imageVector = ImageVector.vectorResource(id = image),
contentDescription = null,
tint = MaterialTheme.colors.onSurface,
tint = MaterialTheme.colorScheme.onSurface,
)
Text(
text = level,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
}

View File

@@ -22,7 +22,6 @@ import android.net.Uri
import android.os.RemoteException
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
@@ -31,20 +30,20 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Check
import androidx.compose.material.icons.twotone.Close
import androidx.compose.material.icons.twotone.ContentCopy
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -72,6 +71,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
@@ -103,11 +103,9 @@ import com.geeksville.mesh.ui.components.rememberDragDropState
import com.geeksville.mesh.ui.radioconfig.components.ChannelCard
import com.geeksville.mesh.ui.radioconfig.components.ChannelSelection
import com.geeksville.mesh.ui.radioconfig.components.EditChannelDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import kotlinx.coroutines.launch
import androidx.core.net.toUri
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
@@ -123,6 +121,9 @@ fun ChannelScreen(
val channels by viewModel.channels.collectAsStateWithLifecycle()
var channelSet by remember(channels) { mutableStateOf(channels) }
var showChannelEditor by rememberSaveable { mutableStateOf(false) }
var showSendDialog by remember { mutableStateOf(false) }
var showResetDialog by remember { mutableStateOf(false) }
var showScanDialog by remember { mutableStateOf(false) }
val isEditing = channelSet != channels || showChannelEditor
/* Holds selections made by the user for QR generation. */
@@ -174,23 +175,29 @@ fun ChannelScreen(
if (permissions.entries.all { it.value }) zxingScan()
}
fun requestPermissionAndScan() {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.camera_required)
.setMessage(R.string.why_camera_required)
.setNeutralButton(R.string.cancel) { _, _ ->
if (showScanDialog) {
AlertDialog(
onDismissRequest = {
debug("Camera permission denied")
showScanDialog = false
},
title = { Text(text = stringResource(id = R.string.camera_required)) },
text = { Text(text = stringResource(id = R.string.why_camera_required)) },
confirmButton = {
TextButton(onClick = { requestPermissionAndScanLauncher.launch(context.getCameraPermissions()) }) {
Text(text = stringResource(id = R.string.accept))
}
},
dismissButton = {
TextButton(onClick = { debug("Camera permission denied") }) {
Text(text = stringResource(id = R.string.cancel))
}
}
.setPositiveButton(R.string.accept) { _, _ ->
requestPermissionAndScanLauncher.launch(context.getCameraPermissions())
}
.show()
)
}
// Send new channel settings to the device
fun installSettings(
newChannelSet: ChannelSet
) {
fun installSettings(newChannelSet: ChannelSet) {
// Try to change the radio, if it fails, tell the user why and throw away their edits
try {
viewModel.setChannels(newChannelSet)
@@ -218,39 +225,53 @@ fun ChannelScreen(
installSettings(newSet)
}
fun resetButton() {
// User just locked it, we should warn and then apply changes to radio
MaterialAlertDialogBuilder(context)
.setTitle(R.string.reset_to_defaults)
.setMessage(R.string.are_you_sure_change_default)
.setNeutralButton(R.string.cancel) { _, _ ->
if (showResetDialog) {
AlertDialog(
onDismissRequest = {
channelSet = channels // throw away any edits
showResetDialog = false
},
title = { Text(text = stringResource(id = R.string.reset_to_defaults)) },
text = { Text(text = stringResource(id = R.string.are_you_sure_change_default)) },
confirmButton = {
TextButton(onClick = {
debug("Switching back to default channel")
installSettings(
Channel.default.settings,
Channel.default.loraConfig.copy {
region = viewModel.region
txEnabled = viewModel.txEnabled
}
)
showResetDialog = false
}) { Text(text = stringResource(id = R.string.apply)) }
},
dismissButton = {
TextButton(onClick = {
channelSet = channels // throw away any edits
showResetDialog = false
}) { Text(text = stringResource(id = R.string.cancel)) }
}
.setPositiveButton(R.string.apply) { _, _ ->
debug("Switching back to default channel")
installSettings(
Channel.default.settings,
Channel.default.loraConfig.copy {
region = viewModel.region
txEnabled = viewModel.txEnabled
}
)
}
.show()
)
}
fun sendButton() {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.change_channel)
.setMessage(R.string.are_you_sure_channel)
.setNeutralButton(R.string.cancel) { _, _ ->
if (showSendDialog) {
AlertDialog(
onDismissRequest = {
showSendDialog = false
showChannelEditor = false
channelSet = channels
}
.setPositiveButton(R.string.accept) { _, _ ->
},
title = { Text(text = stringResource(id = R.string.change_channel)) },
text = { Text(text = stringResource(id = R.string.are_you_sure_channel)) },
confirmButton = {
TextButton(onClick = {
installSettings(channelSet)
showSendDialog = false
}) { Text(text = stringResource(id = R.string.accept)) }
installSettings(channelSet)
}
.show()
)
}
var showEditChannelDialog: Int? by remember { mutableStateOf(null) }
@@ -309,9 +330,7 @@ fun ChannelScreen(
items = channelSet.settingsList,
dragDropState = dragDropState,
) { index, channel, isDragging ->
val elevation by animateDpAsState(if (isDragging) 8.dp else 4.dp, label = "drag")
ChannelCard(
elevation = elevation,
index = index,
title = channel.name.ifEmpty { modemPresetName },
enabled = enabled,
@@ -329,9 +348,6 @@ fun ChannelScreen(
showEditChannelDialog = channelSet.settingsList.lastIndex
},
enabled = enabled && viewModel.maxChannels > channelSet.settingsCount,
colors = ButtonDefaults.buttonColors(
disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
)
) { Text(text = stringResource(R.string.add)) }
}
}
@@ -361,7 +377,7 @@ fun ChannelScreen(
},
onSaveClicked = {
focusManager.clearFocus()
sendButton()
showSendDialog = true
}
)
} else {
@@ -370,12 +386,12 @@ fun ChannelScreen(
negativeText = R.string.reset,
onNegativeClicked = {
focusManager.clearFocus()
resetButton()
showResetDialog = true
},
positiveText = R.string.scan,
onPositiveClicked = {
focusManager.clearFocus()
if (context.hasCameraPermission()) zxingScan() else requestPermissionAndScan()
if (context.hasCameraPermission()) zxingScan() else showScanDialog = true
}
)
}
@@ -462,9 +478,9 @@ private fun EditChannelUrl(
else -> stringResource(R.string.copy)
},
tint = if (isError) {
MaterialTheme.colors.error
MaterialTheme.colorScheme.error
} else {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
LocalContentColor.current
}
)
}
@@ -490,7 +506,7 @@ private fun QrCodeImage(
contentDescription = stringResource(R.string.qr_code),
modifier = modifier,
contentScale = ContentScale.Inside,
alpha = if (enabled) 1.0f else ContentAlpha.disabled,
alpha = if (enabled) 1.0f else 0.7f
// colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }),
)
@@ -528,7 +544,7 @@ private fun ChannelListView(
modifier = Modifier.fillMaxWidth(),
enabled = enabled,
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.onSurface,
contentColor = MaterialTheme.colorScheme.onSurface,
),
) { Text(text = stringResource(R.string.edit)) }
},

View File

@@ -18,7 +18,6 @@
package com.geeksville.mesh.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@@ -29,15 +28,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Chip
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.twotone.VolumeOff
import androidx.compose.material3.AssistChip
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -53,7 +50,6 @@ import com.geeksville.mesh.model.Contact
import com.geeksville.mesh.ui.theme.AppTheme
@Suppress("LongMethod")
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
fun ContactItem(
contact: Contact,
@@ -63,87 +59,88 @@ fun ContactItem(
onLongClick: () -> Unit = {},
) = with(contact) {
Card(
modifier = Modifier
.background(color = if (selected) Color.Gray else MaterialTheme.colors.background)
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 6.dp),
elevation = 4.dp,
shape = RoundedCornerShape(12.dp),
) {
Surface(
modifier = modifier.combinedClickable(
modifier = modifier
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
),
)
.background(color = if (selected) Color.Gray else MaterialTheme.colorScheme.background)
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 6.dp),
shape = RoundedCornerShape(12.dp),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Row(
AssistChip(
onClick = { },
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Chip(
onClick = { },
modifier = Modifier
.padding(end = 8.dp)
.width(72.dp),
) {
.padding(end = 8.dp)
.width(72.dp),
label = {
Text(
text = shortName,
modifier = Modifier.fillMaxWidth(),
fontSize = MaterialTheme.typography.button.fontSize,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
fontWeight = FontWeight.Normal,
textAlign = TextAlign.Center,
)
}
Column(
modifier = Modifier.weight(1f),
)
Column(
modifier = Modifier.weight(1f),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = longName,
modifier = Modifier.weight(1f),
)
Text(
text = lastMessageTime.orEmpty(),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize,
Text(
text = longName,
modifier = Modifier.weight(1f),
)
Text(
text = lastMessageTime.orEmpty(),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = lastMessageText.orEmpty(),
modifier = Modifier.weight(1f),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
overflow = TextOverflow.Ellipsis,
maxLines = 2,
)
AnimatedVisibility(visible = isMuted) {
Icon(
imageVector = Icons.AutoMirrored.TwoTone.VolumeOff,
contentDescription = null,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
AnimatedVisibility(visible = unreadCount > 0) {
Text(
text = lastMessageText.orEmpty(),
modifier = Modifier.weight(1f),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize,
overflow = TextOverflow.Ellipsis,
maxLines = 2,
text = unreadCount.toString(),
modifier = Modifier
.background(
MaterialTheme.colorScheme.primary,
shape = CircleShape
)
.padding(horizontal = 6.dp, vertical = 3.dp),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.bodySmall,
)
AnimatedVisibility(visible = isMuted) {
Icon(
imageVector = Icons.AutoMirrored.TwoTone.VolumeOff,
contentDescription = null,
)
}
AnimatedVisibility(visible = unreadCount > 0) {
Text(
text = unreadCount.toString(),
modifier = Modifier
.background(MaterialTheme.colors.primary, shape = CircleShape)
.padding(horizontal = 6.dp, vertical = 3.dp),
color = MaterialTheme.colors.onPrimary,
style = MaterialTheme.typography.caption,
)
}
}
}
}

View File

@@ -26,21 +26,21 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.selection.selectable
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.RadioButton
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.twotone.VolumeMute
import androidx.compose.material.icons.automirrored.twotone.VolumeUp
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.SelectAll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -121,10 +121,10 @@ fun ContactsScreen(
selectedCount = selectedContactKeys.size,
onCloseSelection = { selectedContactKeys.clear() },
onMuteSelected = {
showMuteDialog = true
showMuteDialog = true
},
onDeleteSelected = {
showDeleteDialog = true
showDeleteDialog = true
},
onSelectAll = {
selectedContactKeys.clear()
@@ -165,7 +165,7 @@ fun ContactsScreen(
)
}
@OptIn(ExperimentalMaterialApi::class) // Required for AlertDialog in some cases, though often not strictly necessary now
@Suppress("LongMethod")
@Composable
fun MuteNotificationsDialog(
showDialog: Boolean,
@@ -240,7 +240,6 @@ fun MuteNotificationsDialog(
}
}
@OptIn(ExperimentalMaterialApi::class) // Not strictly needed for simple AlertDialog
@Composable
fun DeleteConfirmationDialog(
showDialog: Boolean,
@@ -288,6 +287,7 @@ fun DeleteConfirmationDialog(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SelectionToolbar(
selectedCount: Int,

View File

@@ -26,15 +26,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
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.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CloudDownload
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@@ -80,17 +78,15 @@ internal fun DebugScreen(
}
}
SelectionContainer {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState,
) {
items(logs, key = { it.uuid }) { log ->
DebugItem(
modifier = Modifier.animateItem(),
log = log
)
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState,
) {
items(logs, key = { it.uuid }) { log ->
DebugItem(
modifier = Modifier.animateItem(),
log = log
)
}
}
}
@@ -104,10 +100,8 @@ internal fun DebugItem(
modifier = modifier
.fillMaxWidth()
.padding(4.dp),
elevation = 4.dp,
shape = RoundedCornerShape(12.dp),
) {
Surface {
SelectionContainer {
Column(
modifier = Modifier.padding(8.dp)
) {

View File

@@ -20,9 +20,9 @@ package com.geeksville.mesh.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -50,12 +50,10 @@ fun LastHeardInfo(
modifier = Modifier.height(18.dp),
imageVector = ImageVector.vectorResource(id = R.drawable.ic_antenna_24),
contentDescription = null,
tint = MaterialTheme.colors.onSurface,
)
Text(
text = formatAgo(lastHeard, currentTimeMillis),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
}

View File

@@ -21,8 +21,8 @@ import android.content.ActivityNotFoundException
import android.content.ClipData
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -57,7 +57,7 @@ fun LinkedCoordinates(
val uriHandler = LocalUriHandler.current
val style = SpanStyle(
color = HyperlinkBlue,
fontSize = MaterialTheme.typography.button.fontSize,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
textDecoration = TextDecoration.Underline
)
val annotatedString = buildAnnotatedString {

View File

@@ -25,17 +25,6 @@ import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.SnackbarHost
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.twotone.Chat
@@ -47,6 +36,18 @@ import androidx.compose.material.icons.twotone.Contactless
import androidx.compose.material.icons.twotone.Map
import androidx.compose.material.icons.twotone.People
import androidx.compose.material.icons.twotone.Settings
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -74,7 +75,9 @@ import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.showLongNameTitle
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
import com.geeksville.mesh.ui.components.MultipleChoiceAlertDialog
import com.geeksville.mesh.ui.components.ScannedQrCodeDialog
import com.geeksville.mesh.ui.components.SimpleAlertDialog
enum class TopLevelDestination(val label: String, val icon: ImageVector, val route: Route) {
Contacts("Contacts", Icons.AutoMirrored.TwoTone.Chat, Route.Contacts),
@@ -92,6 +95,7 @@ enum class TopLevelDestination(val label: String, val icon: ImageVector, val rou
}
}
@Suppress("LongMethod")
@Composable
fun MainScreen(
viewModel: UIViewModel = hiltViewModel(),
@@ -109,6 +113,26 @@ fun MainScreen(
}
val title by viewModel.title.collectAsStateWithLifecycle()
val alertDialogState by viewModel.currentAlert.collectAsStateWithLifecycle()
alertDialogState?.let { state ->
if (state.choices.isNotEmpty()) {
MultipleChoiceAlertDialog(
title = state.title,
message = state.message,
choices = state.choices,
onDismissRequest = { state.onDismiss?.let { it() } },
)
} else {
SimpleAlertDialog(
title = state.title,
message = state.message,
html = state.html,
onConfirmRequest = { state.onConfirm?.let { it() } },
onDismissRequest = { state.onDismiss?.let { it() } },
)
}
}
Scaffold(
topBar = {
MainAppBar(
@@ -151,6 +175,7 @@ enum class MainMenuAction(@StringRes val stringRes: Int) {
ABOUT(R.string.about),
}
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("LongMethod")
@Composable
private fun MainAppBar(
@@ -271,10 +296,11 @@ private fun MainMenuActions(
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
modifier = Modifier.background(MaterialTheme.colors.background.copy(alpha = 1f)),
modifier = Modifier.background(MaterialTheme.colorScheme.background.copy(alpha = 1f)),
) {
MainMenuAction.entries.forEach { action ->
DropdownMenuItem(
text = { Text(stringResource(id = action.stringRes)) },
onClick = {
onAction(action)
showMenu = false
@@ -283,7 +309,7 @@ private fun MainMenuActions(
MainMenuAction.RADIO_CONFIG -> !isManaged
else -> true
},
) { Text(stringResource(id = action.stringRes)) }
)
}
}
}
@@ -306,10 +332,10 @@ private fun BottomNavigation(
animationSpec = tween(durationMillis = 200),
),
) {
BottomNavigation {
NavigationBar {
TopLevelDestination.entries.forEach {
val isSelected = it == topLevelDestination
BottomNavigationItem(
NavigationBarItem(
icon = {
Icon(
imageVector = it.icon,

View File

@@ -28,20 +28,11 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Air
import androidx.compose.material.icons.filled.BlurOn
@@ -69,6 +60,11 @@ import androidx.compose.material.icons.filled.Verified
import androidx.compose.material.icons.filled.WaterDrop
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Navigation
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@@ -240,7 +236,7 @@ private fun NodeDetailRow(
label: String,
icon: ImageVector,
value: String,
iconTint: Color = MaterialTheme.colors.onSurface
iconTint: Color = MaterialTheme.colorScheme.onSurface
) {
Row(
modifier = Modifier
@@ -336,15 +332,14 @@ private fun NodeDetailsContent(
Spacer(Modifier.width(12.dp))
Text(
text = stringResource(id = R.string.encryption_error),
style = MaterialTheme.typography.h6.copy(color = Color.Red),
style = MaterialTheme.typography.titleLarge.copy(color = Color.Red),
textAlign = TextAlign.Center,
)
}
Spacer(Modifier.height(16.dp))
Text(
text = stringResource(id = R.string.encryption_error_text),
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(16.dp))
@@ -393,41 +388,43 @@ private fun InfoCard(
rotateIcon: Float = 0f,
) {
Card(
shape = RoundedCornerShape(12.dp),
backgroundColor = MaterialTheme.colors.surface,
elevation = 4.dp,
modifier = Modifier
.padding(4.dp)
.widthIn(min = 100.dp, max = 150.dp)
.heightIn(min = 100.dp, max = 150.dp)
.width(100.dp)
.height(100.dp),
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
Box(
modifier = Modifier
.padding(4.dp)
.width(100.dp)
.height(100.dp),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = icon,
contentDescription = text,
modifier = Modifier
.size(24.dp)
.thenIf(rotateIcon != 0f) { rotate(rotateIcon) },
)
Text(
text = text,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.subtitle2
)
Text(
text = value,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = if (value.length < 7) {
MaterialTheme.typography.h5
} else {
MaterialTheme.typography.h6
},
)
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = icon,
contentDescription = text,
modifier = Modifier
.size(24.dp)
.thenIf(rotateIcon != 0f) { rotate(rotateIcon) },
)
Text(
textAlign = TextAlign.Center,
text = text,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.labelSmall
)
Text(
text = value,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleMedium,
)
}
}
}
}
@@ -440,7 +437,7 @@ private fun EnvironmentMetrics(
) = with(node.environmentMetrics) {
FlowRow(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
horizontalArrangement = Arrangement.SpaceEvenly,
verticalArrangement = Arrangement.SpaceEvenly,
) {
if (hasTemperature()) {

View File

@@ -30,15 +30,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.Card
import androidx.compose.material.Chip
import androidx.compose.material.ChipDefaults
import androidx.compose.material.Divider
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.AssistChip
import androidx.compose.material3.AssistChipDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -71,7 +69,6 @@ import com.geeksville.mesh.ui.theme.AppTheme
import com.geeksville.mesh.util.toDistanceString
@Suppress("LongMethod", "CyclomaticComplexMethod")
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun NodeItem(
thisNode: Node?,
@@ -119,173 +116,171 @@ fun NodeItem(
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 4.dp)
.defaultMinSize(minHeight = 80.dp),
elevation = 4.dp,
onClick = { showDetails(!detailsShown) },
) {
Surface {
Column(
Column(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
var menuExpanded by remember { mutableStateOf(false) }
Box(
modifier = Modifier.wrapContentSize(Alignment.TopStart),
) {
var menuExpanded by remember { mutableStateOf(false) }
Box(
modifier = Modifier.wrapContentSize(Alignment.TopStart),
) {
Chip(
modifier = Modifier
.width(IntrinsicSize.Min)
.defaultMinSize(minHeight = 32.dp, minWidth = 72.dp),
colors = ChipDefaults.chipColors(
backgroundColor = Color(nodeColor),
contentColor = Color(textColor),
),
onClick = {
menuExpanded = !menuExpanded
},
) {
AssistChip(
modifier = Modifier
.width(IntrinsicSize.Min)
.defaultMinSize(minHeight = 32.dp, minWidth = 72.dp),
colors = AssistChipDefaults.assistChipColors(
containerColor = Color(nodeColor),
labelColor = Color(textColor),
),
label = {
Text(
modifier = Modifier.fillMaxWidth(),
text = thatNode.user.shortName.ifEmpty { "???" },
fontWeight = if (isFavorite) FontWeight.Bold else FontWeight.Normal,
fontSize = MaterialTheme.typography.button.fontSize,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
textAlign = TextAlign.Center,
)
}
NodeMenu(
node = thatNode,
showFullMenu = !isThisNode && isConnected,
onAction = onAction,
expanded = menuExpanded,
onDismissRequest = { menuExpanded = false },
)
}
NodeKeyStatusIcon(
hasPKC = thatNode.hasPKC,
mismatchKey = thatNode.mismatchKey,
publicKey = thatNode.user.publicKey,
modifier = Modifier.size(32.dp)
},
onClick = {
menuExpanded = !menuExpanded
},
)
Text(
modifier = Modifier.weight(1f),
text = longName,
fontWeight = if (isFavorite) FontWeight.Bold else FontWeight.Normal,
style = style,
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
softWrap = true,
)
LastHeardInfo(
lastHeard = thatNode.lastHeard,
currentTimeMillis = currentTimeMillis
NodeMenu(
node = thatNode,
showFullMenu = !isThisNode && isConnected,
onAction = onAction,
expanded = menuExpanded,
onDismissRequest = { menuExpanded = false },
)
}
NodeKeyStatusIcon(
hasPKC = thatNode.hasPKC,
mismatchKey = thatNode.mismatchKey,
publicKey = thatNode.user.publicKey,
modifier = Modifier.size(32.dp)
)
Text(
modifier = Modifier.weight(1f),
text = longName,
fontWeight = if (isFavorite) FontWeight.Bold else FontWeight.Normal,
style = style,
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
softWrap = true,
)
LastHeardInfo(
lastHeard = thatNode.lastHeard,
currentTimeMillis = currentTimeMillis
)
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
if (distance != null) {
Text(
text = distance,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
} else {
Spacer(modifier = Modifier.width(16.dp))
}
BatteryInfo(
batteryLevel = thatNode.batteryLevel,
voltage = thatNode.voltage
)
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
SignalInfo(
node = thatNode,
isThisNode = isThisNode
)
thatNode.validPosition?.let { position ->
val satCount = position.satsInView
if (satCount > 0) {
SatelliteCountInfo(satCount = satCount)
}
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
val telemetryString = thatNode.getTelemetryString(tempInFahrenheit)
if (telemetryString.isNotEmpty()) {
Text(
text = telemetryString,
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
}
}
if (detailsShown || expanded) {
Spacer(modifier = Modifier.height(8.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
if (distance != null) {
Text(
text = distance,
fontSize = MaterialTheme.typography.button.fontSize,
thatNode.validPosition?.let {
LinkedCoordinates(
latitude = thatNode.latitude,
longitude = thatNode.longitude,
format = gpsFormat,
nodeName = longName
)
} else {
Spacer(modifier = Modifier.width(16.dp))
}
BatteryInfo(
batteryLevel = thatNode.batteryLevel,
voltage = thatNode.voltage
)
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
SignalInfo(
node = thatNode,
isThisNode = isThisNode
)
thatNode.validPosition?.let { position ->
val satCount = position.satsInView
if (satCount > 0) {
SatelliteCountInfo(satCount = satCount)
}
ElevationInfo(
altitude = position.altitude,
system = system,
suffix = stringResource(id = R.string.elevation_suffix)
)
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
val telemetryString = thatNode.getTelemetryString(tempInFahrenheit)
if (telemetryString.isNotEmpty()) {
Text(
text = telemetryString,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize,
)
}
}
if (detailsShown || expanded) {
Spacer(modifier = Modifier.height(8.dp))
Divider()
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
thatNode.validPosition?.let {
LinkedCoordinates(
latitude = thatNode.latitude,
longitude = thatNode.longitude,
format = gpsFormat,
nodeName = longName
)
}
thatNode.validPosition?.let { position ->
ElevationInfo(
altitude = position.altitude,
system = system,
suffix = stringResource(id = R.string.elevation_suffix)
)
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
) {
Text(
modifier = Modifier.weight(1f),
text = hwInfoString,
fontSize = MaterialTheme.typography.button.fontSize,
style = style,
)
Text(
modifier = Modifier.weight(1f),
text = roleName,
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.button.fontSize,
style = style,
)
Text(
modifier = Modifier.weight(1f),
text = thatNode.user.id.ifEmpty { "???" },
textAlign = TextAlign.End,
fontSize = MaterialTheme.typography.button.fontSize,
style = style,
)
}
Text(
modifier = Modifier.weight(1f),
text = hwInfoString,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
style = style,
)
Text(
modifier = Modifier.weight(1f),
text = roleName,
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
style = style,
)
Text(
modifier = Modifier.weight(1f),
text = thatNode.user.id.ifEmpty { "???" },
textAlign = TextAlign.End,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
style = style,
)
}
}
}
@@ -323,7 +318,7 @@ fun NodeInfoPreview(
Column {
Text(
text = "Details Collapsed",
color = MaterialTheme.colors.onBackground
color = MaterialTheme.colorScheme.onBackground
)
NodeItem(
thisNode = thisNode,
@@ -336,7 +331,7 @@ fun NodeInfoPreview(
)
Text(
text = "Details Shown",
color = MaterialTheme.colors.onBackground
color = MaterialTheme.colorScheme.onBackground
)
NodeItem(
thisNode = thisNode,

View File

@@ -26,7 +26,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
@@ -65,7 +65,7 @@ fun NodeScreen(
NodeFilterTextField(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.background)
.background(MaterialTheme.colorScheme.background)
.padding(8.dp),
filterText = state.filter,
onTextChange = model::setNodeFilterText,

View File

@@ -17,7 +17,6 @@
package com.geeksville.mesh.ui
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -35,24 +34,23 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.ListItem
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.DragHandle
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.FastForward
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -69,7 +67,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
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.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -132,12 +129,7 @@ internal fun QuickChatScreen(
dragDropState = dragDropState,
key = { _, item -> item.uuid },
) { _, action, isDragging ->
val elevation by animateDpAsState(
targetValue = if (isDragging) 8.dp else 4.dp,
label = "DragAndDropElevationAnimation",
)
QuickChatItem(
elevation = elevation,
action = action,
onEdit = { showActionDialog = it },
)
@@ -180,116 +172,116 @@ private fun EditQuickChatDialog(
AlertDialog(
onDismissRequest = onDismiss,
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
text = {
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = stringResource(id = title),
modifier = Modifier.fillMaxWidth(),
style = MaterialTheme.typography.h6.copy(
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
),
)
text =
{
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = stringResource(id = title),
modifier = Modifier.fillMaxWidth(),
style = MaterialTheme.typography.titleLarge.copy(
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
),
)
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextFieldWithCounter(
label = stringResource(R.string.name),
value = actionInput.name,
maxSize = 5,
singleLine = true,
modifier = Modifier.fillMaxWidth(),
) { actionInput = actionInput.copy(name = it.uppercase()) }
OutlinedTextFieldWithCounter(
label = stringResource(R.string.name),
value = actionInput.name,
maxSize = 5,
singleLine = true,
modifier = Modifier.fillMaxWidth(),
) { actionInput = actionInput.copy(name = it.uppercase()) }
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextFieldWithCounter(
label = stringResource(id = R.string.message),
value = actionInput.message,
maxSize = 200,
getSize = { it.toByteArray().size + 1 },
OutlinedTextFieldWithCounter(
label = stringResource(id = R.string.message),
value = actionInput.message,
maxSize = 200,
getSize = { it.toByteArray().size + 1 },
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
) {
actionInput = actionInput.copy(message = it)
if (newQuickChat) {
actionInput = actionInput.copy(name = getMessageName(it))
}
}
Spacer(modifier = Modifier.height(8.dp))
val (text, icon) = if (isInstant) {
R.string.quick_chat_instant to Icons.Default.FastForward
} else {
R.string.quick_chat_append to Icons.Default.Add
}
Row(
verticalAlignment = Alignment.CenterVertically,
) {
if (isInstant) {
Icon(
imageVector = icon,
contentDescription = stringResource(id = text),
)
Spacer(Modifier.width(12.dp))
}
Text(
text = stringResource(text),
modifier = Modifier.weight(1f),
)
Switch(
checked = isInstant,
onCheckedChange = { checked ->
actionInput = actionInput.copy(
mode = when (checked) {
true -> QuickChatAction.Mode.Instant
false -> QuickChatAction.Mode.Append
}
)
},
)
}
}
},
confirmButton =
{
FlowRow(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
.padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
actionInput = actionInput.copy(message = it)
if (newQuickChat) {
actionInput = actionInput.copy(name = getMessageName(it))
}
}
Spacer(modifier = Modifier.height(8.dp))
val (text, icon) = if (isInstant) {
R.string.quick_chat_instant to Icons.Default.FastForward
} else {
R.string.quick_chat_append to Icons.Default.Add
}
Row(
verticalAlignment = Alignment.CenterVertically,
) {
if (isInstant) {
Icon(
imageVector = icon,
contentDescription = stringResource(id = text),
)
Spacer(Modifier.width(12.dp))
}
Text(
text = stringResource(text),
TextButton(
modifier = Modifier.weight(1f),
)
onClick = onDismiss,
) { Text(stringResource(R.string.cancel)) }
Switch(
checked = isInstant,
onCheckedChange = { checked ->
actionInput = actionInput.copy(
mode = when (checked) {
true -> QuickChatAction.Mode.Instant
false -> QuickChatAction.Mode.Append
}
)
},
)
}
}
},
buttons = {
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TextButton(
modifier = Modifier.weight(1f),
onClick = onDismiss,
) { Text(stringResource(R.string.cancel)) }
if (!newQuickChat) {
Button(
modifier = Modifier.weight(1f),
onClick = {
onDelete(actionInput)
onDismiss()
},
) { Text(text = stringResource(R.string.delete)) }
}
if (!newQuickChat) {
Button(
modifier = Modifier.weight(1f),
onClick = {
onDelete(actionInput)
onSave(actionInput)
onDismiss()
},
) { Text(text = stringResource(R.string.delete)) }
enabled = actionInput.name.isNotEmpty() && actionInput.message.isNotEmpty(),
) { Text(text = stringResource(R.string.save)) }
}
Button(
modifier = Modifier.weight(1f),
onClick = {
onSave(actionInput)
onDismiss()
},
enabled = actionInput.name.isNotEmpty() && actionInput.message.isNotEmpty(),
) { Text(text = stringResource(R.string.save)) }
}
},
},
)
}
@@ -318,7 +310,7 @@ private fun OutlinedTextFieldWithCounter(
if (isFocused) {
Text(
text = "${getSize(value)}/$maxSize",
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier
.align(Alignment.End)
.padding(top = 4.dp, end = 16.dp)
@@ -326,23 +318,20 @@ private fun OutlinedTextFieldWithCounter(
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun QuickChatItem(
action: QuickChatAction,
modifier: Modifier = Modifier,
onEdit: (QuickChatAction) -> Unit = {},
elevation: Dp = 4.dp,
) {
Card(
modifier = modifier
.fillMaxWidth()
.padding(8.dp),
elevation = elevation,
shape = RoundedCornerShape(12.dp),
) {
ListItem(
icon = {
leadingContent = {
if (action.mode == QuickChatAction.Mode.Instant) {
Icon(
imageVector = Icons.Default.FastForward,
@@ -350,9 +339,9 @@ private fun QuickChatItem(
)
}
},
text = { Text(text = action.name) },
secondaryText = { Text(text = action.message) },
trailing = {
headlineContent = { Text(text = action.name) },
supportingContent = { Text(text = action.message) },
trailingContent = {
Row(
verticalAlignment = Alignment.CenterVertically,
) {

View File

@@ -39,20 +39,20 @@ import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.Checkbox
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.RadioButton
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -250,7 +250,7 @@ fun SettingsScreen(
// Device List and Manual Input
Text(
text = stringResource(R.string.device),
style = MaterialTheme.typography.h6,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(vertical = 8.dp)
)
@@ -269,10 +269,6 @@ fun SettingsScreen(
.selectable(
selected = (device.fullAddress == selectedDevice),
onClick = {
if (device.fullAddress == "n") {
uiViewModel.showSnackbar("Demo Mode enabled")
scanModel.showMockInterface()
}
if (!device.bonded) {
uiViewModel.showSnackbar(context.getString(R.string.starting_pairing))
}
@@ -285,10 +281,6 @@ fun SettingsScreen(
RadioButton(
selected = (device.fullAddress == selectedDevice),
onClick = {
if (device.fullAddress == "n") {
uiViewModel.showSnackbar("Demo Mode enabled")
scanModel.showMockInterface()
}
if (!device.bonded) {
uiViewModel.showSnackbar(context.getString(R.string.starting_pairing))
}
@@ -297,7 +289,7 @@ fun SettingsScreen(
)
Text(
text = device.name,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(start = 16.dp)
)
}
@@ -394,7 +386,7 @@ fun SettingsScreen(
)
Text(
text = stringResource(R.string.provide_location_to_mesh),
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(start = 16.dp)
)
}
@@ -406,8 +398,8 @@ fun SettingsScreen(
if (showWarningNotPaired) {
Text(
text = stringResource(R.string.warning_not_paired),
color = MaterialTheme.colors.error,
style = MaterialTheme.typography.body2,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(horizontal = 16.dp)
)
Spacer(modifier = Modifier.height(16.dp))
@@ -438,7 +430,7 @@ fun SettingsScreen(
)
Text(
text = stringResource(R.string.analytics_okay),
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(start = 16.dp)
)
}
@@ -494,7 +486,7 @@ fun SettingsScreen(
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Select a Bluetooth device",
style = MaterialTheme.typography.h6,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 16.dp)
)
Column(modifier = Modifier.selectableGroup()) {

View File

@@ -23,10 +23,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.R
@Composable
fun SimpleAlertDialog(
title: String,
message: String?,
html: String? = null,
onDismissRequest: () -> Unit,
onConfirmRequest: () -> Unit = onDismissRequest // Default confirm to dismiss
) {
val annotatedString = html?.let {
AnnotatedString.fromHtml(
html,
linkStyles = TextLinkStyles(
style = SpanStyle(
textDecoration = TextDecoration.Underline,
fontStyle = FontStyle.Italic,
color = Color.Blue
)
)
)
}
AlertDialog(
onDismissRequest = onDismissRequest,
title = { Text(text = title) },
text = {
if (annotatedString != null) {
Text(
text = annotatedString,
)
} else {
Text(
text = message.orEmpty()
)
}
},
confirmButton = {
TextButton(onClick = onConfirmRequest) {
Text(stringResource(id = R.string.okay))
}
},
)
}
// For Rationale Dialogs
@Composable
fun MultipleChoiceAlertDialog(
title: String,
message: String?,
choices: Map<String, () -> Unit>,
onDismissRequest: () -> Unit,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
title = { Text(text = title) },
text = {
Column(
modifier = Modifier.verticalScroll(rememberScrollState())
) {
message?.let {
Text(
text = it,
modifier = Modifier.padding(bottom = 8.dp)
)
}
choices.forEach { (choice, action) ->
Button(
modifier = Modifier.fillMaxWidth().padding(8.dp),
onClick = {
action()
onDismissRequest()
},
) {
Text(text = choice)
}
}
}
},
confirmButton = {
}
)
}

View File

@@ -21,7 +21,7 @@ import android.text.Spannable
import android.text.Spannable.Factory
import android.text.style.URLSpan
import android.text.util.Linkify
import androidx.compose.material.Text
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier

View File

@@ -20,13 +20,13 @@ package com.geeksville.mesh.ui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.Checkbox
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.KeyboardArrowDown
import androidx.compose.material.icons.twotone.KeyboardArrowUp
import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -70,7 +70,7 @@ fun BitwisePreference(
DropdownMenuItem(
onClick = { onItemSelected(value xor item.first) },
modifier = modifier.fillMaxWidth(),
content = {
text = {
Text(
text = item.second,
overflow = TextOverflow.Ellipsis,

View File

@@ -26,7 +26,7 @@ import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -59,7 +59,7 @@ fun BottomSheetDialog(
modifier = modifier
.align(Alignment.BottomCenter)
.background(
color = MaterialTheme.colors.surface.copy(alpha = 1f),
color = MaterialTheme.colorScheme.surface.copy(alpha = 1f),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
)
.padding(16.dp),

View File

@@ -20,9 +20,9 @@ package com.geeksville.mesh.ui.components
import androidx.annotation.StringRes
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.material.Icon
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember

View File

@@ -31,13 +31,13 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -89,7 +89,7 @@ fun ChartHeader(amount: Int) {
text = "$amount ${stringResource(R.string.logs)}",
modifier = Modifier.wrapContentWidth(),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
}
@@ -185,7 +185,7 @@ fun TimeAxisOverlay(
val range = newest - oldest
val density = LocalDensity.current
val lineColor = MaterialTheme.colors.onSurface
val lineColor = MaterialTheme.colorScheme.onSurface
Canvas(modifier = modifier) {
val height = size.height
@@ -346,7 +346,6 @@ fun LegendInfoDialog(pairedRes: List<Pair<Int, Int>>, onDismiss: () -> Unit) {
}
},
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background
)
}
@@ -372,8 +371,8 @@ private fun LegendLabel(text: String, color: Color, isLine: Boolean = false) {
Spacer(modifier = Modifier.width(4.dp))
Text(
text = text,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize,
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
}

View File

@@ -18,10 +18,10 @@
package com.geeksville.mesh.ui.components
import android.content.ClipData
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.ContentCopy
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier

View File

@@ -34,10 +34,10 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -62,13 +62,13 @@ import com.geeksville.mesh.TelemetryProtos.Telemetry
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.TimeFrame
import com.geeksville.mesh.ui.BatteryInfo
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
import com.geeksville.mesh.ui.components.CommonCharts.MAX_PERCENT_VALUE
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.ui.theme.Orange
import com.geeksville.mesh.util.GraphUtil
import com.geeksville.mesh.util.GraphUtil.plotPoint
import com.geeksville.mesh.util.GraphUtil.createPath
import com.geeksville.mesh.util.GraphUtil.plotPoint
private enum class Device(val color: Color) {
BATTERY(Color.Green),
@@ -155,7 +155,7 @@ private fun DeviceMetricsChart(
Spacer(modifier = Modifier.height(16.dp))
val graphColor = MaterialTheme.colors.onSurface
val graphColor = MaterialTheme.colorScheme.onSurface
val scrollState = rememberScrollState()
val screenWidth = LocalConfiguration.current.screenWidthDp
@@ -287,7 +287,7 @@ private fun DeviceMetricsCard(telemetry: Telemetry) {
Text(
text = DATE_TIME_FORMAT.format(time),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
BatteryInfo(
@@ -309,8 +309,8 @@ private fun DeviceMetricsCard(telemetry: Telemetry) {
)
Text(
text = text,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
}

View File

@@ -19,13 +19,13 @@ package com.geeksville.mesh.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.KeyboardArrowDown
import androidx.compose.material.icons.twotone.KeyboardArrowUp
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -116,12 +116,12 @@ fun <T> DropDownPreference(
modifier = modifier
.background(
color = if (selectedItem == item.first) {
MaterialTheme.colors.primary.copy(alpha = 0.3f)
MaterialTheme.colorScheme.primary.copy(alpha = 0.3f)
} else {
Color.Unspecified
},
),
content = {
text = {
Text(
text = item.second,
overflow = TextOverflow.Ellipsis,

View File

@@ -21,16 +21,15 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Close
import androidx.compose.material.icons.twotone.Refresh
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -115,9 +114,9 @@ fun EditBase64Preference(
imageVector = icon,
contentDescription = description,
tint = if (isError) {
MaterialTheme.colors.error
MaterialTheme.colorScheme.error
} else {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
LocalContentColor.current
}
)
}

View File

@@ -23,20 +23,17 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
@@ -67,12 +64,7 @@ inline fun <reified T> EditListPreference(
Text(
modifier = modifier.padding(16.dp),
text = title,
style = MaterialTheme.typography.body2,
color = if (enabled) {
Color.Unspecified
} else {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
},
style = MaterialTheme.typography.bodyMedium,
)
listState.forEachIndexed { index, value ->
val trailingIcon = @Composable {
@@ -176,9 +168,6 @@ inline fun <reified T> EditListPreference(
listState.add(listState.size, newElement)
},
enabled = maxCount > listState.size,
colors = ButtonDefaults.buttonColors(
disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
)
) { Text(text = stringResource(R.string.add)) }
}
}

View File

@@ -19,10 +19,10 @@ package com.geeksville.mesh.ui.components
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.VisibilityOff
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf

View File

@@ -23,12 +23,12 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Info
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -224,7 +224,7 @@ fun EditTextPreference(
Icon(
imageVector = Icons.TwoTone.Info,
contentDescription = stringResource(id = R.string.error),
tint = MaterialTheme.colors.error
tint = MaterialTheme.colorScheme.error
)
}
},
@@ -237,8 +237,8 @@ fun EditTextPreference(
) {
Text(
text = "${value.toByteArray().size}/$maxSize",
style = MaterialTheme.typography.caption,
color = if (isError) MaterialTheme.colors.error else MaterialTheme.colors.onBackground,
style = MaterialTheme.typography.bodySmall,
color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(end = 8.dp, bottom = 4.dp)
)
}

View File

@@ -23,7 +23,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
@@ -56,7 +56,7 @@ fun EmojiPicker(
},
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.background)
.background(MaterialTheme.colorScheme.background)
)
}
}

View File

@@ -35,10 +35,10 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -62,8 +62,8 @@ import com.geeksville.mesh.model.Environment
import com.geeksville.mesh.model.EnvironmentGraphingData
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.TimeFrame
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.util.GraphUtil.createPath
import com.geeksville.mesh.util.GraphUtil.drawPathWithGradient
import com.geeksville.mesh.util.UnitConversions.celsiusToFahrenheit
@@ -185,7 +185,7 @@ private fun EnvironmentMetricsChart(
Spacer(modifier = Modifier.height(16.dp))
val graphColor = MaterialTheme.colors.onSurface
val graphColor = MaterialTheme.colorScheme.onSurface
val (rightMin, rightMax) = graphData.rightMinMax
val (pressureMin, pressureMax) = graphData.leftMinMax
@@ -314,7 +314,7 @@ private fun EnvironmentMetricsCard(telemetry: Telemetry, environmentDisplayFahre
Text(
text = DATE_TIME_FORMAT.format(time),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
val textFormat = if (environmentDisplayFahrenheit) "%s %.1f°F" else "%s %.1f°C"
Text(
@@ -322,8 +322,8 @@ private fun EnvironmentMetricsCard(telemetry: Telemetry, environmentDisplayFahre
stringResource(id = R.string.temperature),
envMetrics.temperature
),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
@@ -339,14 +339,14 @@ private fun EnvironmentMetricsCard(telemetry: Telemetry, environmentDisplayFahre
stringResource(id = R.string.humidity),
envMetrics.relativeHumidity,
),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
if (envMetrics.barometricPressure > 0) {
Text(
text = "%.2f hPa".format(envMetrics.barometricPressure),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
}
@@ -360,8 +360,8 @@ private fun EnvironmentMetricsCard(telemetry: Telemetry, environmentDisplayFahre
) {
Text(
text = stringResource(R.string.iaq),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
Spacer(modifier = Modifier.width(4.dp))
IndoorAirQuality(

View File

@@ -31,16 +31,16 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ThumbUp
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -191,7 +191,6 @@ fun IndoorAirQuality(iaq: Int, displayMode: IaqDisplayMode = IaqDisplayMode.Pill
AlertDialog(
onDismissRequest = { isLegendOpen = false },
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
text = {
IAQScale()
},
@@ -218,7 +217,7 @@ fun IAQScale(modifier: Modifier = Modifier) {
) {
Text(
text = stringResource(R.string.indoor_air_quality_iaq),
style = MaterialTheme.typography.h6.copy(
style = MaterialTheme.typography.titleLarge.copy(
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
),
@@ -234,7 +233,7 @@ fun IAQScale(modifier: Modifier = Modifier) {
.background(iaq.color)
)
Spacer(modifier = Modifier.width(8.dp))
Text(getIaqDescriptionWithRange(iaq), style = MaterialTheme.typography.body2)
Text(getIaqDescriptionWithRange(iaq), style = MaterialTheme.typography.bodyMedium)
}
Spacer(modifier = Modifier.height(4.dp))
}
@@ -256,7 +255,7 @@ private fun IndoorAirQualityPreview() {
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text("Pill", style = MaterialTheme.typography.h6)
Text("Pill", style = MaterialTheme.typography.titleLarge)
Row {
IndoorAirQuality(iaq = 6)
IndoorAirQuality(iaq = 51)
@@ -270,7 +269,7 @@ private fun IndoorAirQualityPreview() {
IndoorAirQuality(iaq = 351)
}
Text("Dot", style = MaterialTheme.typography.h6)
Text("Dot", style = MaterialTheme.typography.titleLarge)
Row {
IndoorAirQuality(iaq = 6, displayMode = IaqDisplayMode.Dot)
IndoorAirQuality(iaq = 51, displayMode = IaqDisplayMode.Dot)
@@ -280,7 +279,7 @@ private fun IndoorAirQualityPreview() {
IndoorAirQuality(iaq = 351, displayMode = IaqDisplayMode.Dot)
}
Text("Text", style = MaterialTheme.typography.h6)
Text("Text", style = MaterialTheme.typography.titleLarge)
Row {
IndoorAirQuality(iaq = 6, displayMode = IaqDisplayMode.Text)
IndoorAirQuality(iaq = 51, displayMode = IaqDisplayMode.Text)
@@ -292,7 +291,7 @@ private fun IndoorAirQualityPreview() {
IndoorAirQuality(iaq = 500, displayMode = IaqDisplayMode.Text)
}
Text("Gauge", style = MaterialTheme.typography.h6)
Text("Gauge", style = MaterialTheme.typography.titleLarge)
Row {
IndoorAirQuality(iaq = 6, displayMode = IaqDisplayMode.Gauge)
IndoorAirQuality(iaq = 51, displayMode = IaqDisplayMode.Gauge)
@@ -310,7 +309,7 @@ private fun IndoorAirQualityPreview() {
IndoorAirQuality(iaq = 500, displayMode = IaqDisplayMode.Gauge)
}
Text("Gradient", style = MaterialTheme.typography.h6)
Text("Gradient", style = MaterialTheme.typography.titleLarge)
IndoorAirQuality(iaq = 6, displayMode = IaqDisplayMode.Gradient)
IndoorAirQuality(iaq = 51, displayMode = IaqDisplayMode.Gradient)
IndoorAirQuality(iaq = 101, displayMode = IaqDisplayMode.Gradient)

View File

@@ -18,7 +18,6 @@ package com.geeksville.mesh.ui.components
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.foundation.gestures.scrollBy
@@ -35,8 +34,8 @@ import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Card
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -88,8 +87,7 @@ fun LazyColumnDragAndDropDemo() {
itemsIndexed(list, key = { _, item -> item }) { index, item ->
DraggableItem(dragDropState, index + 1) { isDragging ->
val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp)
Card(elevation = elevation) {
Card {
Text("Item $item", Modifier.fillMaxWidth().padding(20.dp))
}
}

View File

@@ -27,14 +27,14 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.SignalCellular4Bar
import androidx.compose.material.icons.filled.SignalCellularAlt
import androidx.compose.material.icons.filled.SignalCellularAlt1Bar
import androidx.compose.material.icons.filled.SignalCellularAlt2Bar
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -79,7 +79,7 @@ fun NodeSignalQuality(snr: Float, rssi: Int, modifier: Modifier = Modifier) {
Spacer(Modifier.width(8.dp))
Text(
text = "${stringResource(R.string.signal)} ${stringResource(quality.nameRes)}",
fontSize = MaterialTheme.typography.button.fontSize,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
maxLines = 1,
)
Spacer(Modifier.width(8.dp))
@@ -142,7 +142,7 @@ private fun Snr(snr: Float) {
Text(
text = "%s %.2fdB".format(stringResource(id = R.string.snr), snr),
color = color,
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
@@ -158,7 +158,7 @@ private fun Rssi(rssi: Int) {
Text(
text = "%s %ddBm".format(stringResource(id = R.string.rssi), rssi),
color = color,
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}

View File

@@ -27,19 +27,19 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Sort
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -72,7 +72,7 @@ fun NodeFilterTextField(
onToggleShowDetails: () -> Unit,
) {
Row(
modifier = modifier.background(MaterialTheme.colors.background),
modifier = modifier.background(MaterialTheme.colorScheme.background),
) {
NodeFilterTextField(
filterText = filterText,
@@ -109,8 +109,8 @@ private fun NodeFilterTextField(
placeholder = {
Text(
text = stringResource(id = R.string.node_filter_placeholder),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onBackground.copy(alpha = 0.35F)
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.35F)
)
},
leadingIcon = {
@@ -132,8 +132,8 @@ private fun NodeFilterTextField(
)
}
},
textStyle = MaterialTheme.typography.body1.copy(
color = MaterialTheme.colors.onBackground
textStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.onBackground
),
maxLines = 1,
keyboardOptions = KeyboardOptions(
@@ -163,14 +163,14 @@ private fun NodeSortButton(
imageVector = Icons.AutoMirrored.Filled.Sort,
contentDescription = stringResource(R.string.node_sort_button),
modifier = Modifier.heightIn(max = 48.dp),
tint = MaterialTheme.colors.onSurface
tint = MaterialTheme.colorScheme.onSurface
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.background(MaterialTheme.colors.background.copy(alpha = 1f))
modifier = Modifier.background(MaterialTheme.colorScheme.background.copy(alpha = 1f))
) {
NodeSortOption.entries.forEach { sort ->
DropdownMenuItem(
@@ -178,49 +178,56 @@ private fun NodeSortButton(
onSortSelect(sort)
expanded = false
},
) {
Text(
text = stringResource(id = sort.stringRes),
fontWeight = if (sort == currentSortOption) FontWeight.Bold else null,
)
}
text = {
Text(
text = stringResource(id = sort.stringRes),
fontWeight = if (sort == currentSortOption) FontWeight.Bold else null,
)
}
)
}
Divider()
HorizontalDivider()
DropdownMenuItem(
onClick = {
onToggleIncludeUnknown()
expanded = false
},
) {
Text(
text = stringResource(id = R.string.node_filter_include_unknown),
)
AnimatedVisibility(visible = includeUnknown) {
Icon(
imageVector = Icons.Default.Done,
contentDescription = null,
modifier = Modifier.padding(start = 4.dp),
)
text = {
Row {
AnimatedVisibility(visible = includeUnknown) {
Icon(
imageVector = Icons.Default.Done,
contentDescription = null,
modifier = Modifier.padding(end = 4.dp),
)
}
Text(
text = stringResource(id = R.string.node_filter_include_unknown),
)
}
}
}
Divider()
)
HorizontalDivider()
DropdownMenuItem(
onClick = {
onToggleShowDetails()
expanded = false
},
) {
Text(
text = stringResource(id = R.string.node_filter_show_details),
)
AnimatedVisibility(visible = showDetails) {
Icon(
imageVector = Icons.Default.Done,
contentDescription = null,
modifier = Modifier.padding(start = 4.dp),
)
text = {
Row {
AnimatedVisibility(visible = showDetails) {
Icon(
imageVector = Icons.Default.Done,
contentDescription = null,
modifier = Modifier.padding(end = 4.dp),
)
}
Text(
text = stringResource(id = R.string.node_filter_show_details),
)
}
}
}
)
}
}

View File

@@ -28,17 +28,16 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyOff
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -71,7 +70,7 @@ private fun KeyStatusDialog(
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
color = MaterialTheme.colors.background
color = MaterialTheme.colorScheme.background
) {
LazyColumn(
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
@@ -80,13 +79,11 @@ private fun KeyStatusDialog(
item {
Text(
text = stringResource(id = title),
color = MaterialTheme.colors.onBackground.copy(alpha = ContentAlpha.high),
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(16.dp))
Text(
text = stringResource(id = text),
color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(16.dp))
@@ -112,7 +109,7 @@ private fun KeyStatusDialog(
TextButton(
onClick = onDismiss,
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colors.onSurface,
contentColor = MaterialTheme.colorScheme.onSurface,
),
) { Text(text = stringResource(id = R.string.close)) }
}

View File

@@ -18,15 +18,14 @@
package com.geeksville.mesh.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Checkbox
import androidx.compose.material.Divider
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -96,7 +95,7 @@ fun NodeMenu(
)
}
DropdownMenu(
modifier = Modifier.background(MaterialTheme.colors.background.copy(alpha = 1f)),
modifier = Modifier.background(MaterialTheme.colorScheme.background.copy(alpha = 1f)),
expanded = expanded,
onDismissRequest = onDismissRequest,
) {
@@ -107,28 +106,28 @@ fun NodeMenu(
onDismissRequest()
onAction(NodeMenuAction.DirectMessage(node))
},
content = { Text(stringResource(R.string.direct_message)) }
text = { Text(stringResource(R.string.direct_message)) }
)
DropdownMenuItem(
onClick = {
onDismissRequest()
onAction(NodeMenuAction.RequestUserInfo(node))
},
content = { Text(stringResource(R.string.exchange_userinfo)) }
text = { Text(stringResource(R.string.exchange_userinfo)) }
)
DropdownMenuItem(
onClick = {
onDismissRequest()
onAction(NodeMenuAction.RequestPosition(node))
},
content = { Text(stringResource(R.string.exchange_position)) }
text = { Text(stringResource(R.string.exchange_position)) }
)
DropdownMenuItem(
onClick = {
onDismissRequest()
onAction(NodeMenuAction.TraceRoute(node))
},
content = { Text(stringResource(R.string.traceroute)) }
text = { Text(stringResource(R.string.traceroute)) }
)
DropdownMenuItem(
onClick = {
@@ -136,51 +135,56 @@ fun NodeMenu(
displayFavoriteDialog = true
},
enabled = !node.isIgnored,
) {
Text(stringResource(R.string.favorite))
Spacer(Modifier.weight(1f))
Checkbox(
checked = node.isFavorite,
onCheckedChange = {
onDismissRequest()
displayFavoriteDialog = true
},
modifier = Modifier.size(24.dp),
enabled = !node.isIgnored,
)
}
text = {
Text(stringResource(R.string.favorite))
},
trailingIcon = {
Checkbox(
checked = node.isFavorite,
onCheckedChange = {
onDismissRequest()
displayFavoriteDialog = true
},
modifier = Modifier.size(24.dp),
enabled = !node.isIgnored,
)
}
)
DropdownMenuItem(
onClick = {
onDismissRequest()
displayIgnoreDialog = true
},
) {
Text(stringResource(R.string.ignore))
Spacer(Modifier.weight(1f))
Checkbox(
checked = node.isIgnored,
onCheckedChange = {
onDismissRequest()
displayIgnoreDialog = true
},
modifier = Modifier.size(24.dp),
)
}
text = {
Text(stringResource(R.string.ignore))
},
trailingIcon = {
Checkbox(
checked = node.isIgnored,
onCheckedChange = {
onDismissRequest()
displayIgnoreDialog = true
},
modifier = Modifier.size(24.dp),
)
}
)
DropdownMenuItem(
onClick = {
onDismissRequest()
displayRemoveDialog = true
},
enabled = !node.isIgnored,
) { Text(stringResource(R.string.remove)) }
Divider(Modifier.padding(vertical = 8.dp))
text = { Text(stringResource(R.string.remove)) }
)
HorizontalDivider(Modifier.padding(vertical = 8.dp))
}
DropdownMenuItem(
onClick = {
onDismissRequest()
onAction(NodeMenuAction.MoreDetails(node))
},
content = { Text(stringResource(R.string.more_details)) }
text = { Text(stringResource(R.string.more_details)) }
)
}
}

View File

@@ -35,17 +35,15 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@@ -174,7 +172,7 @@ private fun ActionButtons(
onClick = onClear,
enabled = clearButtonEnabled,
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.error,
contentColor = MaterialTheme.colorScheme.error,
)
) {
Icon(
@@ -191,9 +189,6 @@ private fun ActionButtons(
modifier = Modifier.weight(1f),
onClick = onSave,
enabled = saveButtonEnabled,
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
)
) {
Icon(
imageVector = Icons.Default.Save,
@@ -229,7 +224,7 @@ fun PositionLogScreen(
val compactWidth = maxWidth < 600.dp
Column {
val textStyle = if (compactWidth) {
MaterialTheme.typography.caption
MaterialTheme.typography.bodySmall
} else {
LocalTextStyle.current
}
@@ -268,14 +263,12 @@ private fun ColumnScope.PositionList(
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
LazyColumn(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
items(positions) { position ->
PositionItem(compactWidth, position, dateFormat, displayUnits)
}
LazyColumn(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
items(positions) { position ->
PositionItem(compactWidth, position, dateFormat, displayUnits)
}
}
}

View File

@@ -21,9 +21,9 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Slider
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -95,7 +95,7 @@ fun PositionPrecisionPreference(
Text(
text = precisionMeters.toDistanceString(unit),
modifier = Modifier.padding(bottom = 16.dp),
fontSize = MaterialTheme.typography.body1.fontSize,
fontSize = MaterialTheme.typography.bodyLarge.fontSize,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)

View File

@@ -35,10 +35,10 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.Surface
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -62,8 +62,8 @@ import com.geeksville.mesh.R
import com.geeksville.mesh.TelemetryProtos.Telemetry
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.TimeFrame
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.ui.theme.InfantryBlue
import com.geeksville.mesh.util.GraphUtil
import com.geeksville.mesh.util.GraphUtil.createPath
@@ -159,7 +159,7 @@ private fun PowerMetricsChart(
Spacer(modifier = Modifier.height(16.dp))
val graphColor = MaterialTheme.colors.onSurface
val graphColor = MaterialTheme.colorScheme.onSurface
val currentDiff = Power.CURRENT.difference()
val voltageDiff = Power.VOLTAGE.difference()
@@ -292,7 +292,7 @@ private fun PowerMetricsCard(telemetry: Telemetry) {
Text(
text = DATE_TIME_FORMAT.format(time),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
Row(
@@ -334,17 +334,17 @@ private fun PowerChannelColumn(@StringRes titleRes: Int, voltage: Float, current
Text(
text = stringResource(titleRes),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
Text(
text = "%.2fV".format(voltage),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
Text(
text = "%.1fmA".format(current),
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}
}

View File

@@ -21,11 +21,10 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -41,13 +40,11 @@ fun PreferenceCategory(
Text(
text,
modifier = modifier.padding(start = 16.dp, top = 24.dp, bottom = 8.dp, end = 16.dp),
style = MaterialTheme.typography.h6,
style = MaterialTheme.typography.titleLarge,
)
if (content != null) {
Surface(
Card(
modifier = modifier.padding(bottom = 8.dp),
shape = RoundedCornerShape(12.dp),
elevation = 1.dp,
) {
Column(
modifier = Modifier
@@ -55,7 +52,7 @@ fun PreferenceCategory(
.padding(horizontal = 16.dp, vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
ProvideTextStyle(MaterialTheme.typography.body1) {
ProvideTextStyle(MaterialTheme.typography.bodyLarge) {
content()
}
}
@@ -69,4 +66,4 @@ private fun PreferenceCategoryPreview() {
PreferenceCategory(
text = "Advanced settings"
)
}
}

View File

@@ -18,8 +18,12 @@
package com.geeksville.mesh.ui.components
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -67,13 +71,9 @@ fun PreferenceFooter(
.weight(1f),
enabled = enabled,
onClick = onNegativeClicked,
colors = ButtonDefaults.buttonColors(
disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
)
) {
Text(
text = stringResource(id = negativeText),
style = MaterialTheme.typography.body1,
)
}
OutlinedButton(
@@ -82,13 +82,9 @@ fun PreferenceFooter(
.weight(1f),
enabled = enabled,
onClick = onPositiveClicked,
colors = ButtonDefaults.buttonColors(
disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
)
) {
Text(
text = stringResource(id = positiveText),
style = MaterialTheme.typography.body1,
)
}
}

View File

@@ -26,10 +26,9 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -72,9 +71,9 @@ fun RegularPreference(
trailingIcon: ImageVector? = null,
) {
val color = if (enabled) {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium)
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
}
Column(
@@ -93,17 +92,17 @@ fun RegularPreference(
) {
Text(
text = title,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
color = if (enabled) {
Color.Unspecified
} else {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
},
)
Text(
text = subtitle,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
color = color,
)
}
@@ -121,7 +120,7 @@ fun RegularPreference(
if (summary != null) {
Text(
text = summary,
style = MaterialTheme.typography.body2,
style = MaterialTheme.typography.bodyMedium,
color = color,
)
}

View File

@@ -29,12 +29,12 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
@@ -122,7 +122,7 @@ fun ScannedQrCodeDialog(
Surface(
modifier = Modifier.widthIn(max = 600.dp),
shape = RoundedCornerShape(16.dp),
color = MaterialTheme.colors.background
color = MaterialTheme.colorScheme.background
) {
LazyColumn(
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
@@ -132,7 +132,7 @@ fun ScannedQrCodeDialog(
Text(
text = stringResource(id = R.string.new_channel_rcvd),
modifier = Modifier.padding(20.dp),
style = MaterialTheme.typography.h6,
style = MaterialTheme.typography.titleLarge,
)
}
itemsIndexed(channelSet.settingsList) { index, channel ->
@@ -155,7 +155,7 @@ fun ScannedQrCodeDialog(
) {
val selectedColors = ButtonDefaults.buttonColors()
val unselectedColors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.onSurface,
contentColor = MaterialTheme.colorScheme.onSurface,
)
OutlinedButton(
@@ -192,10 +192,10 @@ fun ScannedQrCodeDialog(
) {
Text(
text = stringResource(id = R.string.cancel),
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.colorScheme.onSurface,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
)
}
@@ -208,10 +208,10 @@ fun ScannedQrCodeDialog(
) {
Text(
text = stringResource(id = R.string.accept),
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.colorScheme.onSurface,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
)
}
}

View File

@@ -18,8 +18,8 @@
package com.geeksville.mesh.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -66,8 +66,8 @@ fun SignalInfo(
Text(
modifier = modifier,
text = text,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.caption.fontSize
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.bodySmall.fontSize
)
}
/* We only know the Signal Quality from direct nodes aka 0 hop. */

View File

@@ -35,10 +35,10 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.Surface
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -59,8 +59,8 @@ import com.geeksville.mesh.MeshProtos.MeshPacket
import com.geeksville.mesh.R
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.TimeFrame
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
import com.geeksville.mesh.util.GraphUtil.plotPoint
@Suppress("MagicNumber")
@@ -151,7 +151,7 @@ private fun SignalMetricsChart(
Spacer(modifier = Modifier.height(16.dp))
val graphColor = MaterialTheme.colors.onSurface
val graphColor = MaterialTheme.colorScheme.onSurface
val snrDiff = Metric.SNR.difference()
val rssiDiff = Metric.RSSI.difference()
@@ -262,7 +262,7 @@ private fun SignalMetricsCard(meshPacket: MeshPacket) {
Text(
text = DATE_TIME_FORMAT.format(time),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.button.fontSize
fontSize = MaterialTheme.typography.labelLarge.fontSize
)
}

View File

@@ -21,11 +21,11 @@ import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -49,7 +49,7 @@ fun SimpleAlertDialog(
modifier = Modifier
.padding(horizontal = 16.dp),
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colors.onSurface,
contentColor = MaterialTheme.colorScheme.onSurface,
),
) { Text(text = stringResource(id = R.string.close)) }
},
@@ -60,7 +60,7 @@ fun SimpleAlertDialog(
modifier = Modifier
.padding(horizontal = 16.dp),
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colors.onSurface,
contentColor = MaterialTheme.colorScheme.onSurface,
),
) { Text(text = stringResource(id = R.string.okay)) }
}
@@ -74,7 +74,6 @@ fun SimpleAlertDialog(
},
text = text,
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
)
@Composable

View File

@@ -35,10 +35,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@@ -112,7 +112,10 @@ fun <T : Any> SlidingSelector(
state.onOptionSelected = { onOptionSelected(options[it]) }
/* Animate between whole-number indices so we don't need to do pixel calculations. */
val selectedIndexOffset by animateFloatAsState(state.selectedOption.toFloat(), label = "Selected Index Offset")
val selectedIndexOffset by animateFloatAsState(
state.selectedOption.toFloat(),
label = "Selected Index Offset"
)
Layout(
content = {
@@ -183,7 +186,7 @@ private fun SelectedIndicator(state: SelectorState) {
)
)
.shadow(4.dp, BACKGROUND_SHAPE)
.background(MaterialTheme.colors.background, BACKGROUND_SHAPE)
.background(MaterialTheme.colorScheme.background, BACKGROUND_SHAPE)
)
}
@@ -310,7 +313,10 @@ private class SelectorState {
option: Int,
): Modifier = Modifier.composed {
val scale by animateFloatAsState(if (pressed) pressedSelectedScale else 1f, label = "Scale")
val xOffset by animateDpAsState(if (pressed) PRESSED_TRACK_PADDING else 0.dp, label = "x Offset")
val xOffset by animateDpAsState(
if (pressed) PRESSED_TRACK_PADDING else 0.dp,
label = "x Offset"
)
graphicsLayer {
this.scaleX = scale

View File

@@ -23,12 +23,10 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ContentAlpha
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ListItem
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -36,7 +34,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SwitchPreference(
title: String,
@@ -49,31 +46,32 @@ fun SwitchPreference(
val color = if (enabled) {
Color.Unspecified
} else {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
}
ListItem(
modifier = modifier,
trailing = {
trailingContent = {
Switch(
enabled = enabled,
checked = checked,
onCheckedChange = onCheckedChange,
)
},
secondaryText = {
supportingContent = {
Text(
text = summary,
modifier = Modifier.padding(bottom = 16.dp),
color = color,
)
},
) {
Text(
text = title,
color = color,
)
}
headlineContent = {
Text(
text = title,
color = color,
)
}
)
}
@Composable
@@ -95,11 +93,11 @@ fun SwitchPreference(
) {
Text(
text = title,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
color = if (enabled) {
Color.Unspecified
} else {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
},
)
Switch(

View File

@@ -17,16 +17,14 @@
package com.geeksville.mesh.ui.components
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.Card
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -60,7 +58,6 @@ fun TextDividerPreference(
) {
Card(
modifier = modifier.fillMaxWidth(),
backgroundColor = if (isSystemInDarkTheme()) Color.DarkGray else Color.LightGray,
) {
Row(
modifier = modifier
@@ -70,15 +67,21 @@ fun TextDividerPreference(
) {
Text(
text = title,
style = MaterialTheme.typography.body1,
color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.Unspecified,
)
if (trailingIcon != null) Icon(
trailingIcon, "trailingIcon",
modifier = modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
style = MaterialTheme.typography.bodyLarge,
color = if (!enabled) {
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
} else {
Color.Unspecified
},
)
if (trailingIcon != null) {
Icon(
trailingIcon, "trailingIcon",
modifier = modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
)
}
}
}
}

View File

@@ -31,17 +31,17 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Group
import androidx.compose.material.icons.filled.Groups
import androidx.compose.material.icons.filled.PersonOff
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -135,18 +135,20 @@ fun TracerouteLogScreen(
@Composable
private fun DeleteItem(onClick: () -> Unit) {
DropdownMenuItem(onClick = onClick) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = stringResource(id = R.string.delete),
tint = MaterialTheme.colors.error,
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = stringResource(id = R.string.delete),
color = MaterialTheme.colors.error,
)
}
DropdownMenuItem(
onClick = onClick,
text = {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = stringResource(id = R.string.delete),
tint = MaterialTheme.colorScheme.error,
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = stringResource(id = R.string.delete),
color = MaterialTheme.colorScheme.error,
)
})
}
@Composable
@@ -160,7 +162,6 @@ private fun TracerouteItem(
.fillMaxWidth()
.heightIn(min = 56.dp)
.padding(vertical = 2.dp),
elevation = 4.dp
) {
Row(
modifier = Modifier
@@ -174,7 +175,7 @@ private fun TracerouteItem(
Spacer(modifier = Modifier.width(8.dp))
Text(
text = text,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
)
}
}

View File

@@ -22,8 +22,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -47,7 +47,7 @@ fun UserAvatar(
onClick: () -> Unit = {}
) {
val textMeasurer = rememberTextMeasurer()
val textStyle = MaterialTheme.typography.button.copy(
val textStyle = MaterialTheme.typography.labelLarge.copy(
fontWeight = FontWeight.Normal,
)
val textLayoutResult = remember {

View File

@@ -17,8 +17,8 @@
package com.geeksville.mesh.ui.compose
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.buildAnnotatedString
@@ -37,7 +37,7 @@ fun ElevationInfo(
) {
val annotatedString = buildAnnotatedString {
append(altitude.metersIn(system).toString(system))
MaterialTheme.typography.overline.toSpanStyle().let { style ->
MaterialTheme.typography.labelSmall.toSpanStyle().let { style ->
withStyle(style) {
append(" $suffix")
}
@@ -46,7 +46,7 @@ fun ElevationInfo(
Text(
modifier = modifier,
fontSize = MaterialTheme.typography.button.fontSize,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
text = annotatedString,
)
}

View File

@@ -20,11 +20,11 @@ package com.geeksville.mesh.ui.compose
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.SatelliteAlt
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -46,12 +46,12 @@ fun SatelliteCountInfo(
modifier = Modifier.size(18.dp),
imageVector = Icons.TwoTone.SatelliteAlt,
contentDescription = null,
tint = MaterialTheme.colors.onSurface,
tint = MaterialTheme.colorScheme.onSurface,
)
Text(
text = "$satCount",
fontSize = MaterialTheme.typography.button.fontSize,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
color = MaterialTheme.colorScheme.onSurface,
)
}
}

View File

@@ -27,10 +27,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.Button
import androidx.compose.material.ContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -51,15 +50,14 @@ internal fun CacheLayout(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.background(color = MaterialTheme.colors.background)
.background(color = MaterialTheme.colorScheme.background)
.padding(8.dp),
) {
Text(
text = stringResource(id = R.string.map_select_download_region),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h5,
color = MaterialTheme.colors.onBackground.copy(alpha = ContentAlpha.medium),
style = MaterialTheme.typography.headlineSmall,
)
Spacer(modifier = Modifier.height(8.dp))
@@ -68,8 +66,7 @@ internal fun CacheLayout(
text = stringResource(R.string.map_tile_download_estimate) + " " + cacheEstimate,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onBackground.copy(alpha = ContentAlpha.medium),
style = MaterialTheme.typography.bodyLarge,
)
FlowRow(
@@ -84,7 +81,7 @@ internal fun CacheLayout(
) {
Text(
text = stringResource(id = R.string.cancel),
color = MaterialTheme.colors.onPrimary,
color = MaterialTheme.colorScheme.onPrimary,
)
}
Button(
@@ -93,7 +90,7 @@ internal fun CacheLayout(
) {
Text(
text = stringResource(id = R.string.map_start_download),
color = MaterialTheme.colors.onPrimary,
color = MaterialTheme.colorScheme.onPrimary,
)
}
}

View File

@@ -22,11 +22,11 @@ import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Download
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
@@ -51,7 +51,7 @@ internal fun DownloadButton(
) {
FloatingActionButton(
onClick = onClick,
backgroundColor = MaterialTheme.colors.primary,
contentColor = MaterialTheme.colorScheme.primary,
) {
Icon(
imageVector = Icons.Default.Download,

View File

@@ -32,15 +32,15 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -84,12 +84,11 @@ internal fun EditWaypointDialog(
AlertDialog(
onDismissRequest = onDismissRequest,
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
text = {
Column(modifier = modifier.fillMaxWidth()) {
Text(
text = stringResource(title),
style = MaterialTheme.typography.h6.copy(
style = MaterialTheme.typography.titleLarge.copy(
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
),
@@ -113,7 +112,7 @@ internal fun EditWaypointDialog(
Text(
text = String(Character.toChars(emoji)),
modifier = Modifier
.background(MaterialTheme.colors.background, CircleShape)
.background(MaterialTheme.colorScheme.background, CircleShape)
.padding(4.dp),
fontSize = 24.sp,
color = Color.Unspecified.copy(alpha = 1f),
@@ -157,7 +156,7 @@ internal fun EditWaypointDialog(
}
}
},
buttons = {
confirmButton = {
FlowRow(
modifier = modifier.padding(start = 20.dp, end = 20.dp, bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),

View File

@@ -20,15 +20,12 @@ package com.geeksville.mesh.ui.map
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Layers
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
@@ -41,14 +38,12 @@ fun MapButton(
icon: ImageVector,
@StringRes contentDescription: Int,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onClick: () -> Unit = {}
) {
MapButton(
icon = icon,
contentDescription = stringResource(contentDescription),
modifier = modifier,
enabled = enabled,
onClick = onClick,
)
}
@@ -58,23 +53,17 @@ fun MapButton(
icon: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onClick: () -> Unit = {}
) {
Button(
FloatingActionButton(
onClick = onClick,
enabled = enabled,
modifier = modifier.size(48.dp),
modifier = modifier,
shape = CircleShape,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.surface.copy(alpha = 1f),
contentColor = MaterialTheme.colors.onSurface,
),
) {
Icon(
imageVector = icon,
contentDescription = contentDescription,
modifier = Modifier.scale(scale = 1.5f),
modifier = Modifier.size(24.dp),
)
}
}

View File

@@ -26,11 +26,11 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.LocationDisabled
import androidx.compose.material.icons.outlined.Layers
import androidx.compose.material.icons.outlined.MyLocation
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -423,9 +423,9 @@ fun MapView(
if (enabled) {
showEditWaypointDialog = waypoint {
latitudeI = (p.latitude * 1e7).toInt()
longitudeI = (p.longitude * 1e7).toInt()
}
latitudeI = (p.latitude * 1e7).toInt()
longitudeI = (p.longitude * 1e7).toInt()
}
}
return true
}
@@ -587,14 +587,14 @@ fun MapView(
)
if (downloadRegionBoundingBox != null) {
CacheLayout(
cacheEstimate = cacheEstimate,
onExecuteJob = { startDownload() },
onCancelDownload = {
downloadRegionBoundingBox = null
map.overlays.removeAll { it is Polygon }
map.invalidate()
},
modifier = Modifier.align(Alignment.BottomCenter)
cacheEstimate = cacheEstimate,
onExecuteJob = { startDownload() },
onCancelDownload = {
downloadRegionBoundingBox = null
map.overlays.removeAll { it is Polygon }
map.invalidate()
},
modifier = Modifier.align(Alignment.BottomCenter)
)
} else {
Column(
@@ -608,19 +608,20 @@ fun MapView(
icon = Icons.Outlined.Layers,
contentDescription = R.string.map_style_selection,
)
MapButton(
enabled = hasGps,
icon = if (myLocationOverlay == null) {
Icons.Outlined.MyLocation
} else {
Icons.Default.LocationDisabled
},
contentDescription = stringResource(R.string.toggle_my_position),
) {
if (context.hasLocationPermission()) {
map.toggleMyLocation()
} else {
requestPermissionAndToggleLauncher.launch(context.getLocationPermissions())
if (hasGps) {
MapButton(
icon = if (myLocationOverlay == null) {
Icons.Outlined.MyLocation
} else {
Icons.Default.LocationDisabled
},
contentDescription = stringResource(R.string.toggle_my_position),
) {
if (context.hasLocationPermission()) {
map.toggleMyLocation()
} else {
requestPermissionAndToggleLauncher.launch(context.getLocationPermissions())
}
}
}
}
@@ -636,9 +637,9 @@ fun MapView(
showEditWaypointDialog = null
model.sendWaypoint(
waypoint.copy {
if (id == 0) id = model.generatePacketId() ?: return@EditWaypointDialog
expire = Int.MAX_VALUE // TODO add expire picker
lockedTo = if (waypoint.lockedTo != 0) model.myNodeNum ?: 0 else 0
if (id == 0) id = model.generatePacketId() ?: return@EditWaypointDialog
expire = Int.MAX_VALUE // TODO add expire picker
lockedTo = if (waypoint.lockedTo != 0) model.myNodeNum ?: 0 else 0
}
)
},

View File

@@ -18,7 +18,6 @@
package com.geeksville.mesh.ui.message
import android.content.ClipData
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -28,25 +27,24 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.TextFieldDefaults
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.Send
import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.SelectAll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
@@ -59,11 +57,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
@@ -183,7 +179,6 @@ internal fun MessageScreen(
val isConnected = connState.isConnected()
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.padding(start = 8.dp, end = 8.dp, bottom = 4.dp),
) {
QuickChatRow(isConnected, quickChat) { action ->
@@ -248,7 +243,6 @@ private fun DeleteMessageDialog(
AlertDialog(
onDismissRequest = onDismiss,
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
text = {
Text(
text = deleteMessagesString,
@@ -276,6 +270,7 @@ sealed class MessageMenuAction {
data object SelectAll : MessageMenuAction()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ActionModeTopBar(
selectedList: Set<Long>,
@@ -310,9 +305,9 @@ private fun ActionModeTopBar(
)
}
},
backgroundColor = MaterialTheme.colors.primary,
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MessageTopBar(
title: String,
@@ -358,9 +353,6 @@ private fun QuickChatRow(
onClick = { onClick(action) },
modifier = Modifier.padding(horizontal = 4.dp),
enabled = enabled,
colors = ButtonDefaults.buttonColors(
backgroundColor = colorResource(id = R.color.colorMyMsg),
)
) {
Text(
text = action.name,
@@ -396,10 +388,6 @@ private fun TextInput(
),
maxLines = 3,
shape = RoundedCornerShape(24.dp),
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
trailingIcon = {
IconButton(
onClick = {
@@ -416,7 +404,6 @@ private fun TextInput(
Icon(
imageVector = Icons.AutoMirrored.Default.Send,
contentDescription = stringResource(id = R.string.send_text),
tint = MaterialTheme.colors.primary
)
}
}
@@ -424,7 +411,7 @@ private fun TextInput(
if (isFocused) {
Text(
text = "${message.value.text.toByteArray().size}/$maxSize",
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier
.align(Alignment.End)
.padding(top = 4.dp, end = 72.dp)

View File

@@ -27,13 +27,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Cloud
import androidx.compose.material.icons.twotone.CloudDone
@@ -41,6 +34,11 @@ import androidx.compose.material.icons.twotone.CloudOff
import androidx.compose.material.icons.twotone.CloudUpload
import androidx.compose.material.icons.twotone.HowToReg
import androidx.compose.material.icons.twotone.Warning
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -50,7 +48,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.geeksville.mesh.DataPacket
import com.geeksville.mesh.MessageStatus
import com.geeksville.mesh.R
@@ -76,9 +73,9 @@ internal fun MessageItem(
onStatusClick: () -> Unit = {},
onSendReaction: (String) -> Unit = {},
) = Row(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.background(color = if (selected) Color.Gray else MaterialTheme.colors.background),
.background(color = if (selected) Color.Gray else MaterialTheme.colorScheme.background),
verticalAlignment = Alignment.CenterVertically,
) {
val fromLocal = node.user.id == DataPacket.ID_LOCAL
@@ -102,70 +99,61 @@ internal fun MessageItem(
Card(
modifier = Modifier
.weight(1f)
.then(messageModifier),
elevation = 4.dp,
shape = RoundedCornerShape(topStart, topEnd, 12.dp, 12.dp),
) {
Surface(
modifier = modifier.combinedClickable(
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
),
color = colorResource(id = messageColor),
)
.then(messageModifier),
colors = CardDefaults.cardColors(
containerColor = colorResource(messageColor)
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
Column(
modifier = Modifier.padding(top = 8.dp),
) {
Column(
modifier = Modifier.padding(top = 8.dp),
) {
if (!fromLocal) {
Text(
text = with(node.user) { "$longName ($id)" },
modifier = Modifier.padding(bottom = 4.dp),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.button.copy(
color = MaterialTheme.colors.onSurface,
letterSpacing = 0.1.sp,
)
)
}
AutoLinkText(
text = messageText.orEmpty(),
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.onBackground,
),
if (!fromLocal) {
Text(
text = with(node.user) { "$longName ($id)" },
modifier = Modifier.padding(bottom = 4.dp),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.labelLarge
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = messageTime,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.caption.fontSize,
}
AutoLinkText(
text = messageText.orEmpty(),
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = messageTime,
fontSize = MaterialTheme.typography.bodySmall.fontSize,
)
AnimatedVisibility(visible = fromLocal) {
Icon(
imageVector = when (messageStatus) {
MessageStatus.RECEIVED -> Icons.TwoTone.HowToReg
MessageStatus.QUEUED -> Icons.TwoTone.CloudUpload
MessageStatus.DELIVERED -> Icons.TwoTone.CloudDone
MessageStatus.ENROUTE -> Icons.TwoTone.Cloud
MessageStatus.ERROR -> Icons.TwoTone.CloudOff
else -> Icons.TwoTone.Warning
},
contentDescription = stringResource(R.string.message_delivery_status),
modifier = Modifier
.padding(start = 8.dp)
.clickable { onStatusClick() },
)
AnimatedVisibility(visible = fromLocal) {
Icon(
imageVector = when (messageStatus) {
MessageStatus.RECEIVED -> Icons.TwoTone.HowToReg
MessageStatus.QUEUED -> Icons.TwoTone.CloudUpload
MessageStatus.DELIVERED -> Icons.TwoTone.CloudDone
MessageStatus.ENROUTE -> Icons.TwoTone.Cloud
MessageStatus.ERROR -> Icons.TwoTone.CloudOff
else -> Icons.TwoTone.Warning
},
contentDescription = stringResource(R.string.message_delivery_status),
modifier = Modifier
.padding(start = 8.dp)
.clickable { onStatusClick() },
)
}
}
}
}

View File

@@ -33,17 +33,16 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Badge
import androidx.compose.material.BadgedBox
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EmojiEmotions
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -83,7 +82,6 @@ fun ReactionButton(
imageVector = Icons.Default.EmojiEmotions,
contentDescription = "emoji",
modifier = modifier.size(16.dp),
tint = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
)
}
}
@@ -99,8 +97,8 @@ private fun ReactionItem(
badge = {
if (emojiCount > 1) {
Badge(
backgroundColor = MaterialTheme.colors.onBackground,
contentColor = MaterialTheme.colors.background,
containerColor = MaterialTheme.colorScheme.onBackground,
contentColor = MaterialTheme.colorScheme.background,
) {
Text(
fontWeight = FontWeight.Bold,
@@ -113,9 +111,8 @@ private fun ReactionItem(
Surface(
modifier = Modifier
.clickable { onClick() },
color = MaterialTheme.colors.surface,
color = MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(32.dp),
elevation = 4.dp,
) {
Text(
text = emoji,
@@ -192,7 +189,7 @@ fun ReactionDialog(
.clickable {
selectedEmoji = if (selectedEmoji == emoji) null else emoji
},
style = MaterialTheme.typography.body2
style = MaterialTheme.typography.bodyMedium
)
}
}
@@ -211,11 +208,11 @@ fun ReactionDialog(
) {
Text(
text = reaction.user.longName,
style = MaterialTheme.typography.subtitle1
style = MaterialTheme.typography.titleMedium
)
Text(
text = reaction.emoji,
style = MaterialTheme.typography.h6
style = MaterialTheme.typography.titleLarge
)
}
}
@@ -227,7 +224,7 @@ fun ReactionDialog(
fun ReactionItemPreview() {
AppTheme {
Column(
modifier = Modifier.background(MaterialTheme.colors.background)
modifier = Modifier.background(MaterialTheme.colorScheme.background)
) {
ReactionItem(emoji = "\uD83D\uDE42")
ReactionItem(emoji = "\uD83D\uDE42", emojiCount = 2)

View File

@@ -22,7 +22,6 @@ import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -37,19 +36,18 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.Card
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowRight
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Upload
import androidx.compose.material.icons.twotone.Warning
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -195,18 +193,12 @@ fun NavCard(
icon: ImageVector? = null,
onClick: () -> Unit
) {
val color = if (enabled) {
MaterialTheme.colors.onSurface
} else {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
}
Card(
onClick = onClick,
enabled = enabled,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 2.dp)
.clickable(enabled = enabled) { onClick() },
elevation = 4.dp
) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -217,20 +209,17 @@ fun NavCard(
imageVector = icon,
contentDescription = title,
modifier = Modifier.size(24.dp),
tint = color,
)
Spacer(modifier = Modifier.width(8.dp))
}
Text(
text = title,
style = MaterialTheme.typography.body1,
color = color,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f)
)
Icon(
Icons.AutoMirrored.TwoTone.KeyboardArrowRight, "trailingIcon",
modifier = Modifier.wrapContentSize(),
tint = color,
)
}
}
@@ -244,7 +233,6 @@ private fun NavButton(@StringRes title: Int, enabled: Boolean, onClick: () -> Un
AlertDialog(
onDismissRequest = {},
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
title = {
Row(
modifier = Modifier.fillMaxWidth(),
@@ -265,7 +253,7 @@ private fun NavButton(@StringRes title: Int, enabled: Boolean, onClick: () -> Un
)
}
},
buttons = {
confirmButton = {
Row(
modifier = Modifier
.fillMaxWidth()

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -89,7 +89,7 @@ fun AmbientLightingConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -20,7 +20,8 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -88,7 +89,7 @@ fun AudioConfigItemList(
onCheckedChange = { audioInput = audioInput.copy { codec2Enabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -88,7 +88,7 @@ fun BluetoothConfigItemList(
onCheckedChange = { bluetoothInput = bluetoothInput.copy { this.enabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
DropDownPreference(
@@ -101,7 +101,7 @@ fun BluetoothConfigItemList(
onItemSelected = { bluetoothInput = bluetoothInput.copy { mode = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -101,7 +101,7 @@ fun CannedMessageConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -113,7 +113,7 @@ fun CannedMessageConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -164,7 +164,7 @@ fun CannedMessageConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
DropDownPreference(
@@ -179,7 +179,7 @@ fun CannedMessageConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
DropDownPreference(
@@ -194,7 +194,7 @@ fun CannedMessageConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -206,7 +206,7 @@ fun CannedMessageConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -235,7 +235,7 @@ fun CannedMessageConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -19,7 +19,6 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
@@ -34,19 +33,17 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Card
import androidx.compose.material.Checkbox
import androidx.compose.material.Chip
import androidx.compose.material.ContentAlpha
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Add
import androidx.compose.material.icons.twotone.Close
import androidx.compose.material3.AssistChip
import androidx.compose.material3.Card
import androidx.compose.material3.Checkbox
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -56,13 +53,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -77,14 +72,12 @@ import com.geeksville.mesh.ui.components.dragDropItemsIndexed
import com.geeksville.mesh.ui.components.rememberDragDropState
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun ChannelItem(
index: Int,
title: String,
enabled: Boolean,
onClick: () -> Unit = {},
elevation: Dp = 4.dp,
content: @Composable RowScope.() -> Unit,
) {
Card(
@@ -92,31 +85,23 @@ private fun ChannelItem(
.fillMaxWidth()
.padding(vertical = 2.dp)
.clickable(enabled = enabled) { onClick() },
elevation = elevation,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 4.dp)
) {
val textColor = if (enabled) {
Color.Unspecified
} else {
MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
}
Chip(onClick = onClick) {
AssistChip(onClick = onClick, label = {
Text(
text = "$index",
color = textColor,
)
}
})
Text(
text = title,
modifier = Modifier.weight(1f),
color = textColor,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
)
content()
}
@@ -130,13 +115,11 @@ fun ChannelCard(
enabled: Boolean,
onEditClick: () -> Unit,
onDeleteClick: () -> Unit,
elevation: Dp = 4.dp,
) = ChannelItem(
index = index,
title = title,
enabled = enabled,
onClick = onEditClick,
elevation = elevation,
) {
IconButton(onClick = { onDeleteClick() }) {
Icon(
@@ -256,9 +239,7 @@ fun ChannelSettingsItemList(
items = settingsListInput,
dragDropState = dragDropState,
) { index, channel, isDragging ->
val elevation by animateDpAsState(if (isDragging) 8.dp else 4.dp, label = "drag")
ChannelCard(
elevation = elevation,
index = index,
title = channel.name.ifEmpty { modemPresetName },
enabled = enabled,

View File

@@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -94,7 +94,7 @@ fun DetectionSensorConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -130,7 +130,7 @@ fun DetectionSensorConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -174,7 +174,7 @@ fun DetectionSensorConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -186,7 +186,7 @@ fun DetectionSensorConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -25,11 +25,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.AlertDialog
import androidx.compose.material.Checkbox
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -207,7 +207,7 @@ fun DeviceConfigItemList(
},
summary = stringResource(id = deviceInput.role.stringRes),
)
Divider()
HorizontalDivider()
}
item {
@@ -242,7 +242,7 @@ fun DeviceConfigItemList(
onItemSelected = { deviceInput = deviceInput.copy { rebroadcastMode = it } },
summary = stringResource(id = deviceInput.rebroadcastMode.stringRes),
)
Divider()
HorizontalDivider()
}
item {
@@ -265,7 +265,7 @@ fun DeviceConfigItemList(
enabled = enabled,
onCheckedChange = { deviceInput = deviceInput.copy { doubleTapAsButtonPress = it } }
)
Divider()
HorizontalDivider()
}
item {
@@ -276,7 +276,7 @@ fun DeviceConfigItemList(
enabled = enabled,
onCheckedChange = { deviceInput = deviceInput.copy { disableTripleClick = it } }
)
Divider()
HorizontalDivider()
}
item {
@@ -304,7 +304,7 @@ fun DeviceConfigItemList(
enabled = enabled,
onCheckedChange = { deviceInput = deviceInput.copy { ledHeartbeatDisabled = it } }
)
Divider()
HorizontalDivider()
}
item {

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -101,7 +101,7 @@ fun DisplayConfigItemList(
onItemSelected = { displayInput = displayInput.copy { gpsFormat = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -123,7 +123,7 @@ fun DisplayConfigItemList(
onCheckedChange = { displayInput = displayInput.copy { compassNorthTop = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -133,7 +133,7 @@ fun DisplayConfigItemList(
onCheckedChange = { displayInput = displayInput.copy { flipScreen = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
DropDownPreference(
@@ -146,7 +146,7 @@ fun DisplayConfigItemList(
onItemSelected = { displayInput = displayInput.copy { units = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
DropDownPreference(
@@ -159,7 +159,7 @@ fun DisplayConfigItemList(
onItemSelected = { displayInput = displayInput.copy { oled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
DropDownPreference(
@@ -172,7 +172,7 @@ fun DisplayConfigItemList(
onItemSelected = { displayInput = displayInput.copy { displaymode = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -182,7 +182,7 @@ fun DisplayConfigItemList(
onCheckedChange = { displayInput = displayInput.copy { headingBold = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -192,7 +192,7 @@ fun DisplayConfigItemList(
onCheckedChange = { displayInput = displayInput.copy { wakeOnTapOrMotion = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
DropDownPreference(
@@ -205,7 +205,7 @@ fun DisplayConfigItemList(
onItemSelected = { displayInput = displayInput.copy { compassOrientation = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -26,11 +26,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -70,7 +69,6 @@ fun EditChannelDialog(
AlertDialog(
onDismissRequest = onDismissRequest,
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
text = {
Column(modifier.fillMaxWidth()) {
EditTextPreference(
@@ -139,7 +137,7 @@ fun EditChannelDialog(
)
}
},
buttons = {
confirmButton = {
FlowRow(
modifier = modifier
.fillMaxWidth()

View File

@@ -25,12 +25,12 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.remember
@@ -67,12 +67,11 @@ fun EditDeviceProfileDialog(
AlertDialog(
onDismissRequest = onDismiss,
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
text = {
Column(modifier.fillMaxWidth()) {
Text(
text = title,
style = MaterialTheme.typography.h6.copy(
style = MaterialTheme.typography.titleLarge.copy(
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
),
@@ -80,7 +79,7 @@ fun EditDeviceProfileDialog(
.fillMaxWidth()
.padding(bottom = 16.dp),
)
Divider()
HorizontalDivider()
state.keys.sortedBy { it.number }.forEach { field ->
SwitchPreference(
title = field.name,
@@ -90,10 +89,10 @@ fun EditDeviceProfileDialog(
padding = PaddingValues(0.dp)
)
}
Divider()
HorizontalDivider()
}
},
buttons = {
confirmButton = {
FlowRow(
modifier = modifier
.fillMaxWidth()

View File

@@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -114,7 +114,7 @@ fun ExternalNotificationConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -127,7 +127,7 @@ fun ExternalNotificationConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -153,7 +153,7 @@ fun ExternalNotificationConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -166,7 +166,7 @@ fun ExternalNotificationConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -179,7 +179,7 @@ fun ExternalNotificationConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -205,7 +205,7 @@ fun ExternalNotificationConfigItemList(
)
}
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -231,7 +231,7 @@ fun ExternalNotificationConfigItemList(
)
}
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -294,7 +294,7 @@ fun ExternalNotificationConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -103,7 +103,7 @@ fun LoRaConfigItemList(
onCheckedChange = { loraInput = loraInput.copy { usePreset = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
if (loraInput.usePreset) {
item {
@@ -117,7 +117,7 @@ fun LoRaConfigItemList(
onItemSelected = { loraInput = loraInput.copy { modemPreset = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
} else {
item {
EditTextPreference(
@@ -169,7 +169,7 @@ fun LoRaConfigItemList(
onItemSelected = { loraInput = loraInput.copy { region = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -189,7 +189,7 @@ fun LoRaConfigItemList(
onCheckedChange = { loraInput = loraInput.copy { txEnabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SignedIntegerEditTextPreference(
@@ -225,7 +225,7 @@ fun LoRaConfigItemList(
onCheckedChange = { loraInput = loraInput.copy { overrideDutyCycle = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditListPreference(
@@ -251,7 +251,7 @@ fun LoRaConfigItemList(
onCheckedChange = { loraInput = loraInput.copy { sx126XRxBoostedGain = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
var isFocused by remember { mutableStateOf(false) }
@@ -274,7 +274,7 @@ fun LoRaConfigItemList(
onCheckedChange = { loraInput = loraInput.copy { paFanDisabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
}
item {
@@ -285,7 +285,7 @@ fun LoRaConfigItemList(
onCheckedChange = { loraInput = loraInput.copy { ignoreMqtt = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -295,7 +295,7 @@ fun LoRaConfigItemList(
onCheckedChange = { loraInput = loraInput.copy { configOkToMqtt = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -22,7 +22,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -94,7 +94,7 @@ fun MQTTConfigItemList(
onCheckedChange = { mqttInput = mqttInput.copy { this.enabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -145,7 +145,7 @@ fun MQTTConfigItemList(
onCheckedChange = { mqttInput = mqttInput.copy { encryptionEnabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -155,7 +155,7 @@ fun MQTTConfigItemList(
onCheckedChange = { mqttInput = mqttInput.copy { jsonEnabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -165,7 +165,7 @@ fun MQTTConfigItemList(
onCheckedChange = { mqttInput = mqttInput.copy { tlsEnabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -190,7 +190,7 @@ fun MQTTConfigItemList(
onCheckedChange = { mqttInput = mqttInput.copy { proxyToClientEnabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PositionPrecisionPreference(
@@ -207,7 +207,7 @@ fun MQTTConfigItemList(
modifier = Modifier.padding(horizontal = 16.dp)
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -89,7 +89,7 @@ fun NeighborInfoConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -113,7 +113,7 @@ fun NeighborInfoConfigItemList(
neighborInfoInput = neighborInfoInput.copy { transmitOverLora = it }
}
)
Divider()
HorizontalDivider()
}
item {

View File

@@ -25,9 +25,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Button
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -149,7 +149,7 @@ fun NetworkConfigItemList(
enabled = enabled && hasWifi,
onCheckedChange = { networkInput = networkInput.copy { wifiEnabled = it } }
)
Divider()
HorizontalDivider()
}
item {
@@ -200,7 +200,7 @@ fun NetworkConfigItemList(
enabled = enabled && hasEthernet,
onCheckedChange = { networkInput = networkInput.copy { ethEnabled = it } }
)
Divider()
HorizontalDivider()
}
item {
@@ -247,7 +247,7 @@ fun NetworkConfigItemList(
selectedItem = networkInput.addressMode,
onItemSelected = { networkInput = networkInput.copy { addressMode = it } }
)
Divider()
HorizontalDivider()
}
item {
@@ -301,7 +301,7 @@ fun NetworkConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
if (hasEthernet || hasWifi) {
item {
PreferenceCategory(text = stringResource(R.string.udp_config))
@@ -321,7 +321,7 @@ fun NetworkConfigItemList(
)
}
item { Divider() }
item { HorizontalDivider() }
}
item {
PreferenceFooter(

View File

@@ -24,11 +24,10 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -48,7 +47,6 @@ fun <T> PacketResponseStateDialog(
AlertDialog(
onDismissRequest = {},
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
title = {
Column(
modifier = Modifier.fillMaxWidth(),
@@ -65,7 +63,6 @@ fun <T> PacketResponseStateDialog(
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
color = MaterialTheme.colors.onSurface,
)
if (state.total == state.completed) onComplete()
}
@@ -78,7 +75,7 @@ fun <T> PacketResponseStateDialog(
}
}
},
buttons = {
confirmButton = {
Row(
modifier = Modifier
.fillMaxWidth()

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -90,7 +90,7 @@ fun PaxcounterConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -127,7 +127,7 @@ fun PositionConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
if (positionInput.positionBroadcastSmartEnabled) {
item {
@@ -163,7 +163,7 @@ fun PositionConfigItemList(
onCheckedChange = { positionInput = positionInput.copy { fixedPosition = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
if (positionInput.fixedPosition) {
item {
@@ -216,7 +216,7 @@ fun PositionConfigItemList(
onItemSelected = { positionInput = positionInput.copy { gpsMode = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -239,7 +239,7 @@ fun PositionConfigItemList(
onItemSelected = { positionInput = positionInput.copy { positionFlags = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -87,7 +87,7 @@ fun PowerConfigItemList(
onCheckedChange = { powerInput = powerInput.copy { isPowerSaving = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -87,7 +87,7 @@ fun RangeTestConfigItemList(
onCheckedChange = { rangeTestInput = rangeTestInput.copy { this.enabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -107,7 +107,7 @@ fun RangeTestConfigItemList(
onCheckedChange = { rangeTestInput = rangeTestInput.copy { save = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -89,7 +89,7 @@ fun RemoteHardwareConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -101,7 +101,7 @@ fun RemoteHardwareConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditListPreference(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -148,7 +148,7 @@ fun SecurityConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -158,7 +158,7 @@ fun SecurityConfigItemList(
onCheckedChange = { securityInput = securityInput.copy { serialEnabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -170,7 +170,7 @@ fun SecurityConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -182,7 +182,7 @@ fun SecurityConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -88,7 +88,7 @@ fun SerialConfigItemList(
onCheckedChange = { serialInput = serialInput.copy { this.enabled = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -98,7 +98,7 @@ fun SerialConfigItemList(
onCheckedChange = { serialInput = serialInput.copy { echo = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -131,7 +131,7 @@ fun SerialConfigItemList(
onItemSelected = { serialInput = serialInput.copy { baud = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -154,7 +154,7 @@ fun SerialConfigItemList(
onItemSelected = { serialInput = serialInput.copy { mode = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -166,7 +166,7 @@ fun SerialConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -89,7 +89,7 @@ fun StoreForwardConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -99,7 +99,7 @@ fun StoreForwardConfigItemList(
onCheckedChange = { storeForwardInput = storeForwardInput.copy { heartbeat = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -143,7 +143,7 @@ fun StoreForwardConfigItemList(
onCheckedChange = { storeForwardInput = storeForwardInput.copy { isServer = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -113,7 +113,7 @@ fun TelemetryConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -125,7 +125,7 @@ fun TelemetryConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -137,7 +137,7 @@ fun TelemetryConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
@@ -149,7 +149,7 @@ fun TelemetryConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -173,7 +173,7 @@ fun TelemetryConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -197,7 +197,7 @@ fun TelemetryConfigItemList(
}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -87,7 +87,7 @@ fun UserConfigItemList(
onClick = {}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
EditTextPreference(
@@ -128,17 +128,18 @@ fun UserConfigItemList(
onClick = {}
)
}
item { Divider() }
item { HorizontalDivider() }
item {
SwitchPreference(
title = stringResource(R.string.licensed_amateur_radio),
summary = stringResource(R.string.licensed_amateur_radio_text),
checked = userInput.isLicensed,
enabled = enabled,
onCheckedChange = { userInput = userInput.copy { isLicensed = it } }
)
}
item { Divider() }
item { HorizontalDivider() }
item {
PreferenceFooter(

View File

@@ -16,12 +16,226 @@
*/
package com.geeksville.mesh.ui.theme
import androidx.compose.ui.graphics.Color
val MeshtasticGreen = Color(0xFF67EA94)
val MeshtasticAlt = Color(0xFF2C2D3C)
val HyperlinkBlue = Color(0xFF43C3B0)
val InfantryBlue = Color(red = 75, green = 119, blue = 190)
val Orange = Color(red = 247, green = 147, blue = 26)
val Orange = Color(red = 247, green = 147, blue = 26)
val primaryLight = Color(0xFF306A42)
val onPrimaryLight = Color(0xFFFFFFFF)
val primaryContainerLight = Color(0xFFB3F1BF)
val onPrimaryContainerLight = Color(0xFF00210D)
val secondaryLight = Color(0xFF506353)
val onSecondaryLight = Color(0xFFFFFFFF)
val secondaryContainerLight = Color(0xFFD2E8D3)
val onSecondaryContainerLight = Color(0xFF0D1F12)
val tertiaryLight = Color(0xFF3A656E)
val onTertiaryLight = Color(0xFFFFFFFF)
val tertiaryContainerLight = Color(0xFFBEEAF6)
val onTertiaryContainerLight = Color(0xFF001F25)
val errorLight = Color(0xFFBA1A1A)
val onErrorLight = Color(0xFFFFFFFF)
val errorContainerLight = Color(0xFFFFDAD6)
val onErrorContainerLight = Color(0xFF410002)
val backgroundLight = Color(0xFFF6FBF3)
val onBackgroundLight = Color(0xFF181D18)
val surfaceLight = Color(0xFFF6FBF3)
val onSurfaceLight = Color(0xFF181D18)
val surfaceVariantLight = Color(0xFFDDE5DA)
val onSurfaceVariantLight = Color(0xFF414941)
val outlineLight = Color(0xFF717971)
val outlineVariantLight = Color(0xFFC1C9BF)
val scrimLight = Color(0xFF000000)
val inverseSurfaceLight = Color(0xFF2D322D)
val inverseOnSurfaceLight = Color(0xFFEEF2EA)
val inversePrimaryLight = Color(0xFF97D5A5)
val surfaceDimLight = Color(0xFFD7DBD4)
val surfaceBrightLight = Color(0xFFF6FBF3)
val surfaceContainerLowestLight = Color(0xFFFFFFFF)
val surfaceContainerLowLight = Color(0xFFF0F5ED)
val surfaceContainerLight = Color(0xFFEBEFE7)
val surfaceContainerHighLight = Color(0xFFE5EAE2)
val surfaceContainerHighestLight = Color(0xFFDFE4DC)
val primaryLightMediumContrast = Color(0xFF0F4D29)
val onPrimaryLightMediumContrast = Color(0xFFFFFFFF)
val primaryContainerLightMediumContrast = Color(0xFF478157)
val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF)
val secondaryLightMediumContrast = Color(0xFF344738)
val onSecondaryLightMediumContrast = Color(0xFFFFFFFF)
val secondaryContainerLightMediumContrast = Color(0xFF657A68)
val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF)
val tertiaryLightMediumContrast = Color(0xFF1C4952)
val onTertiaryLightMediumContrast = Color(0xFFFFFFFF)
val tertiaryContainerLightMediumContrast = Color(0xFF517B85)
val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF)
val errorLightMediumContrast = Color(0xFF8C0009)
val onErrorLightMediumContrast = Color(0xFFFFFFFF)
val errorContainerLightMediumContrast = Color(0xFFDA342E)
val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF)
val backgroundLightMediumContrast = Color(0xFFF6FBF3)
val onBackgroundLightMediumContrast = Color(0xFF181D18)
val surfaceLightMediumContrast = Color(0xFFF6FBF3)
val onSurfaceLightMediumContrast = Color(0xFF181D18)
val surfaceVariantLightMediumContrast = Color(0xFFDDE5DA)
val onSurfaceVariantLightMediumContrast = Color(0xFF3D453D)
val outlineLightMediumContrast = Color(0xFF596159)
val outlineVariantLightMediumContrast = Color(0xFF757D74)
val scrimLightMediumContrast = Color(0xFF000000)
val inverseSurfaceLightMediumContrast = Color(0xFF2D322D)
val inverseOnSurfaceLightMediumContrast = Color(0xFFEEF2EA)
val inversePrimaryLightMediumContrast = Color(0xFF97D5A5)
val surfaceDimLightMediumContrast = Color(0xFFD7DBD4)
val surfaceBrightLightMediumContrast = Color(0xFFF6FBF3)
val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF)
val surfaceContainerLowLightMediumContrast = Color(0xFFF0F5ED)
val surfaceContainerLightMediumContrast = Color(0xFFEBEFE7)
val surfaceContainerHighLightMediumContrast = Color(0xFFE5EAE2)
val surfaceContainerHighestLightMediumContrast = Color(0xFFDFE4DC)
val primaryLightHighContrast = Color(0xFF002911)
val onPrimaryLightHighContrast = Color(0xFFFFFFFF)
val primaryContainerLightHighContrast = Color(0xFF0F4D29)
val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF)
val secondaryLightHighContrast = Color(0xFF142619)
val onSecondaryLightHighContrast = Color(0xFFFFFFFF)
val secondaryContainerLightHighContrast = Color(0xFF344738)
val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF)
val tertiaryLightHighContrast = Color(0xFF00262E)
val onTertiaryLightHighContrast = Color(0xFFFFFFFF)
val tertiaryContainerLightHighContrast = Color(0xFF1C4952)
val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF)
val errorLightHighContrast = Color(0xFF4E0002)
val onErrorLightHighContrast = Color(0xFFFFFFFF)
val errorContainerLightHighContrast = Color(0xFF8C0009)
val onErrorContainerLightHighContrast = Color(0xFFFFFFFF)
val backgroundLightHighContrast = Color(0xFFF6FBF3)
val onBackgroundLightHighContrast = Color(0xFF181D18)
val surfaceLightHighContrast = Color(0xFFF6FBF3)
val onSurfaceLightHighContrast = Color(0xFF000000)
val surfaceVariantLightHighContrast = Color(0xFFDDE5DA)
val onSurfaceVariantLightHighContrast = Color(0xFF1E261F)
val outlineLightHighContrast = Color(0xFF3D453D)
val outlineVariantLightHighContrast = Color(0xFF3D453D)
val scrimLightHighContrast = Color(0xFF000000)
val inverseSurfaceLightHighContrast = Color(0xFF2D322D)
val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF)
val inversePrimaryLightHighContrast = Color(0xFFBCFBC8)
val surfaceDimLightHighContrast = Color(0xFFD7DBD4)
val surfaceBrightLightHighContrast = Color(0xFFF6FBF3)
val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF)
val surfaceContainerLowLightHighContrast = Color(0xFFF0F5ED)
val surfaceContainerLightHighContrast = Color(0xFFEBEFE7)
val surfaceContainerHighLightHighContrast = Color(0xFFE5EAE2)
val surfaceContainerHighestLightHighContrast = Color(0xFFDFE4DC)
val primaryDark = Color(0xFF97D5A5)
val onPrimaryDark = Color(0xFF00391A)
val primaryContainerDark = Color(0xFF15512C)
val onPrimaryContainerDark = Color(0xFFB3F1BF)
val secondaryDark = Color(0xFFB6CCB8)
val onSecondaryDark = Color(0xFF223526)
val secondaryContainerDark = Color(0xFF384B3C)
val onSecondaryContainerDark = Color(0xFFD2E8D3)
val tertiaryDark = Color(0xFFA2CED9)
val onTertiaryDark = Color(0xFF01363F)
val tertiaryContainerDark = Color(0xFF204D56)
val onTertiaryContainerDark = Color(0xFFBEEAF6)
val errorDark = Color(0xFFFFB4AB)
val onErrorDark = Color(0xFF690005)
val errorContainerDark = Color(0xFF93000A)
val onErrorContainerDark = Color(0xFFFFDAD6)
val backgroundDark = Color(0xFF101510)
val onBackgroundDark = Color(0xFFDFE4DC)
val surfaceDark = Color(0xFF101510)
val onSurfaceDark = Color(0xFFDFE4DC)
val surfaceVariantDark = Color(0xFF414941)
val onSurfaceVariantDark = Color(0xFFC1C9BF)
val outlineDark = Color(0xFF8B938A)
val outlineVariantDark = Color(0xFF414941)
val scrimDark = Color(0xFF000000)
val inverseSurfaceDark = Color(0xFFDFE4DC)
val inverseOnSurfaceDark = Color(0xFF2D322D)
val inversePrimaryDark = Color(0xFF306A42)
val surfaceDimDark = Color(0xFF101510)
val surfaceBrightDark = Color(0xFF353A35)
val surfaceContainerLowestDark = Color(0xFF0A0F0B)
val surfaceContainerLowDark = Color(0xFF181D18)
val surfaceContainerDark = Color(0xFF1C211C)
val surfaceContainerHighDark = Color(0xFF262B26)
val surfaceContainerHighestDark = Color(0xFF313631)
val primaryDarkMediumContrast = Color(0xFF9BD9A9)
val onPrimaryDarkMediumContrast = Color(0xFF001B09)
val primaryContainerDarkMediumContrast = Color(0xFF639D72)
val onPrimaryContainerDarkMediumContrast = Color(0xFF000000)
val secondaryDarkMediumContrast = Color(0xFFBBD0BC)
val onSecondaryDarkMediumContrast = Color(0xFF081A0D)
val secondaryContainerDarkMediumContrast = Color(0xFF819683)
val onSecondaryContainerDarkMediumContrast = Color(0xFF000000)
val tertiaryDarkMediumContrast = Color(0xFFA6D2DD)
val onTertiaryDarkMediumContrast = Color(0xFF00191F)
val tertiaryContainerDarkMediumContrast = Color(0xFF6D97A2)
val onTertiaryContainerDarkMediumContrast = Color(0xFF000000)
val errorDarkMediumContrast = Color(0xFFFFBAB1)
val onErrorDarkMediumContrast = Color(0xFF370001)
val errorContainerDarkMediumContrast = Color(0xFFFF5449)
val onErrorContainerDarkMediumContrast = Color(0xFF000000)
val backgroundDarkMediumContrast = Color(0xFF101510)
val onBackgroundDarkMediumContrast = Color(0xFFDFE4DC)
val surfaceDarkMediumContrast = Color(0xFF101510)
val onSurfaceDarkMediumContrast = Color(0xFFF8FCF4)
val surfaceVariantDarkMediumContrast = Color(0xFF414941)
val onSurfaceVariantDarkMediumContrast = Color(0xFFC5CDC3)
val outlineDarkMediumContrast = Color(0xFF9DA59C)
val outlineVariantDarkMediumContrast = Color(0xFF7D857D)
val scrimDarkMediumContrast = Color(0xFF000000)
val inverseSurfaceDarkMediumContrast = Color(0xFFDFE4DC)
val inverseOnSurfaceDarkMediumContrast = Color(0xFF262B26)
val inversePrimaryDarkMediumContrast = Color(0xFF16522E)
val surfaceDimDarkMediumContrast = Color(0xFF101510)
val surfaceBrightDarkMediumContrast = Color(0xFF353A35)
val surfaceContainerLowestDarkMediumContrast = Color(0xFF0A0F0B)
val surfaceContainerLowDarkMediumContrast = Color(0xFF181D18)
val surfaceContainerDarkMediumContrast = Color(0xFF1C211C)
val surfaceContainerHighDarkMediumContrast = Color(0xFF262B26)
val surfaceContainerHighestDarkMediumContrast = Color(0xFF313631)
val primaryDarkHighContrast = Color(0xFFEFFFEE)
val onPrimaryDarkHighContrast = Color(0xFF000000)
val primaryContainerDarkHighContrast = Color(0xFF9BD9A9)
val onPrimaryContainerDarkHighContrast = Color(0xFF000000)
val secondaryDarkHighContrast = Color(0xFFEFFFEE)
val onSecondaryDarkHighContrast = Color(0xFF000000)
val secondaryContainerDarkHighContrast = Color(0xFFBBD0BC)
val onSecondaryContainerDarkHighContrast = Color(0xFF000000)
val tertiaryDarkHighContrast = Color(0xFFF3FCFF)
val onTertiaryDarkHighContrast = Color(0xFF000000)
val tertiaryContainerDarkHighContrast = Color(0xFFA6D2DD)
val onTertiaryContainerDarkHighContrast = Color(0xFF000000)
val errorDarkHighContrast = Color(0xFFFFF9F9)
val onErrorDarkHighContrast = Color(0xFF000000)
val errorContainerDarkHighContrast = Color(0xFFFFBAB1)
val onErrorContainerDarkHighContrast = Color(0xFF000000)
val backgroundDarkHighContrast = Color(0xFF101510)
val onBackgroundDarkHighContrast = Color(0xFFDFE4DC)
val surfaceDarkHighContrast = Color(0xFF101510)
val onSurfaceDarkHighContrast = Color(0xFFFFFFFF)
val surfaceVariantDarkHighContrast = Color(0xFF414941)
val onSurfaceVariantDarkHighContrast = Color(0xFFF5FDF2)
val outlineDarkHighContrast = Color(0xFFC5CDC3)
val outlineVariantDarkHighContrast = Color(0xFFC5CDC3)
val scrimDarkHighContrast = Color(0xFF000000)
val inverseSurfaceDarkHighContrast = Color(0xFFDFE4DC)
val inverseOnSurfaceDarkHighContrast = Color(0xFF000000)
val inversePrimaryDarkHighContrast = Color(0xFF003216)
val surfaceDimDarkHighContrast = Color(0xFF101510)
val surfaceBrightDarkHighContrast = Color(0xFF353A35)
val surfaceContainerLowestDarkHighContrast = Color(0xFF0A0F0B)
val surfaceContainerLowDarkHighContrast = Color(0xFF181D18)
val surfaceContainerDarkHighContrast = Color(0xFF1C211C)
val surfaceContainerHighDarkHighContrast = Color(0xFF262B26)
val surfaceContainerHighestDarkHighContrast = Color(0xFF313631)

View File

@@ -15,39 +15,295 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("UnusedPrivateProperty")
package com.geeksville.mesh.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorPalette = darkColors(
primary = MeshtasticGreen,
primaryVariant = MeshtasticGreen,
secondary = MeshtasticGreen,
private val lightScheme = lightColorScheme(
primary = primaryLight,
onPrimary = onPrimaryLight,
primaryContainer = primaryContainerLight,
onPrimaryContainer = onPrimaryContainerLight,
secondary = secondaryLight,
onSecondary = onSecondaryLight,
secondaryContainer = secondaryContainerLight,
onSecondaryContainer = onSecondaryContainerLight,
tertiary = tertiaryLight,
onTertiary = onTertiaryLight,
tertiaryContainer = tertiaryContainerLight,
onTertiaryContainer = onTertiaryContainerLight,
error = errorLight,
onError = onErrorLight,
errorContainer = errorContainerLight,
onErrorContainer = onErrorContainerLight,
background = backgroundLight,
onBackground = onBackgroundLight,
surface = surfaceLight,
onSurface = onSurfaceLight,
surfaceVariant = surfaceVariantLight,
onSurfaceVariant = onSurfaceVariantLight,
outline = outlineLight,
outlineVariant = outlineVariantLight,
scrim = scrimLight,
inverseSurface = inverseSurfaceLight,
inverseOnSurface = inverseOnSurfaceLight,
inversePrimary = inversePrimaryLight,
surfaceDim = surfaceDimLight,
surfaceBright = surfaceBrightLight,
surfaceContainerLowest = surfaceContainerLowestLight,
surfaceContainerLow = surfaceContainerLowLight,
surfaceContainer = surfaceContainerLight,
surfaceContainerHigh = surfaceContainerHighLight,
surfaceContainerHighest = surfaceContainerHighestLight,
)
private val LightColorPalette = lightColors(
primary = MeshtasticGreen,
primaryVariant = MeshtasticGreen,
secondary = MeshtasticGreen,
private val darkScheme = darkColorScheme(
primary = primaryDark,
onPrimary = onPrimaryDark,
primaryContainer = primaryContainerDark,
onPrimaryContainer = onPrimaryContainerDark,
secondary = secondaryDark,
onSecondary = onSecondaryDark,
secondaryContainer = secondaryContainerDark,
onSecondaryContainer = onSecondaryContainerDark,
tertiary = tertiaryDark,
onTertiary = onTertiaryDark,
tertiaryContainer = tertiaryContainerDark,
onTertiaryContainer = onTertiaryContainerDark,
error = errorDark,
onError = onErrorDark,
errorContainer = errorContainerDark,
onErrorContainer = onErrorContainerDark,
background = backgroundDark,
onBackground = onBackgroundDark,
surface = surfaceDark,
onSurface = onSurfaceDark,
surfaceVariant = surfaceVariantDark,
onSurfaceVariant = onSurfaceVariantDark,
outline = outlineDark,
outlineVariant = outlineVariantDark,
scrim = scrimDark,
inverseSurface = inverseSurfaceDark,
inverseOnSurface = inverseOnSurfaceDark,
inversePrimary = inversePrimaryDark,
surfaceDim = surfaceDimDark,
surfaceBright = surfaceBrightDark,
surfaceContainerLowest = surfaceContainerLowestDark,
surfaceContainerLow = surfaceContainerLowDark,
surfaceContainer = surfaceContainerDark,
surfaceContainerHigh = surfaceContainerHighDark,
surfaceContainerHighest = surfaceContainerHighestDark,
)
private val mediumContrastLightColorScheme = lightColorScheme(
primary = primaryLightMediumContrast,
onPrimary = onPrimaryLightMediumContrast,
primaryContainer = primaryContainerLightMediumContrast,
onPrimaryContainer = onPrimaryContainerLightMediumContrast,
secondary = secondaryLightMediumContrast,
onSecondary = onSecondaryLightMediumContrast,
secondaryContainer = secondaryContainerLightMediumContrast,
onSecondaryContainer = onSecondaryContainerLightMediumContrast,
tertiary = tertiaryLightMediumContrast,
onTertiary = onTertiaryLightMediumContrast,
tertiaryContainer = tertiaryContainerLightMediumContrast,
onTertiaryContainer = onTertiaryContainerLightMediumContrast,
error = errorLightMediumContrast,
onError = onErrorLightMediumContrast,
errorContainer = errorContainerLightMediumContrast,
onErrorContainer = onErrorContainerLightMediumContrast,
background = backgroundLightMediumContrast,
onBackground = onBackgroundLightMediumContrast,
surface = surfaceLightMediumContrast,
onSurface = onSurfaceLightMediumContrast,
surfaceVariant = surfaceVariantLightMediumContrast,
onSurfaceVariant = onSurfaceVariantLightMediumContrast,
outline = outlineLightMediumContrast,
outlineVariant = outlineVariantLightMediumContrast,
scrim = scrimLightMediumContrast,
inverseSurface = inverseSurfaceLightMediumContrast,
inverseOnSurface = inverseOnSurfaceLightMediumContrast,
inversePrimary = inversePrimaryLightMediumContrast,
surfaceDim = surfaceDimLightMediumContrast,
surfaceBright = surfaceBrightLightMediumContrast,
surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
surfaceContainerLow = surfaceContainerLowLightMediumContrast,
surfaceContainer = surfaceContainerLightMediumContrast,
surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
)
private val highContrastLightColorScheme = lightColorScheme(
primary = primaryLightHighContrast,
onPrimary = onPrimaryLightHighContrast,
primaryContainer = primaryContainerLightHighContrast,
onPrimaryContainer = onPrimaryContainerLightHighContrast,
secondary = secondaryLightHighContrast,
onSecondary = onSecondaryLightHighContrast,
secondaryContainer = secondaryContainerLightHighContrast,
onSecondaryContainer = onSecondaryContainerLightHighContrast,
tertiary = tertiaryLightHighContrast,
onTertiary = onTertiaryLightHighContrast,
tertiaryContainer = tertiaryContainerLightHighContrast,
onTertiaryContainer = onTertiaryContainerLightHighContrast,
error = errorLightHighContrast,
onError = onErrorLightHighContrast,
errorContainer = errorContainerLightHighContrast,
onErrorContainer = onErrorContainerLightHighContrast,
background = backgroundLightHighContrast,
onBackground = onBackgroundLightHighContrast,
surface = surfaceLightHighContrast,
onSurface = onSurfaceLightHighContrast,
surfaceVariant = surfaceVariantLightHighContrast,
onSurfaceVariant = onSurfaceVariantLightHighContrast,
outline = outlineLightHighContrast,
outlineVariant = outlineVariantLightHighContrast,
scrim = scrimLightHighContrast,
inverseSurface = inverseSurfaceLightHighContrast,
inverseOnSurface = inverseOnSurfaceLightHighContrast,
inversePrimary = inversePrimaryLightHighContrast,
surfaceDim = surfaceDimLightHighContrast,
surfaceBright = surfaceBrightLightHighContrast,
surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
surfaceContainerLow = surfaceContainerLowLightHighContrast,
surfaceContainer = surfaceContainerLightHighContrast,
surfaceContainerHigh = surfaceContainerHighLightHighContrast,
surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
)
private val mediumContrastDarkColorScheme = darkColorScheme(
primary = primaryDarkMediumContrast,
onPrimary = onPrimaryDarkMediumContrast,
primaryContainer = primaryContainerDarkMediumContrast,
onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
secondary = secondaryDarkMediumContrast,
onSecondary = onSecondaryDarkMediumContrast,
secondaryContainer = secondaryContainerDarkMediumContrast,
onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
tertiary = tertiaryDarkMediumContrast,
onTertiary = onTertiaryDarkMediumContrast,
tertiaryContainer = tertiaryContainerDarkMediumContrast,
onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
error = errorDarkMediumContrast,
onError = onErrorDarkMediumContrast,
errorContainer = errorContainerDarkMediumContrast,
onErrorContainer = onErrorContainerDarkMediumContrast,
background = backgroundDarkMediumContrast,
onBackground = onBackgroundDarkMediumContrast,
surface = surfaceDarkMediumContrast,
onSurface = onSurfaceDarkMediumContrast,
surfaceVariant = surfaceVariantDarkMediumContrast,
onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
outline = outlineDarkMediumContrast,
outlineVariant = outlineVariantDarkMediumContrast,
scrim = scrimDarkMediumContrast,
inverseSurface = inverseSurfaceDarkMediumContrast,
inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
inversePrimary = inversePrimaryDarkMediumContrast,
surfaceDim = surfaceDimDarkMediumContrast,
surfaceBright = surfaceBrightDarkMediumContrast,
surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
surfaceContainer = surfaceContainerDarkMediumContrast,
surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
)
private val highContrastDarkColorScheme = darkColorScheme(
primary = primaryDarkHighContrast,
onPrimary = onPrimaryDarkHighContrast,
primaryContainer = primaryContainerDarkHighContrast,
onPrimaryContainer = onPrimaryContainerDarkHighContrast,
secondary = secondaryDarkHighContrast,
onSecondary = onSecondaryDarkHighContrast,
secondaryContainer = secondaryContainerDarkHighContrast,
onSecondaryContainer = onSecondaryContainerDarkHighContrast,
tertiary = tertiaryDarkHighContrast,
onTertiary = onTertiaryDarkHighContrast,
tertiaryContainer = tertiaryContainerDarkHighContrast,
onTertiaryContainer = onTertiaryContainerDarkHighContrast,
error = errorDarkHighContrast,
onError = onErrorDarkHighContrast,
errorContainer = errorContainerDarkHighContrast,
onErrorContainer = onErrorContainerDarkHighContrast,
background = backgroundDarkHighContrast,
onBackground = onBackgroundDarkHighContrast,
surface = surfaceDarkHighContrast,
onSurface = onSurfaceDarkHighContrast,
surfaceVariant = surfaceVariantDarkHighContrast,
onSurfaceVariant = onSurfaceVariantDarkHighContrast,
outline = outlineDarkHighContrast,
outlineVariant = outlineVariantDarkHighContrast,
scrim = scrimDarkHighContrast,
inverseSurface = inverseSurfaceDarkHighContrast,
inverseOnSurface = inverseOnSurfaceDarkHighContrast,
inversePrimary = inversePrimaryDarkHighContrast,
surfaceDim = surfaceDimDarkHighContrast,
surfaceBright = surfaceBrightDarkHighContrast,
surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
surfaceContainerLow = surfaceContainerLowDarkHighContrast,
surfaceContainer = surfaceContainerDarkHighContrast,
surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
)
@Immutable
data class ColorFamily(
val color: Color,
val onColor: Color,
val colorContainer: Color,
val onColorContainer: Color
)
val unspecified_scheme = ColorFamily(
Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
)
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable() () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> darkScheme
else -> lightScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
}
}
MaterialTheme(
colors = colors,
colorScheme = colorScheme,
typography = AppTypography,
content = content
)
}
const val MODE_DYNAMIC = 6969420

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.theme
import androidx.compose.material3.Typography
val AppTypography = Typography()

View File

@@ -580,7 +580,8 @@
<string name="long_name">Long name</string>
<string name="short_name">Short name</string>
<string name="hardware_model">Hardware model</string>
<string name="licensed_amateur_radio">Licensed amateur radio</string>
<string name="licensed_amateur_radio">Licensed amateur radio (HAM)</string>
<string name="licensed_amateur_radio_text">Enabling this option disables encryption and is not compatible with the default Meshtastic network.</string>
<string name="dew_point">Dew Point</string>
<string name="pressure">Pressure</string>
<string name="gas_resistance">Gas Resistance</string>
@@ -607,4 +608,5 @@
<string name="alt">Alt</string>
<string name="set_region">Set Region</string>
<string name="unmute">Unmute</string>
<string name="dynamic">Dynamic</string>
</resources>

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (c) 2025 Meshtastic LLC
This program is free software: you can redistribute it and/or modify
@@ -19,7 +18,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar"/>
<style name="AppTheme" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
// Set the splash screen background, animated icon, and animation duration.
@@ -28,11 +31,13 @@
// Use windowSplashScreenAnimatedIcon to add either a drawable or an
// animated drawable. One of these is required.
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_launcher2_foreground</item>
<item name="windowSplashScreenAnimationDuration">1000</item> # Required for
<item name="windowSplashScreenAnimationDuration">1000</item>
# Required for
# animated icons
// Set the theme of the Activity that directly follows your splash screen.
<item name="postSplashScreenTheme">@style/AppTheme</item> # Required.
<item name="postSplashScreenTheme">@style/AppTheme</item>
# Required.
</style>
</resources>

View File

@@ -91,7 +91,6 @@
<ID>FinalNewline:BluetoothRepositoryModule.kt$com.geeksville.mesh.repository.bluetooth.BluetoothRepositoryModule.kt</ID>
<ID>FinalNewline:BluetoothViewModel.kt$com.geeksville.mesh.model.BluetoothViewModel.kt</ID>
<ID>FinalNewline:BootCompleteReceiver.kt$com.geeksville.mesh.service.BootCompleteReceiver.kt</ID>
<ID>FinalNewline:Color.kt$com.geeksville.mesh.ui.theme.Color.kt</ID>
<ID>FinalNewline:CoroutineDispatchers.kt$com.geeksville.mesh.CoroutineDispatchers.kt</ID>
<ID>FinalNewline:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt</ID>
<ID>FinalNewline:CustomTileSource.kt$com.geeksville.mesh.model.map.CustomTileSource.kt</ID>
@@ -111,7 +110,6 @@
<ID>FinalNewline:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt</ID>
<ID>FinalNewline:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt</ID>
<ID>FinalNewline:OnlineTileSourceAuth.kt$com.geeksville.mesh.model.map.OnlineTileSourceAuth.kt</ID>
<ID>FinalNewline:PreferenceCategory.kt$com.geeksville.mesh.ui.components.PreferenceCategory.kt</ID>
<ID>FinalNewline:PreviewParameterProviders.kt$com.geeksville.mesh.ui.preview.PreviewParameterProviders.kt</ID>
<ID>FinalNewline:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt</ID>
<ID>FinalNewline:QuickChatActionRepository.kt$com.geeksville.mesh.database.QuickChatActionRepository.kt</ID>
@@ -159,7 +157,6 @@
<ID>LongMethod:PowerConfigItemList.kt$@Composable fun PowerConfigItemList( powerConfig: PowerConfig, enabled: Boolean, onSaveClicked: (PowerConfig) -&gt; Unit, )</ID>
<ID>LongMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
<ID>LongMethod:SerialConfigItemList.kt$@Composable fun SerialConfigItemList( serialConfig: SerialConfig, enabled: Boolean, onSaveClicked: (SerialConfig) -&gt; Unit, )</ID>
<ID>LongMethod:SettingsFragment.kt$SettingsFragment$private fun initCommonUI()</ID>
<ID>LongMethod:StoreForwardConfigItemList.kt$@Composable fun StoreForwardConfigItemList( storeForwardConfig: StoreForwardConfig, enabled: Boolean, onSaveClicked: (StoreForwardConfig) -&gt; Unit, )</ID>
<ID>LongMethod:TelemetryConfigItemList.kt$@Composable fun TelemetryConfigItemList( telemetryConfig: TelemetryConfig, enabled: Boolean, onSaveClicked: (TelemetryConfig) -&gt; Unit, )</ID>
<ID>LongMethod:UIState.kt$UIViewModel$fun saveMessagesCSV(uri: Uri)</ID>
@@ -291,21 +288,6 @@
<ID>MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$360.0</ID>
<ID>MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$4</ID>
<ID>MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$5</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.BLUETOOTH$6</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.DISPLAY$4</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.LORA$5</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.NETWORK$3</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.SECURITY$7</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.AMBIENT_LIGHTING$10</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.AUDIO$7</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.CANNED_MESSAGE$6</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.DETECTION_SENSOR$11</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.NEIGHBOR_INFO$9</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.PAXCOUNTER$12</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.RANGE_TEST$4</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.REMOTE_HARDWARE$8</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.STORE_FORWARD$3</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.TELEMETRY$5</ID>
<ID>MagicNumber:NodeInfo.kt$DeviceMetrics.Companion$1000</ID>
<ID>MagicNumber:NodeInfo.kt$EnvironmentMetrics.Companion$1000</ID>
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0.114</ID>
@@ -433,9 +415,6 @@
<ID>MaxLineLength:SafeBluetooth.kt$SafeBluetooth.&lt;no name provided&gt;$// to do an autoconnection attempt. When that attempt succeeds/fails the normal callbacks will be called</ID>
<ID>MaxLineLength:ServiceClient.kt$ServiceClient$// Some phones seem to ahve a race where if you unbind and quickly rebind bindService returns false. Try</ID>
<ID>MaxLineLength:ServiceClient.kt$ServiceClient.&lt;no name provided&gt;$// If we start to close a service, it seems that there is a possibility a onServiceConnected event is the queue</ID>
<ID>MaxLineLength:SettingsFragment.kt$SettingsFragment$// Note: we pull this into a tempvar, because otherwise some other thread can change selectedAddress after our null check</ID>
<ID>MaxLineLength:SettingsFragment.kt$SettingsFragment$deviceSelectIPAddress.isChecked = scanModel.onSelected(BTScanModel.DeviceListEntry("", "t" + inputIPAddress.text, true))</ID>
<ID>MaxLineLength:SettingsFragment.kt$SettingsFragment$if</ID>
<ID>MaxLineLength:SqlTileWriterExt.kt$SqlTileWriterExt$"select " + DatabaseFileArchive.COLUMN_KEY + "," + COLUMN_EXPIRES + "," + DatabaseFileArchive.COLUMN_PROVIDER + " from " + DatabaseFileArchive.TABLE + " limit ? offset ?"</ID>
<ID>MaxLineLength:StreamInterface.kt$StreamInterface$*</ID>
<ID>MaxLineLength:StreamInterface.kt$StreamInterface$* An interface that assumes we are talking to a meshtastic device over some sort of stream connection (serial or TCP probably)</ID>
@@ -443,7 +422,6 @@
<ID>MaxLineLength:StreamInterface.kt$StreamInterface$deliverPacket()</ID>
<ID>MaxLineLength:StreamInterface.kt$StreamInterface$lostSync()</ID>
<ID>MaxLineLength:StreamInterface.kt$StreamInterface$service.onDisconnect(isPermanent = true)</ID>
<ID>MaxLineLength:TextDividerPreference.kt$color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.Unspecified</ID>
<ID>MaxLineLength:UIState.kt$UIViewModel$// date,time,from,sender name,sender lat,sender long,rx lat,rx long,rx elevation,rx snr,distance,hop limit,payload</ID>
<ID>MaxLineLength:UIState.kt$UIViewModel$writer.appendLine("$rxDateTime,\"$rxFrom\",\"$senderName\",\"$senderLat\",\"$senderLong\",\"$rxLat\",\"$rxLong\",\"$rxAlt\",\"$rxSnr\",\"$dist\",\"$hopLimit\",\"$payload\"")</ID>
<ID>MaxLineLength:UIState.kt$UIViewModel$writer.appendLine("\"date\",\"time\",\"from\",\"sender name\",\"sender lat\",\"sender long\",\"rx lat\",\"rx long\",\"rx elevation\",\"rx snr\",\"distance\",\"hop limit\",\"payload\"")</ID>
@@ -466,14 +444,11 @@
<ID>MultiLineIfElse:ContextServices.kt$invokeFun()</ID>
<ID>MultiLineIfElse:EditListPreference.kt$EditBase64Preference( title = "${index + 1}/$maxCount", value = value, enabled = enabled, keyboardActions = keyboardActions, onValueChange = { listState[index] = it as T onValuesChanged(listState) }, modifier = modifier.fillMaxWidth(), trailingIcon = trailingIcon, )</ID>
<ID>MultiLineIfElse:EditListPreference.kt$EditTextPreference( title = "${index + 1}/$maxCount", value = value, enabled = enabled, keyboardActions = keyboardActions, onValueChanged = { listState[index] = it as T onValuesChanged(listState) }, modifier = modifier.fillMaxWidth(), trailingIcon = trailingIcon, )</ID>
<ID>MultiLineIfElse:EditPasswordPreference.kt$painterResource(R.drawable.ic_twotone_visibility_24)</ID>
<ID>MultiLineIfElse:EditPasswordPreference.kt$painterResource(R.drawable.ic_twotone_visibility_off_24)</ID>
<ID>MultiLineIfElse:EditTextPreference.kt$it.toDoubleOrNull()?.let { double -&gt; valueState = it onValueChanged(double) }</ID>
<ID>MultiLineIfElse:EditTextPreference.kt$it.toFloatOrNull()?.let { float -&gt; valueState = it onValueChanged(float) }</ID>
<ID>MultiLineIfElse:EditTextPreference.kt$it.toUIntOrNull()?.toInt()?.let { int -&gt; valueState = it onValueChanged(int) }</ID>
<ID>MultiLineIfElse:EditTextPreference.kt$onValueChanged(it)</ID>
<ID>MultiLineIfElse:EditTextPreference.kt$valueState = it</ID>
<ID>MultiLineIfElse:EditWaypointDialog.kt$AlertDialog( onDismissRequest = onDismissRequest, shape = RoundedCornerShape(16.dp), backgroundColor = MaterialTheme.colors.background, text = { Column(modifier = modifier.fillMaxWidth()) { Text( text = stringResource(title), style = MaterialTheme.typography.h6.copy( fontWeight = FontWeight.Bold, textAlign = TextAlign.Center, ), modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp), ) EditTextPreference( title = stringResource(R.string.name), value = waypointInput.name, maxSize = 29, // name max_size:30 enabled = true, isError = false, keyboardOptions = KeyboardOptions.Default.copy( keyboardType = KeyboardType.Text, imeAction = ImeAction.Done ), keyboardActions = KeyboardActions(onDone = { /*TODO*/ }), onValueChanged = { waypointInput = waypointInput.copy { name = it } }, trailingIcon = { IconButton(onClick = { showEmojiPickerView = true }) { Text( text = String(Character.toChars(emoji)), modifier = Modifier .background(MaterialTheme.colors.background, CircleShape) .padding(4.dp), fontSize = 24.sp, color = Color.Unspecified.copy(alpha = 1f), ) } }, ) EditTextPreference(title = stringResource(R.string.description), value = waypointInput.description, maxSize = 99, // description max_size:100 enabled = true, isError = false, keyboardOptions = KeyboardOptions.Default.copy( keyboardType = KeyboardType.Text, imeAction = ImeAction.Done ), keyboardActions = KeyboardActions(onDone = { /*TODO*/ }), onValueChanged = { waypointInput = waypointInput.copy { description = it } } ) Row( modifier = Modifier .fillMaxWidth() .size(48.dp), verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(R.drawable.ic_twotone_lock_24), contentDescription = stringResource(R.string.locked), ) Text(stringResource(R.string.locked)) Switch( modifier = Modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), checked = waypointInput.lockedTo != 0, onCheckedChange = { waypointInput = waypointInput.copy { lockedTo = if (it) 1 else 0 } } ) } } }, buttons = { FlowRow( modifier = modifier.padding(start = 20.dp, end = 20.dp, bottom = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.Center, ) { TextButton( modifier = modifier.weight(1f), onClick = onDismissRequest ) { Text(stringResource(R.string.cancel)) } if (waypoint.id != 0) { Button( modifier = modifier.weight(1f), onClick = { onDeleteClicked(waypointInput) }, enabled = waypointInput.name.isNotEmpty(), ) { Text(stringResource(R.string.delete)) } } Button( modifier = modifier.weight(1f), onClick = { onSendClicked(waypointInput) }, enabled = waypointInput.name.isNotEmpty(), ) { Text(stringResource(R.string.send)) } } }, )</ID>
<ID>MultiLineIfElse:Exceptions.kt$Exceptions.errormsg("ignoring exception", ex)</ID>
<ID>MultiLineIfElse:ExpireChecker.kt$ExpireChecker$doExpire()</ID>
<ID>MultiLineIfElse:Logging.kt$Logging$printlog(Log.ERROR, tag(), "$msg (exception ${ex.message})")</ID>
@@ -501,8 +476,6 @@
<ID>MultiLineIfElse:SafeBluetooth.kt$SafeBluetooth.&lt;no name provided&gt;$if (!characteristic.value.contentEquals(reliable)) { errormsg("A reliable write failed!") gatt.abortReliableWrite() completeWork( STATUS_RELIABLE_WRITE_FAILED, characteristic ) // skanky code to indicate failure } else { logAssert(gatt.executeReliableWrite()) // After this execute reliable completes - we can continue with normal operations (see onReliableWriteCompleted) }</ID>
<ID>MultiLineIfElse:SafeBluetooth.kt$SafeBluetooth.&lt;no name provided&gt;$lostConnection("lost connection")</ID>
<ID>MultiLineIfElse:SafeBluetooth.kt$SafeBluetooth.&lt;no name provided&gt;$warn("Received notification from $characteristic, but no handler registered")</ID>
<ID>MultiLineIfElse:TextDividerPreference.kt$Icon( trailingIcon, "trailingIcon", modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), )</ID>
<ID>MultiLineIfElse:UIState.kt$add(channel { role = when (i) { 0 -&gt; ChannelProtos.Channel.Role.PRIMARY in 1..new.lastIndex -&gt; ChannelProtos.Channel.Role.SECONDARY else -&gt; ChannelProtos.Channel.Role.DISABLED } index = i settings = new.getOrNull(i) ?: channelSettings { } })</ID>
<ID>NestedBlockDepth:LanguageUtils.kt$LanguageUtils$fun getLanguageTags(context: Context): Map&lt;String, String&gt;</ID>
<ID>NestedBlockDepth:MainActivity.kt$MainActivity$private fun onMeshConnectionChanged(newConnection: MeshService.ConnectionState)</ID>
<ID>NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedAdmin(fromNodeNum: Int, a: AdminProtos.AdminMessage)</ID>
@@ -516,7 +489,6 @@
<ID>NewLineAtEndOfFile:BluetoothRepositoryModule.kt$com.geeksville.mesh.repository.bluetooth.BluetoothRepositoryModule.kt</ID>
<ID>NewLineAtEndOfFile:BluetoothViewModel.kt$com.geeksville.mesh.model.BluetoothViewModel.kt</ID>
<ID>NewLineAtEndOfFile:BootCompleteReceiver.kt$com.geeksville.mesh.service.BootCompleteReceiver.kt</ID>
<ID>NewLineAtEndOfFile:Color.kt$com.geeksville.mesh.ui.theme.Color.kt</ID>
<ID>NewLineAtEndOfFile:CoroutineDispatchers.kt$com.geeksville.mesh.CoroutineDispatchers.kt</ID>
<ID>NewLineAtEndOfFile:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt</ID>
<ID>NewLineAtEndOfFile:CustomTileSource.kt$com.geeksville.mesh.model.map.CustomTileSource.kt</ID>
@@ -536,7 +508,6 @@
<ID>NewLineAtEndOfFile:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt</ID>
<ID>NewLineAtEndOfFile:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt</ID>
<ID>NewLineAtEndOfFile:OnlineTileSourceAuth.kt$com.geeksville.mesh.model.map.OnlineTileSourceAuth.kt</ID>
<ID>NewLineAtEndOfFile:PreferenceCategory.kt$com.geeksville.mesh.ui.components.PreferenceCategory.kt</ID>
<ID>NewLineAtEndOfFile:PreviewParameterProviders.kt$com.geeksville.mesh.ui.preview.PreviewParameterProviders.kt</ID>
<ID>NewLineAtEndOfFile:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt</ID>
<ID>NewLineAtEndOfFile:QuickChatActionRepository.kt$com.geeksville.mesh.database.QuickChatActionRepository.kt</ID>
@@ -585,11 +556,8 @@
<ID>NoWildcardImports:BluetoothInterface.kt$import com.geeksville.mesh.service.*</ID>
<ID>NoWildcardImports:DeviceVersionTest.kt$import org.junit.Assert.*</ID>
<ID>NoWildcardImports:MockInterface.kt$import com.geeksville.mesh.*</ID>
<ID>NoWildcardImports:PreferenceFooter.kt$import androidx.compose.foundation.layout.*</ID>
<ID>NoWildcardImports:PreferenceFooter.kt$import androidx.compose.material.*</ID>
<ID>NoWildcardImports:SafeBluetooth.kt$import android.bluetooth.*</ID>
<ID>NoWildcardImports:SafeBluetooth.kt$import kotlinx.coroutines.*</ID>
<ID>NoWildcardImports:SettingsFragment.kt$import com.geeksville.mesh.android.*</ID>
<ID>NoWildcardImports:UsbRepository.kt$import kotlinx.coroutines.flow.*</ID>
<ID>OptionalAbstractKeyword:SyncContinuation.kt$Continuation$abstract</ID>
<ID>ParameterListWrapping:AppPrefs.kt$FloatPref$(thisRef: AppPrefs, prop: KProperty&lt;Float&gt;)</ID>
@@ -613,7 +581,6 @@
<ID>SwallowedException:ChannelSet.kt$ex: Throwable</ID>
<ID>SwallowedException:DeviceVersion.kt$DeviceVersion$e: Exception</ID>
<ID>SwallowedException:Exceptions.kt$ex: Throwable</ID>
<ID>SwallowedException:MainActivity.kt$MainActivity$ex: BindFailedException</ID>
<ID>SwallowedException:MeshLog.kt$MeshLog$e: IOException</ID>
<ID>SwallowedException:MeshService.kt$MeshService$e: Exception</ID>
<ID>SwallowedException:MeshService.kt$MeshService$e: TimeoutException</ID>
@@ -667,7 +634,6 @@
<ID>TooManyFunctions:RadioConfigViewModel.kt$RadioConfigViewModel : ViewModelLogging</ID>
<ID>TooManyFunctions:RadioInterfaceService.kt$RadioInterfaceService : Logging</ID>
<ID>TooManyFunctions:SafeBluetooth.kt$SafeBluetooth : LoggingCloseable</ID>
<ID>TooManyFunctions:SettingsFragment.kt$SettingsFragment : ScreenFragmentLogging</ID>
<ID>TooManyFunctions:UIState.kt$UIViewModel : ViewModelLogging</ID>
<ID>TopLevelPropertyNaming:Constants.kt$const val prefix = "com.geeksville.mesh"</ID>
<ID>UnusedPrivateMember:NOAAWmsTileSource.kt$NOAAWmsTileSource$private fun tile2lat(y: Int, z: Int): Double</ID>
@@ -695,11 +661,8 @@
<ID>WildcardImport:BluetoothInterface.kt$import com.geeksville.mesh.service.*</ID>
<ID>WildcardImport:DeviceVersionTest.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:MockInterface.kt$import com.geeksville.mesh.*</ID>
<ID>WildcardImport:PreferenceFooter.kt$import androidx.compose.foundation.layout.*</ID>
<ID>WildcardImport:PreferenceFooter.kt$import androidx.compose.material.*</ID>
<ID>WildcardImport:SafeBluetooth.kt$import android.bluetooth.*</ID>
<ID>WildcardImport:SafeBluetooth.kt$import kotlinx.coroutines.*</ID>
<ID>WildcardImport:SettingsFragment.kt$import com.geeksville.mesh.android.*</ID>
<ID>WildcardImport:UsbRepository.kt$import kotlinx.coroutines.flow.*</ID>
</CurrentIssues>
</SmellBaseline>

View File

@@ -30,6 +30,7 @@ kotlinx-coroutines-android = "1.10.2"
kotlinx-serialization-json = "1.8.1"
lifecycle = "2.9.0"
material = "1.12.0"
material3 = "1.2.0"
mgrs = "2.1.3"
navigation = "2.9.0"
org-eclipse-paho-client-mqttv3 = "1.2.5"
@@ -57,7 +58,7 @@ awesome-app-rating = { group = "com.suddenh4x.ratingdialog", name = "awesome-app
cardview = { group = "androidx.cardview", name = "cardview", version.ref = "cardview" }
coil = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
coil-svg = { group = "io.coil-kt.coil3", name = "coil-svg", version.ref = "coil" }
compose-material = { group = "androidx.compose.material", name = "material" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" }
@@ -128,7 +129,7 @@ zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing-c
androidx = ["core-ktx", "appcompat", "appcompat-resources", "cardview", "fragment-ktx", "actvity-ktx", "fragment-compose", "activity-compose"]
# UI
ui = ["material", "constraintlayout", "viewpager2", "compose-material", "compose-material-icons-extended", "compose-ui-tooling-preview", "compose-runtime-livedata"]
ui = ["material", "constraintlayout", "viewpager2", "compose-material3", "compose-material-icons-extended", "compose-ui-tooling-preview", "compose-runtime-livedata"]
ui-tooling = ["compose-ui-tooling"] #Separate for debugImplementation
# Lifecycle