From 4cba13ea147adfa18d6e95ff0c2f04cc91a3690b Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Sat, 17 May 2025 11:39:53 -0500 Subject: [PATCH] feat: material3 (#1862) --- app/src/main/AndroidManifest.xml | 1 - .../java/com/geeksville/mesh/MainActivity.kt | 192 +++++------- .../com/geeksville/mesh/model/BTScanModel.kt | 54 ++-- .../java/com/geeksville/mesh/model/UIState.kt | 231 +++++++++----- .../repository/radio/RadioInterfaceService.kt | 6 +- .../com/geeksville/mesh/ui/BatteryInfo.kt | 12 +- .../java/com/geeksville/mesh/ui/Channel.kt | 140 +++++---- .../com/geeksville/mesh/ui/ContactItem.kt | 145 +++++---- .../java/com/geeksville/mesh/ui/Contacts.kt | 26 +- .../main/java/com/geeksville/mesh/ui/Debug.kt | 34 +- .../com/geeksville/mesh/ui/LastHeardInfo.kt | 10 +- .../geeksville/mesh/ui/LinkedCoordinates.kt | 6 +- .../main/java/com/geeksville/mesh/ui/Main.kt | 56 +++- .../java/com/geeksville/mesh/ui/NodeDetail.kt | 87 +++--- .../java/com/geeksville/mesh/ui/NodeItem.kt | 295 +++++++++--------- .../java/com/geeksville/mesh/ui/NodeScreen.kt | 4 +- .../java/com/geeksville/mesh/ui/QuickChat.kt | 233 +++++++------- .../java/com/geeksville/mesh/ui/Settings.kt | 46 ++- .../main/java/com/geeksville/mesh/ui/Share.kt | 4 +- .../mesh/ui/components/AlertDialogs.kt | 122 ++++++++ .../mesh/ui/components/AutoLinkText.kt | 2 +- .../mesh/ui/components/BitwisePreference.kt | 10 +- .../mesh/ui/components/BottomSheetDialog.kt | 4 +- .../mesh/ui/components/ClickableTextField.kt | 6 +- .../mesh/ui/components/CommonCharts.kt | 19 +- .../mesh/ui/components/CopyIconButton.kt | 4 +- .../mesh/ui/components/DeviceMetrics.kt | 20 +- .../mesh/ui/components/DropDownPreference.kt | 12 +- .../ui/components/EditBase64Preference.kt | 17 +- .../mesh/ui/components/EditListPreference.kt | 23 +- .../ui/components/EditPasswordPreference.kt | 4 +- .../mesh/ui/components/EditTextPreference.kt | 14 +- .../mesh/ui/components/EmojiPicker.kt | 4 +- .../mesh/ui/components/EnvironmentMetrics.kt | 30 +- .../mesh/ui/components/IndoorAirQuality.kt | 29 +- .../components/LazyColumnDragAndDropDemo.kt | 8 +- .../mesh/ui/components/LoraSignalIndicator.kt | 12 +- .../mesh/ui/components/NodeFilterTextField.kt | 97 +++--- .../mesh/ui/components/NodeKeyStatusIcon.kt | 21 +- .../geeksville/mesh/ui/components/NodeMenu.kt | 84 ++--- .../mesh/ui/components/PositionLog.kt | 35 +-- .../components/PositionPrecisionPreference.kt | 8 +- .../mesh/ui/components/PowerMetrics.kt | 24 +- .../mesh/ui/components/PreferenceCategory.kt | 19 +- .../mesh/ui/components/PreferenceFooter.kt | 16 +- .../mesh/ui/components/RegularPreference.kt | 19 +- .../mesh/ui/components/ScannedQrCodeDialog.kt | 26 +- .../mesh/ui/components/SignalInfo.kt | 8 +- .../mesh/ui/components/SignalMetrics.kt | 14 +- .../mesh/ui/components/SimpleAlertDialog.kt | 15 +- .../mesh/ui/components/SlidingSelector.kt | 20 +- .../mesh/ui/components/SwitchPreference.kt | 34 +- .../ui/components/TextDividerPreference.kt | 33 +- .../mesh/ui/components/TracerouteLog.kt | 41 +-- .../mesh/ui/components/UserAvatar.kt | 6 +- .../mesh/ui/compose/ElevationInfo.kt | 8 +- .../mesh/ui/compose/SatelliteCountInfo.kt | 12 +- .../com/geeksville/mesh/ui/map/CacheLayout.kt | 19 +- .../geeksville/mesh/ui/map/DownloadButton.kt | 8 +- .../mesh/ui/map/EditWaypointDialog.kt | 21 +- .../com/geeksville/mesh/ui/map/MapButton.kt | 21 +- .../com/geeksville/mesh/ui/map/MapView.kt | 57 ++-- .../com/geeksville/mesh/ui/message/Message.kt | 43 +-- .../mesh/ui/message/components/MessageItem.kt | 124 ++++---- .../mesh/ui/message/components/Reaction.kt | 33 +- .../mesh/ui/radioconfig/RadioConfig.kt | 34 +- .../AmbientLightingConfigItemList.kt | 4 +- .../components/AudioConfigItemList.kt | 5 +- .../components/BluetoothConfigItemList.kt | 6 +- .../components/CannedMessageConfigItemList.kt | 16 +- .../components/ChannelSettingsItemList.kt | 41 +-- .../DetectionSensorConfigItemList.kt | 10 +- .../components/DeviceConfigItemList.kt | 20 +- .../components/DisplayConfigItemList.kt | 20 +- .../components/EditChannelDialog.kt | 12 +- .../components/EditDeviceProfileDialog.kt | 21 +- .../ExternalNotificationConfigItemList.kt | 18 +- .../components/LoRaConfigItemList.kt | 20 +- .../components/MQTTConfigItemList.kt | 14 +- .../components/NeighborInfoConfigItemList.kt | 6 +- .../components/NetworkConfigItemList.kt | 16 +- .../components/PacketResponseStateDialog.kt | 13 +- .../components/PaxcounterConfigItemList.kt | 4 +- .../components/PositionConfigItemList.kt | 10 +- .../components/PowerConfigItemList.kt | 4 +- .../components/RangeTestConfigItemList.kt | 6 +- .../RemoteHardwareConfigItemList.kt | 6 +- .../components/SecurityConfigItemList.kt | 10 +- .../components/SerialConfigItemList.kt | 12 +- .../components/StoreForwardConfigItemList.kt | 8 +- .../components/TelemetryConfigItemList.kt | 14 +- .../components/UserConfigItemList.kt | 9 +- .../com/geeksville/mesh/ui/theme/Color.kt | 220 ++++++++++++- .../com/geeksville/mesh/ui/theme/Theme.kt | 290 ++++++++++++++++- .../java/com/geeksville/mesh/ui/theme/Type.kt | 22 ++ app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/styles.xml | 15 +- config/detekt/detekt-baseline.xml | 37 --- gradle/libs.versions.toml | 5 +- 99 files changed, 2134 insertions(+), 1606 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/ui/components/AlertDialogs.kt create mode 100644 app/src/main/java/com/geeksville/mesh/ui/theme/Type.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3b87420d9..7fe79c0c8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -136,7 +136,6 @@ diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 6ac62691a..0599a32ce 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -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, + ) } } diff --git a/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt b/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt index a47193c17..0864045ed 100644 --- a/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt @@ -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>(mutableMapOf()) val errorText = MutableLiveData(null) - private val showMockInterface = MutableStateFlow(radioInterfaceService.isMockInterface) - - fun showMockInterface() { - showMockInterface.value = true - } + private val showMockInterface: StateFlow get() = + MutableStateFlow(radioInterfaceService.isMockInterface()).asStateFlow() init { combine( @@ -77,7 +76,9 @@ class BTScanModel @Inject constructor( showMockInterface, ) { ble, tcp, usb, showMockInterface -> devices.value = mutableMapOf().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>(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) { diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index 436ca4e6e..549a58869 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -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 = _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 Unit> = emptyMap() + ) + + private val _currentAlert: MutableStateFlow = MutableStateFlow(null) + val currentAlert = _currentAlert.asStateFlow() + + fun showAlert( + title: String, + message: String? = null, + html: String? = null, + onConfirm: (() -> Unit)? = {}, + dismissable: Boolean = true, + choices: Map 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 = _title.asStateFlow() fun setTitle(title: String) { _title.value = title } + val receivingLocationUpdates: StateFlow get() = locationRepository.receivingLocationUpdates val meshService: IMeshService? get() = radioConfigRepository.meshService @@ -194,15 +246,17 @@ class UIViewModel @Inject constructor( val localConfig: StateFlow = _localConfig val config get() = _localConfig.value - private val _moduleConfig = MutableStateFlow(LocalModuleConfig.getDefaultInstance()) + private val _moduleConfig = + MutableStateFlow(LocalModuleConfig.getDefaultInstance()) val moduleConfig: StateFlow = _moduleConfig val module get() = _moduleConfig.value private val _channels = MutableStateFlow(channelSet {}) val channels: StateFlow 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(preferences.getBoolean("provide-location", false)) { - override fun setValue(value: Boolean) { - super.setValue(value) + val provideLocation = + object : MutableLiveData(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 -> diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt index 6c3d8f73c..13a720c63 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt @@ -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 } diff --git a/app/src/main/java/com/geeksville/mesh/ui/BatteryInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/BatteryInfo.kt index 6c05396ee..c1f009c35 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/BatteryInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/BatteryInfo.kt @@ -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 ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/Channel.kt b/app/src/main/java/com/geeksville/mesh/ui/Channel.kt index c29e6e453..05e3cd616 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Channel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Channel.kt @@ -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)) } }, diff --git a/app/src/main/java/com/geeksville/mesh/ui/ContactItem.kt b/app/src/main/java/com/geeksville/mesh/ui/ContactItem.kt index f9156fe18..94ed086e6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/ContactItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/ContactItem.kt @@ -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, - ) - } } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/Contacts.kt b/app/src/main/java/com/geeksville/mesh/ui/Contacts.kt index b9f8d1e6c..ef6608536 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Contacts.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Contacts.kt @@ -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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/Debug.kt b/app/src/main/java/com/geeksville/mesh/ui/Debug.kt index d0bfd28ad..b4f176422 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Debug.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Debug.kt @@ -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) ) { diff --git a/app/src/main/java/com/geeksville/mesh/ui/LastHeardInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/LastHeardInfo.kt index fb00fe3aa..4eb75ecaf 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/LastHeardInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/LastHeardInfo.kt @@ -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 ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/LinkedCoordinates.kt b/app/src/main/java/com/geeksville/mesh/ui/LinkedCoordinates.kt index 216233c98..72fa5d78f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/LinkedCoordinates.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/LinkedCoordinates.kt @@ -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 { diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt index 6577e25a1..6bb33674f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt @@ -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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt index 882a68282..619c7160d 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt @@ -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()) { diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt index 09d511bbb..801a2245b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt @@ -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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt index fada31f76..1a1fcf684 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeScreen.kt @@ -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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/QuickChat.kt b/app/src/main/java/com/geeksville/mesh/ui/QuickChat.kt index 6cb0f83c9..e602b0a7a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChat.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChat.kt @@ -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, ) { diff --git a/app/src/main/java/com/geeksville/mesh/ui/Settings.kt b/app/src/main/java/com/geeksville/mesh/ui/Settings.kt index b87c238bb..777f0a9e1 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Settings.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Settings.kt @@ -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()) { diff --git a/app/src/main/java/com/geeksville/mesh/ui/Share.kt b/app/src/main/java/com/geeksville/mesh/ui/Share.kt index 99e9e8ee6..37f83ba98 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Share.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Share.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/AlertDialogs.kt b/app/src/main/java/com/geeksville/mesh/ui/components/AlertDialogs.kt new file mode 100644 index 000000000..e5f2fe9af --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/components/AlertDialogs.kt @@ -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 . + */ + +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 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 = { + } + ) +} diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/AutoLinkText.kt b/app/src/main/java/com/geeksville/mesh/ui/components/AutoLinkText.kt index 5d95df02e..1b551a26b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/AutoLinkText.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/AutoLinkText.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/BitwisePreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/BitwisePreference.kt index fb6f4e0ff..1e66013ea 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/BitwisePreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/BitwisePreference.kt @@ -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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/BottomSheetDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/components/BottomSheetDialog.kt index c4e6da6e4..d67903eac 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/BottomSheetDialog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/BottomSheetDialog.kt @@ -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), diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/ClickableTextField.kt b/app/src/main/java/com/geeksville/mesh/ui/components/ClickableTextField.kt index c117c0387..4d38ceaab 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/ClickableTextField.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/ClickableTextField.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/CommonCharts.kt b/app/src/main/java/com/geeksville/mesh/ui/components/CommonCharts.kt index d2d75921b..72fe51a72 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/CommonCharts.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/CommonCharts.kt @@ -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>, 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, ) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/CopyIconButton.kt b/app/src/main/java/com/geeksville/mesh/ui/components/CopyIconButton.kt index 95986c90c..375b51c66 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/CopyIconButton.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/CopyIconButton.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/DeviceMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/components/DeviceMetrics.kt index 941720238..6cd4703ee 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/DeviceMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/DeviceMetrics.kt @@ -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 ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/DropDownPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/DropDownPreference.kt index e04fd84ed..ef3be8d09 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/DropDownPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/DropDownPreference.kt @@ -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 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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EditBase64Preference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EditBase64Preference.kt index 18daf11db..8424a8d7f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/EditBase64Preference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EditBase64Preference.kt @@ -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 } ) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EditListPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EditListPreference.kt index 10da02168..72a5134ce 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/EditListPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EditListPreference.kt @@ -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 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 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)) } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt index 620ccd877..3520805eb 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EditPasswordPreference.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt index f022689d8..de5ca65c4 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EditTextPreference.kt @@ -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) ) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EmojiPicker.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EmojiPicker.kt index e0a296e04..3ef31430b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/EmojiPicker.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EmojiPicker.kt @@ -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) ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/EnvironmentMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/components/EnvironmentMetrics.kt index b46837f13..b134cabff 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/EnvironmentMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/EnvironmentMetrics.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/IndoorAirQuality.kt b/app/src/main/java/com/geeksville/mesh/ui/components/IndoorAirQuality.kt index 271a95bd8..f8339418a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/IndoorAirQuality.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/IndoorAirQuality.kt @@ -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) diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/LazyColumnDragAndDropDemo.kt b/app/src/main/java/com/geeksville/mesh/ui/components/LazyColumnDragAndDropDemo.kt index 64d35cee2..771b32def 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/LazyColumnDragAndDropDemo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/LazyColumnDragAndDropDemo.kt @@ -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)) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt b/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt index 99f488fc6..c8770b715 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt @@ -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 ) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt b/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt index 3f83b01b0..e10ce5585 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/NodeFilterTextField.kt @@ -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), + ) + } } - } + ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/NodeKeyStatusIcon.kt b/app/src/main/java/com/geeksville/mesh/ui/components/NodeKeyStatusIcon.kt index 3a06f482e..5f1186f98 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/NodeKeyStatusIcon.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/NodeKeyStatusIcon.kt @@ -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)) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt index 1fee3b261..8e25b1e35 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt @@ -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)) } ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/PositionLog.kt b/app/src/main/java/com/geeksville/mesh/ui/components/PositionLog.kt index 8a789550a..54bdfc96d 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/PositionLog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/PositionLog.kt @@ -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) } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/PositionPrecisionPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/PositionPrecisionPreference.kt index 5122c3344..2e135720e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/PositionPrecisionPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/PositionPrecisionPreference.kt @@ -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, ) diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/PowerMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/components/PowerMetrics.kt index 413b772b0..3ce0dbfde 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/PowerMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/PowerMetrics.kt @@ -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 ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceCategory.kt b/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceCategory.kt index fa9fa56b5..7110c77f8 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceCategory.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceCategory.kt @@ -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" ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceFooter.kt b/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceFooter.kt index 7c51ab5b8..822a1dffb 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceFooter.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/PreferenceFooter.kt @@ -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, ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/RegularPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/RegularPreference.kt index a0f840536..03848f881 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/RegularPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/RegularPreference.kt @@ -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, ) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt index 060d845ef..2ade7d227 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt @@ -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, ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/SignalInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/components/SignalInfo.kt index 344c6ef7e..47bfed155 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/SignalInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/SignalInfo.kt @@ -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. */ diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt index 927d12334..0646e3025 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt @@ -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 ) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/SimpleAlertDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/components/SimpleAlertDialog.kt index 5fe03b033..af15c4a3d 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/SimpleAlertDialog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/SimpleAlertDialog.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/SlidingSelector.kt b/app/src/main/java/com/geeksville/mesh/ui/components/SlidingSelector.kt index fdd5969e1..6f4c94083 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/SlidingSelector.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/SlidingSelector.kt @@ -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 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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/SwitchPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/SwitchPreference.kt index 8c2fc1c8d..6c7460fc9 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/SwitchPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/SwitchPreference.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/TextDividerPreference.kt b/app/src/main/java/com/geeksville/mesh/ui/components/TextDividerPreference.kt index 9379cc649..5c888dbbe 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/TextDividerPreference.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/TextDividerPreference.kt @@ -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), + ) + } } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/TracerouteLog.kt b/app/src/main/java/com/geeksville/mesh/ui/components/TracerouteLog.kt index 353c22405..bbf8cc2ba 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/TracerouteLog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/TracerouteLog.kt @@ -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, ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/UserAvatar.kt b/app/src/main/java/com/geeksville/mesh/ui/components/UserAvatar.kt index 7bd36564b..f2946b878 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/UserAvatar.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/UserAvatar.kt @@ -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 { diff --git a/app/src/main/java/com/geeksville/mesh/ui/compose/ElevationInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/compose/ElevationInfo.kt index 560b1eda0..9609b6151 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/compose/ElevationInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/compose/ElevationInfo.kt @@ -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, ) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/compose/SatelliteCountInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/compose/SatelliteCountInfo.kt index 79c0d5989..0d276a2c6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/compose/SatelliteCountInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/compose/SatelliteCountInfo.kt @@ -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, ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/CacheLayout.kt b/app/src/main/java/com/geeksville/mesh/ui/map/CacheLayout.kt index f332f121b..1159d99d6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/map/CacheLayout.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/map/CacheLayout.kt @@ -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, ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/DownloadButton.kt b/app/src/main/java/com/geeksville/mesh/ui/map/DownloadButton.kt index 034bd40b0..fc71e0a13 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/map/DownloadButton.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/map/DownloadButton.kt @@ -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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/EditWaypointDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/map/EditWaypointDialog.kt index bbf44d43d..f965c6716 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/map/EditWaypointDialog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/map/EditWaypointDialog.kt @@ -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), diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/MapButton.kt b/app/src/main/java/com/geeksville/mesh/ui/map/MapButton.kt index 6c653d5ff..7cb1f4e32 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/map/MapButton.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/map/MapButton.kt @@ -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), ) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/MapView.kt b/app/src/main/java/com/geeksville/mesh/ui/map/MapView.kt index b062edf2c..411ed24a9 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/map/MapView.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/map/MapView.kt @@ -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 } ) }, diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt b/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt index f2134de36..f4c95d098 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt @@ -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, @@ -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) diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt index 5aeccf99c..9bd76e498 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt @@ -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() }, - ) - } } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/components/Reaction.kt b/app/src/main/java/com/geeksville/mesh/ui/message/components/Reaction.kt index b2fcb5963..280af3ff5 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/message/components/Reaction.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/message/components/Reaction.kt @@ -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) diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfig.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfig.kt index 86cc7098a..287bfa80c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfig.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfig.kt @@ -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() diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AmbientLightingConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AmbientLightingConfigItemList.kt index 1510f7b47..8075ff2fa 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AmbientLightingConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AmbientLightingConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AudioConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AudioConfigItemList.kt index cbfa0bd27..18f580c6d 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AudioConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/AudioConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/BluetoothConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/BluetoothConfigItemList.kt index 0844cb79b..0bcf7f805 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/BluetoothConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/BluetoothConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/CannedMessageConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/CannedMessageConfigItemList.kt index bb76474de..ac83d533c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/CannedMessageConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/CannedMessageConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt index 0228d0144..d2f72e221 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ChannelSettingsItemList.kt @@ -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, diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DetectionSensorConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DetectionSensorConfigItemList.kt index ed8410b59..380279667 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DetectionSensorConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DetectionSensorConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DeviceConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DeviceConfigItemList.kt index fb8ed8eb0..f518c684f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DeviceConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DeviceConfigItemList.kt @@ -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 { diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DisplayConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DisplayConfigItemList.kt index 478ecd1a9..cabfb1832 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DisplayConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/DisplayConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditChannelDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditChannelDialog.kt index ee1c06811..1388ef61b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditChannelDialog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditChannelDialog.kt @@ -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() diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditDeviceProfileDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditDeviceProfileDialog.kt index e8bf571ee..393ed9e24 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditDeviceProfileDialog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/EditDeviceProfileDialog.kt @@ -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() diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ExternalNotificationConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ExternalNotificationConfigItemList.kt index 29b386535..a1c0531d6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ExternalNotificationConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/ExternalNotificationConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/LoRaConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/LoRaConfigItemList.kt index fba0a73aa..6eb2bb023 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/LoRaConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/LoRaConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/MQTTConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/MQTTConfigItemList.kt index bc0765da1..2cbeb8bf7 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/MQTTConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/MQTTConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NeighborInfoConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NeighborInfoConfigItemList.kt index 7fbffffe3..b4b02f754 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NeighborInfoConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NeighborInfoConfigItemList.kt @@ -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 { diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NetworkConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NetworkConfigItemList.kt index 9024734f0..1c7ed071a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NetworkConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/NetworkConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PacketResponseStateDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PacketResponseStateDialog.kt index 0ea426889..179be8132 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PacketResponseStateDialog.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PacketResponseStateDialog.kt @@ -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 PacketResponseStateDialog( AlertDialog( onDismissRequest = {}, shape = RoundedCornerShape(16.dp), - backgroundColor = MaterialTheme.colors.background, title = { Column( modifier = Modifier.fillMaxWidth(), @@ -65,7 +63,6 @@ fun PacketResponseStateDialog( modifier = Modifier .fillMaxWidth() .padding(top = 8.dp), - color = MaterialTheme.colors.onSurface, ) if (state.total == state.completed) onComplete() } @@ -78,7 +75,7 @@ fun PacketResponseStateDialog( } } }, - buttons = { + confirmButton = { Row( modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PaxcounterConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PaxcounterConfigItemList.kt index 17b0cca4b..6c0602375 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PaxcounterConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PaxcounterConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PositionConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PositionConfigItemList.kt index 63d10013c..d170041fd 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PositionConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PositionConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PowerConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PowerConfigItemList.kt index d012cf9b0..de9899245 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PowerConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/PowerConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RangeTestConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RangeTestConfigItemList.kt index 1c316b4a3..6618d9f5c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RangeTestConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RangeTestConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RemoteHardwareConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RemoteHardwareConfigItemList.kt index 722cedcd9..63bcb8640 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RemoteHardwareConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/RemoteHardwareConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SecurityConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SecurityConfigItemList.kt index 54182c65a..87cf0a776 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SecurityConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SecurityConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SerialConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SerialConfigItemList.kt index 8afbd436e..af3e852b6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SerialConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/SerialConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/StoreForwardConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/StoreForwardConfigItemList.kt index 93deeb443..da579aa08 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/StoreForwardConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/StoreForwardConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/TelemetryConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/TelemetryConfigItemList.kt index 6ef200e77..1049e805e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/TelemetryConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/TelemetryConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt index b2cb63455..bccf0e2aa 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/components/UserConfigItemList.kt @@ -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( diff --git a/app/src/main/java/com/geeksville/mesh/ui/theme/Color.kt b/app/src/main/java/com/geeksville/mesh/ui/theme/Color.kt index 1fa9d80d0..e1d83e0f1 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/theme/Color.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/theme/Color.kt @@ -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) \ No newline at end of file +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) diff --git a/app/src/main/java/com/geeksville/mesh/ui/theme/Theme.kt b/app/src/main/java/com/geeksville/mesh/ui/theme/Theme.kt index 3be0d2126..419f70dec 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/theme/Theme.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/theme/Theme.kt @@ -15,39 +15,295 @@ * along with this program. If not, see . */ +@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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/theme/Type.kt b/app/src/main/java/com/geeksville/mesh/ui/theme/Type.kt new file mode 100644 index 000000000..1e84a7449 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/theme/Type.kt @@ -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 . + */ + +package com.geeksville.mesh.ui.theme + +import androidx.compose.material3.Typography + +val AppTypography = Typography() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 665308bb4..26ece572b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -580,7 +580,8 @@ Long name Short name Hardware model - Licensed amateur radio + Licensed amateur radio (HAM) + Enabling this option disables encryption and is not compatible with the default Meshtastic network. Dew Point Pressure Gas Resistance @@ -607,4 +608,5 @@ Alt Set Region Unmute + Dynamic diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 9eb6b2e2a..43a806684 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,5 +1,4 @@ - - - diff --git a/config/detekt/detekt-baseline.xml b/config/detekt/detekt-baseline.xml index 3b34f1353..764bea39e 100644 --- a/config/detekt/detekt-baseline.xml +++ b/config/detekt/detekt-baseline.xml @@ -91,7 +91,6 @@ FinalNewline:BluetoothRepositoryModule.kt$com.geeksville.mesh.repository.bluetooth.BluetoothRepositoryModule.kt FinalNewline:BluetoothViewModel.kt$com.geeksville.mesh.model.BluetoothViewModel.kt FinalNewline:BootCompleteReceiver.kt$com.geeksville.mesh.service.BootCompleteReceiver.kt - FinalNewline:Color.kt$com.geeksville.mesh.ui.theme.Color.kt FinalNewline:CoroutineDispatchers.kt$com.geeksville.mesh.CoroutineDispatchers.kt FinalNewline:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt FinalNewline:CustomTileSource.kt$com.geeksville.mesh.model.map.CustomTileSource.kt @@ -111,7 +110,6 @@ FinalNewline:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt FinalNewline:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt FinalNewline:OnlineTileSourceAuth.kt$com.geeksville.mesh.model.map.OnlineTileSourceAuth.kt - FinalNewline:PreferenceCategory.kt$com.geeksville.mesh.ui.components.PreferenceCategory.kt FinalNewline:PreviewParameterProviders.kt$com.geeksville.mesh.ui.preview.PreviewParameterProviders.kt FinalNewline:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt FinalNewline:QuickChatActionRepository.kt$com.geeksville.mesh.database.QuickChatActionRepository.kt @@ -159,7 +157,6 @@ LongMethod:PowerConfigItemList.kt$@Composable fun PowerConfigItemList( powerConfig: PowerConfig, enabled: Boolean, onSaveClicked: (PowerConfig) -> Unit, ) LongMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket) LongMethod:SerialConfigItemList.kt$@Composable fun SerialConfigItemList( serialConfig: SerialConfig, enabled: Boolean, onSaveClicked: (SerialConfig) -> Unit, ) - LongMethod:SettingsFragment.kt$SettingsFragment$private fun initCommonUI() LongMethod:StoreForwardConfigItemList.kt$@Composable fun StoreForwardConfigItemList( storeForwardConfig: StoreForwardConfig, enabled: Boolean, onSaveClicked: (StoreForwardConfig) -> Unit, ) LongMethod:TelemetryConfigItemList.kt$@Composable fun TelemetryConfigItemList( telemetryConfig: TelemetryConfig, enabled: Boolean, onSaveClicked: (TelemetryConfig) -> Unit, ) LongMethod:UIState.kt$UIViewModel$fun saveMessagesCSV(uri: Uri) @@ -291,21 +288,6 @@ MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$360.0 MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$4 MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$5 - MagicNumber:NavGraph.kt$ConfigRoute.BLUETOOTH$6 - MagicNumber:NavGraph.kt$ConfigRoute.DISPLAY$4 - MagicNumber:NavGraph.kt$ConfigRoute.LORA$5 - MagicNumber:NavGraph.kt$ConfigRoute.NETWORK$3 - MagicNumber:NavGraph.kt$ConfigRoute.SECURITY$7 - MagicNumber:NavGraph.kt$ModuleRoute.AMBIENT_LIGHTING$10 - MagicNumber:NavGraph.kt$ModuleRoute.AUDIO$7 - MagicNumber:NavGraph.kt$ModuleRoute.CANNED_MESSAGE$6 - MagicNumber:NavGraph.kt$ModuleRoute.DETECTION_SENSOR$11 - MagicNumber:NavGraph.kt$ModuleRoute.NEIGHBOR_INFO$9 - MagicNumber:NavGraph.kt$ModuleRoute.PAXCOUNTER$12 - MagicNumber:NavGraph.kt$ModuleRoute.RANGE_TEST$4 - MagicNumber:NavGraph.kt$ModuleRoute.REMOTE_HARDWARE$8 - MagicNumber:NavGraph.kt$ModuleRoute.STORE_FORWARD$3 - MagicNumber:NavGraph.kt$ModuleRoute.TELEMETRY$5 MagicNumber:NodeInfo.kt$DeviceMetrics.Companion$1000 MagicNumber:NodeInfo.kt$EnvironmentMetrics.Companion$1000 MagicNumber:NodeInfo.kt$NodeInfo$0.114 @@ -433,9 +415,6 @@ MaxLineLength:SafeBluetooth.kt$SafeBluetooth.<no name provided>$// to do an autoconnection attempt. When that attempt succeeds/fails the normal callbacks will be called MaxLineLength:ServiceClient.kt$ServiceClient$// Some phones seem to ahve a race where if you unbind and quickly rebind bindService returns false. Try 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 - MaxLineLength:SettingsFragment.kt$SettingsFragment$// Note: we pull this into a tempvar, because otherwise some other thread can change selectedAddress after our null check - MaxLineLength:SettingsFragment.kt$SettingsFragment$deviceSelectIPAddress.isChecked = scanModel.onSelected(BTScanModel.DeviceListEntry("", "t" + inputIPAddress.text, true)) - MaxLineLength:SettingsFragment.kt$SettingsFragment$if MaxLineLength:SqlTileWriterExt.kt$SqlTileWriterExt$"select " + DatabaseFileArchive.COLUMN_KEY + "," + COLUMN_EXPIRES + "," + DatabaseFileArchive.COLUMN_PROVIDER + " from " + DatabaseFileArchive.TABLE + " limit ? offset ?" MaxLineLength:StreamInterface.kt$StreamInterface$* 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) @@ -443,7 +422,6 @@ MaxLineLength:StreamInterface.kt$StreamInterface$deliverPacket() MaxLineLength:StreamInterface.kt$StreamInterface$lostSync() MaxLineLength:StreamInterface.kt$StreamInterface$service.onDisconnect(isPermanent = true) - MaxLineLength:TextDividerPreference.kt$color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.Unspecified 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 MaxLineLength:UIState.kt$UIViewModel$writer.appendLine("$rxDateTime,\"$rxFrom\",\"$senderName\",\"$senderLat\",\"$senderLong\",\"$rxLat\",\"$rxLong\",\"$rxAlt\",\"$rxSnr\",\"$dist\",\"$hopLimit\",\"$payload\"") 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\"") @@ -466,14 +444,11 @@ MultiLineIfElse:ContextServices.kt$invokeFun() 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, ) 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, ) - MultiLineIfElse:EditPasswordPreference.kt$painterResource(R.drawable.ic_twotone_visibility_24) - MultiLineIfElse:EditPasswordPreference.kt$painterResource(R.drawable.ic_twotone_visibility_off_24) MultiLineIfElse:EditTextPreference.kt$it.toDoubleOrNull()?.let { double -> valueState = it onValueChanged(double) } MultiLineIfElse:EditTextPreference.kt$it.toFloatOrNull()?.let { float -> valueState = it onValueChanged(float) } MultiLineIfElse:EditTextPreference.kt$it.toUIntOrNull()?.toInt()?.let { int -> valueState = it onValueChanged(int) } MultiLineIfElse:EditTextPreference.kt$onValueChanged(it) MultiLineIfElse:EditTextPreference.kt$valueState = it - 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)) } } }, ) MultiLineIfElse:Exceptions.kt$Exceptions.errormsg("ignoring exception", ex) MultiLineIfElse:ExpireChecker.kt$ExpireChecker$doExpire() MultiLineIfElse:Logging.kt$Logging$printlog(Log.ERROR, tag(), "$msg (exception ${ex.message})") @@ -501,8 +476,6 @@ 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) } MultiLineIfElse:SafeBluetooth.kt$SafeBluetooth.<no name provided>$lostConnection("lost connection") MultiLineIfElse:SafeBluetooth.kt$SafeBluetooth.<no name provided>$warn("Received notification from $characteristic, but no handler registered") - MultiLineIfElse:TextDividerPreference.kt$Icon( trailingIcon, "trailingIcon", modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), ) - 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 { } }) NestedBlockDepth:LanguageUtils.kt$LanguageUtils$fun getLanguageTags(context: Context): Map<String, String> NestedBlockDepth:MainActivity.kt$MainActivity$private fun onMeshConnectionChanged(newConnection: MeshService.ConnectionState) NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedAdmin(fromNodeNum: Int, a: AdminProtos.AdminMessage) @@ -516,7 +489,6 @@ NewLineAtEndOfFile:BluetoothRepositoryModule.kt$com.geeksville.mesh.repository.bluetooth.BluetoothRepositoryModule.kt NewLineAtEndOfFile:BluetoothViewModel.kt$com.geeksville.mesh.model.BluetoothViewModel.kt NewLineAtEndOfFile:BootCompleteReceiver.kt$com.geeksville.mesh.service.BootCompleteReceiver.kt - NewLineAtEndOfFile:Color.kt$com.geeksville.mesh.ui.theme.Color.kt NewLineAtEndOfFile:CoroutineDispatchers.kt$com.geeksville.mesh.CoroutineDispatchers.kt NewLineAtEndOfFile:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt NewLineAtEndOfFile:CustomTileSource.kt$com.geeksville.mesh.model.map.CustomTileSource.kt @@ -536,7 +508,6 @@ NewLineAtEndOfFile:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt NewLineAtEndOfFile:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt NewLineAtEndOfFile:OnlineTileSourceAuth.kt$com.geeksville.mesh.model.map.OnlineTileSourceAuth.kt - NewLineAtEndOfFile:PreferenceCategory.kt$com.geeksville.mesh.ui.components.PreferenceCategory.kt NewLineAtEndOfFile:PreviewParameterProviders.kt$com.geeksville.mesh.ui.preview.PreviewParameterProviders.kt NewLineAtEndOfFile:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt NewLineAtEndOfFile:QuickChatActionRepository.kt$com.geeksville.mesh.database.QuickChatActionRepository.kt @@ -585,11 +556,8 @@ NoWildcardImports:BluetoothInterface.kt$import com.geeksville.mesh.service.* NoWildcardImports:DeviceVersionTest.kt$import org.junit.Assert.* NoWildcardImports:MockInterface.kt$import com.geeksville.mesh.* - NoWildcardImports:PreferenceFooter.kt$import androidx.compose.foundation.layout.* - NoWildcardImports:PreferenceFooter.kt$import androidx.compose.material.* NoWildcardImports:SafeBluetooth.kt$import android.bluetooth.* NoWildcardImports:SafeBluetooth.kt$import kotlinx.coroutines.* - NoWildcardImports:SettingsFragment.kt$import com.geeksville.mesh.android.* NoWildcardImports:UsbRepository.kt$import kotlinx.coroutines.flow.* OptionalAbstractKeyword:SyncContinuation.kt$Continuation$abstract ParameterListWrapping:AppPrefs.kt$FloatPref$(thisRef: AppPrefs, prop: KProperty<Float>) @@ -613,7 +581,6 @@ SwallowedException:ChannelSet.kt$ex: Throwable SwallowedException:DeviceVersion.kt$DeviceVersion$e: Exception SwallowedException:Exceptions.kt$ex: Throwable - SwallowedException:MainActivity.kt$MainActivity$ex: BindFailedException SwallowedException:MeshLog.kt$MeshLog$e: IOException SwallowedException:MeshService.kt$MeshService$e: Exception SwallowedException:MeshService.kt$MeshService$e: TimeoutException @@ -667,7 +634,6 @@ TooManyFunctions:RadioConfigViewModel.kt$RadioConfigViewModel : ViewModelLogging TooManyFunctions:RadioInterfaceService.kt$RadioInterfaceService : Logging TooManyFunctions:SafeBluetooth.kt$SafeBluetooth : LoggingCloseable - TooManyFunctions:SettingsFragment.kt$SettingsFragment : ScreenFragmentLogging TooManyFunctions:UIState.kt$UIViewModel : ViewModelLogging TopLevelPropertyNaming:Constants.kt$const val prefix = "com.geeksville.mesh" UnusedPrivateMember:NOAAWmsTileSource.kt$NOAAWmsTileSource$private fun tile2lat(y: Int, z: Int): Double @@ -695,11 +661,8 @@ WildcardImport:BluetoothInterface.kt$import com.geeksville.mesh.service.* WildcardImport:DeviceVersionTest.kt$import org.junit.Assert.* WildcardImport:MockInterface.kt$import com.geeksville.mesh.* - WildcardImport:PreferenceFooter.kt$import androidx.compose.foundation.layout.* - WildcardImport:PreferenceFooter.kt$import androidx.compose.material.* WildcardImport:SafeBluetooth.kt$import android.bluetooth.* WildcardImport:SafeBluetooth.kt$import kotlinx.coroutines.* - WildcardImport:SettingsFragment.kt$import com.geeksville.mesh.android.* WildcardImport:UsbRepository.kt$import kotlinx.coroutines.flow.* diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3f965c946..225f6bb06 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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