mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-05 06:33:52 -04:00
feat: material3 (#1862)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) }
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)) }
|
||||
}
|
||||
|
||||
@@ -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)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
22
app/src/main/java/com/geeksville/mesh/ui/theme/Type.kt
Normal file
22
app/src/main/java/com/geeksville/mesh/ui/theme/Type.kt
Normal 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()
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) -> 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) -> Unit, )</ID>
|
||||
<ID>LongMethod:SettingsFragment.kt$SettingsFragment$private fun initCommonUI()</ID>
|
||||
<ID>LongMethod:StoreForwardConfigItemList.kt$@Composable fun StoreForwardConfigItemList( storeForwardConfig: StoreForwardConfig, enabled: Boolean, onSaveClicked: (StoreForwardConfig) -> Unit, )</ID>
|
||||
<ID>LongMethod:TelemetryConfigItemList.kt$@Composable fun TelemetryConfigItemList( telemetryConfig: TelemetryConfig, enabled: Boolean, onSaveClicked: (TelemetryConfig) -> 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.<no name provided>$// 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.<no name provided>$// 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 -> valueState = it onValueChanged(double) }</ID>
|
||||
<ID>MultiLineIfElse:EditTextPreference.kt$it.toFloatOrNull()?.let { float -> valueState = it onValueChanged(float) }</ID>
|
||||
<ID>MultiLineIfElse:EditTextPreference.kt$it.toUIntOrNull()?.toInt()?.let { int -> 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.<no name provided>$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.<no name provided>$lostConnection("lost connection")</ID>
|
||||
<ID>MultiLineIfElse:SafeBluetooth.kt$SafeBluetooth.<no name provided>$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 -> ChannelProtos.Channel.Role.PRIMARY in 1..new.lastIndex -> ChannelProtos.Channel.Role.SECONDARY else -> ChannelProtos.Channel.Role.DISABLED } index = i settings = new.getOrNull(i) ?: channelSettings { } })</ID>
|
||||
<ID>NestedBlockDepth:LanguageUtils.kt$LanguageUtils$fun getLanguageTags(context: Context): Map<String, String></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<Float>)</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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user