This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
# a3270
|
# a3270
|
||||||
|
|
||||||
[](https://git.hugfreevikings.wtf/rudi/a3270/actions)
|
[](https://git.hugfreevikings.wtf/rudi/a3270/actions)
|
||||||
[](https://git.hugfreevikings.wtf/rudi/a3270/releases)
|
|
||||||
[](https://developer.android.com)
|
[](https://developer.android.com)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
@@ -110,3 +109,6 @@ I have contributed no code to this project, it was entirely written by LLMs with
|
|||||||
- Claude Opus 4.6
|
- Claude Opus 4.6
|
||||||
- Gemini 3.1 Pro
|
- Gemini 3.1 Pro
|
||||||
- Gemini 3.7 Flash
|
- Gemini 3.7 Flash
|
||||||
|
- Gemma4 12B and 26B
|
||||||
|
- GPT-OSS 120B
|
||||||
|
- Qwen3 4B and 32B
|
||||||
|
|||||||
+2
-2
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId "org.pubvm.a3270"
|
applicationId "org.pubvm.a3270"
|
||||||
minSdk 24
|
minSdk 24
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 3
|
versionCode 4
|
||||||
versionName "0.1.2"
|
versionName "0.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ fun MainScreen(
|
|||||||
val screenVersion by viewModel.screenVersion.collectAsState()
|
val screenVersion by viewModel.screenVersion.collectAsState()
|
||||||
val maskHiddenInput by viewModel.maskHiddenInput.collectAsState()
|
val maskHiddenInput by viewModel.maskHiddenInput.collectAsState()
|
||||||
val cursorBlink by viewModel.cursorBlink.collectAsState()
|
val cursorBlink by viewModel.cursorBlink.collectAsState()
|
||||||
|
val hapticFeedback by viewModel.hapticFeedback.collectAsState()
|
||||||
val ftState by viewModel.ftState.collectAsState()
|
val ftState by viewModel.ftState.collectAsState()
|
||||||
|
|
||||||
var showConnectDialog by remember { mutableStateOf(false) }
|
var showConnectDialog by remember { mutableStateOf(false) }
|
||||||
@@ -249,6 +250,7 @@ fun MainScreen(
|
|||||||
TwoRowKeyBar(
|
TwoRowKeyBar(
|
||||||
connectionState = connectionState,
|
connectionState = connectionState,
|
||||||
isShiftPressed = isShiftPressed,
|
isShiftPressed = isShiftPressed,
|
||||||
|
hapticFeedbackEnabled = hapticFeedback,
|
||||||
onClearShift = onClearShift,
|
onClearShift = onClearShift,
|
||||||
onConnectClick = { showConnectDialog = true },
|
onConnectClick = { showConnectDialog = true },
|
||||||
onDisconnectClick = { viewModel.disconnect() },
|
onDisconnectClick = { viewModel.disconnect() },
|
||||||
@@ -318,10 +320,11 @@ fun MainScreen(
|
|||||||
ftProgressState = ftState,
|
ftProgressState = ftState,
|
||||||
onDismiss = { showFtDialog = false },
|
onDismiss = { showFtDialog = false },
|
||||||
onStartTransfer = { config ->
|
onStartTransfer = { config ->
|
||||||
val error = viewModel.startFileTransfer(config)
|
viewModel.startFileTransfer(config) { error ->
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
Toast.makeText(context, error, Toast.LENGTH_LONG).show()
|
Toast.makeText(context, error, Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onCancelTransfer = {
|
onCancelTransfer = {
|
||||||
viewModel.cancelFileTransfer()
|
viewModel.cancelFileTransfer()
|
||||||
@@ -333,10 +336,11 @@ fun MainScreen(
|
|||||||
SettingsDialog(
|
SettingsDialog(
|
||||||
initialMaskHiddenInput = maskHiddenInput,
|
initialMaskHiddenInput = maskHiddenInput,
|
||||||
initialCursorBlink = cursorBlink,
|
initialCursorBlink = cursorBlink,
|
||||||
|
initialHapticFeedback = hapticFeedback,
|
||||||
onDismiss = { showSettingsDialog = false },
|
onDismiss = { showSettingsDialog = false },
|
||||||
onSave = { mask, blink ->
|
onSave = { mask, blink, haptic ->
|
||||||
showSettingsDialog = false
|
showSettingsDialog = false
|
||||||
viewModel.updateSettings(mask, blink)
|
viewModel.updateSettings(mask, blink, haptic)
|
||||||
terminalInputViewRef?.showSoftKeyboard()
|
terminalInputViewRef?.showSoftKeyboard()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.lib3270j.ConnectionConfig
|
import org.lib3270j.ConnectionConfig
|
||||||
import org.lib3270j.ConnectionState
|
import org.lib3270j.ConnectionState
|
||||||
import org.lib3270j.Telnet3270Client
|
import org.lib3270j.Telnet3270Client
|
||||||
@@ -92,6 +93,9 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
|
|||||||
private val _cursorBlink = MutableStateFlow(AppSettings.isCursorBlinkEnabled(application))
|
private val _cursorBlink = MutableStateFlow(AppSettings.isCursorBlinkEnabled(application))
|
||||||
val cursorBlink: StateFlow<Boolean> = _cursorBlink.asStateFlow()
|
val cursorBlink: StateFlow<Boolean> = _cursorBlink.asStateFlow()
|
||||||
|
|
||||||
|
private val _hapticFeedback = MutableStateFlow(AppSettings.isHapticFeedbackEnabled(application))
|
||||||
|
val hapticFeedback: StateFlow<Boolean> = _hapticFeedback.asStateFlow()
|
||||||
|
|
||||||
// File Transfer State
|
// File Transfer State
|
||||||
private var fileTransferCoordinator: FileTransfer? = null
|
private var fileTransferCoordinator: FileTransfer? = null
|
||||||
private val _ftState = MutableStateFlow(FTProgressState())
|
private val _ftState = MutableStateFlow(FTProgressState())
|
||||||
@@ -191,11 +195,13 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSettings(maskHidden: Boolean, blink: Boolean) {
|
fun updateSettings(maskHidden: Boolean, blink: Boolean, haptic: Boolean) {
|
||||||
AppSettings.setMaskHiddenInputEnabled(getApplication(), maskHidden)
|
AppSettings.setMaskHiddenInputEnabled(getApplication(), maskHidden)
|
||||||
AppSettings.setCursorBlinkEnabled(getApplication(), blink)
|
AppSettings.setCursorBlinkEnabled(getApplication(), blink)
|
||||||
|
AppSettings.setHapticFeedbackEnabled(getApplication(), haptic)
|
||||||
_maskHiddenInput.value = maskHidden
|
_maskHiddenInput.value = maskHidden
|
||||||
_cursorBlink.value = blink
|
_cursorBlink.value = blink
|
||||||
|
_hapticFeedback.value = haptic
|
||||||
}
|
}
|
||||||
|
|
||||||
fun connect(host: String, port: Int = 23, modelNum: Int = 2, luName: String = "", hostType: String = "TSO") {
|
fun connect(host: String, port: Int = 23, modelNum: Int = 2, luName: String = "", hostType: String = "TSO") {
|
||||||
@@ -316,9 +322,17 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startFileTransfer(config: FTConfig): String? {
|
fun startFileTransfer(config: FTConfig, onResult: (String?) -> Unit = {}) {
|
||||||
val c = client ?: return "Terminal is not connected."
|
val c = client
|
||||||
|
if (c == null || !_connectionState.value.isConnected()) {
|
||||||
|
val err = "Terminal is not connected."
|
||||||
|
_ftState.value = FTProgressState(isActive = false, statusMessage = err, isError = true)
|
||||||
|
onResult(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
// Resolve local path if relative (default to app files or download directory)
|
// Resolve local path if relative (default to app files or download directory)
|
||||||
val rawPath = config.localFilename
|
val rawPath = config.localFilename
|
||||||
if (!rawPath.startsWith("/")) {
|
if (!rawPath.startsWith("/")) {
|
||||||
@@ -382,11 +396,31 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
|
|||||||
isError = true
|
isError = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return err
|
withContext(Dispatchers.Main) {
|
||||||
|
onResult(err)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.warning("Error in startFileTransfer: ${e.message}")
|
||||||
|
_ftState.value = FTProgressState(
|
||||||
|
isActive = false,
|
||||||
|
statusMessage = "Transfer error: ${e.message}",
|
||||||
|
isError = true
|
||||||
|
)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
onResult(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelFileTransfer() {
|
fun cancelFileTransfer() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
fileTransferCoordinator?.cancel()
|
fileTransferCoordinator?.cancel()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.warning("Error cancelling transfer: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun typeChar(ch: Char) {
|
fun typeChar(ch: Char) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ object AppSettings {
|
|||||||
private const val PREFS_NAME = "a3270_settings"
|
private const val PREFS_NAME = "a3270_settings"
|
||||||
private const val KEY_MASK_HIDDEN_INPUT = "mask_hidden_input"
|
private const val KEY_MASK_HIDDEN_INPUT = "mask_hidden_input"
|
||||||
private const val KEY_CURSOR_BLINK = "cursor_blink"
|
private const val KEY_CURSOR_BLINK = "cursor_blink"
|
||||||
|
private const val KEY_HAPTIC_FEEDBACK = "haptic_feedback"
|
||||||
|
|
||||||
private fun getPrefs(context: Context): SharedPreferences {
|
private fun getPrefs(context: Context): SharedPreferences {
|
||||||
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
@@ -27,4 +28,12 @@ object AppSettings {
|
|||||||
fun setCursorBlinkEnabled(context: Context, enabled: Boolean) {
|
fun setCursorBlinkEnabled(context: Context, enabled: Boolean) {
|
||||||
getPrefs(context).edit().putBoolean(KEY_CURSOR_BLINK, enabled).apply()
|
getPrefs(context).edit().putBoolean(KEY_CURSOR_BLINK, enabled).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isHapticFeedbackEnabled(context: Context): Boolean {
|
||||||
|
return getPrefs(context).getBoolean(KEY_HAPTIC_FEEDBACK, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHapticFeedbackEnabled(context: Context, enabled: Boolean) {
|
||||||
|
getPrefs(context).edit().putBoolean(KEY_HAPTIC_FEEDBACK, enabled).apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package org.pubvm.a3270.ui
|
package org.pubvm.a3270.ui
|
||||||
|
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.horizontalScroll
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.focusProperties
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.focus.focusProperties
|
|
||||||
import org.lib3270j.ConnectionState
|
import org.lib3270j.ConnectionState
|
||||||
import org.lib3270j.protocol.DS3270Constants.*
|
import org.lib3270j.protocol.DS3270Constants.*
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ import org.lib3270j.protocol.DS3270Constants.*
|
|||||||
fun TwoRowKeyBar(
|
fun TwoRowKeyBar(
|
||||||
connectionState: ConnectionState,
|
connectionState: ConnectionState,
|
||||||
isShiftPressed: Boolean = false,
|
isShiftPressed: Boolean = false,
|
||||||
|
hapticFeedbackEnabled: Boolean = true,
|
||||||
onClearShift: () -> Unit = {},
|
onClearShift: () -> Unit = {},
|
||||||
onConnectClick: () -> Unit,
|
onConnectClick: () -> Unit,
|
||||||
onDisconnectClick: () -> Unit,
|
onDisconnectClick: () -> Unit,
|
||||||
@@ -58,6 +59,7 @@ fun TwoRowKeyBar(
|
|||||||
label = "⋮",
|
label = "⋮",
|
||||||
color = Color(0xFF343A40),
|
color = Color(0xFF343A40),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
hapticFeedbackEnabled = hapticFeedbackEnabled,
|
||||||
innerPaddingHorizontal = 0.dp,
|
innerPaddingHorizontal = 0.dp,
|
||||||
onClick = { menuExpanded = true }
|
onClick = { menuExpanded = true }
|
||||||
)
|
)
|
||||||
@@ -106,6 +108,7 @@ fun TwoRowKeyBar(
|
|||||||
label = tabLabel,
|
label = tabLabel,
|
||||||
color = Color(0xFF1C7ED6),
|
color = Color(0xFF1C7ED6),
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
|
hapticFeedbackEnabled = hapticFeedbackEnabled,
|
||||||
innerPaddingHorizontal = 0.dp,
|
innerPaddingHorizontal = 0.dp,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (isShiftPressed) {
|
if (isShiftPressed) {
|
||||||
@@ -118,34 +121,34 @@ fun TwoRowKeyBar(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 3. RESET
|
// 3. RESET
|
||||||
KeyButton("RESET", Color(0xFFE67700), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = onReset)
|
KeyButton("RESET", Color(0xFFE67700), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = onReset)
|
||||||
|
|
||||||
// 4. ENTER
|
// 4. ENTER
|
||||||
KeyButton("ENTER", Color(0xFF2B8A3E), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_ENTER) })
|
KeyButton("ENTER", Color(0xFF2B8A3E), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_ENTER) })
|
||||||
|
|
||||||
// 5. CLEAR
|
// 5. CLEAR
|
||||||
KeyButton("CLEAR", Color(0xFFC92A2A), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_CLEAR) })
|
KeyButton("CLEAR", Color(0xFFC92A2A), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_CLEAR) })
|
||||||
|
|
||||||
// 6. PA1
|
// 6. PA1
|
||||||
KeyButton("PA1", Color(0xFF495057), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_PA1) })
|
KeyButton("PA1", Color(0xFF495057), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_PA1) })
|
||||||
|
|
||||||
// 7. PA2
|
// 7. PA2
|
||||||
KeyButton("PA2", Color(0xFF495057), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_PA2) })
|
KeyButton("PA2", Color(0xFF495057), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_PA2) })
|
||||||
|
|
||||||
// 8. PA3
|
// 8. PA3
|
||||||
KeyButton("PA3", Color(0xFF495057), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_PA3) })
|
KeyButton("PA3", Color(0xFF495057), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = { onSendAid(AID_PA3) })
|
||||||
|
|
||||||
// 9. Left Navigation
|
// 9. Left Navigation
|
||||||
KeyButton("←", Color(0xFF343A40), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = onCursorLeft)
|
KeyButton("←", Color(0xFF343A40), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = onCursorLeft)
|
||||||
|
|
||||||
// 10. Up Navigation
|
// 10. Up Navigation
|
||||||
KeyButton("↑", Color(0xFF343A40), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = onCursorUp)
|
KeyButton("↑", Color(0xFF343A40), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = onCursorUp)
|
||||||
|
|
||||||
// 11. Down Navigation
|
// 11. Down Navigation
|
||||||
KeyButton("↓", Color(0xFF343A40), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = onCursorDown)
|
KeyButton("↓", Color(0xFF343A40), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = onCursorDown)
|
||||||
|
|
||||||
// 12. Right Navigation
|
// 12. Right Navigation
|
||||||
KeyButton("→", Color(0xFF343A40), modifier = Modifier.weight(1f), innerPaddingHorizontal = 0.dp, onClick = onCursorRight)
|
KeyButton("→", Color(0xFF343A40), modifier = Modifier.weight(1f), hapticFeedbackEnabled = hapticFeedbackEnabled, innerPaddingHorizontal = 0.dp, onClick = onCursorRight)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(1.dp).fillMaxWidth().background(Color(0xFF2C2D30)))
|
Spacer(modifier = Modifier.height(1.dp).fillMaxWidth().background(Color(0xFF2C2D30)))
|
||||||
@@ -174,6 +177,7 @@ fun TwoRowKeyBar(
|
|||||||
label = "F$i",
|
label = "F$i",
|
||||||
color = Color(0xFF364FC7),
|
color = Color(0xFF364FC7),
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
|
hapticFeedbackEnabled = hapticFeedbackEnabled,
|
||||||
innerPaddingHorizontal = 0.dp,
|
innerPaddingHorizontal = 0.dp,
|
||||||
onClick = {
|
onClick = {
|
||||||
onSendAid(aid)
|
onSendAid(aid)
|
||||||
@@ -193,10 +197,17 @@ private fun KeyButton(
|
|||||||
color: Color,
|
color: Color,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
hapticFeedbackEnabled: Boolean = true,
|
||||||
innerPaddingHorizontal: androidx.compose.ui.unit.Dp = 0.dp
|
innerPaddingHorizontal: androidx.compose.ui.unit.Dp = 0.dp
|
||||||
) {
|
) {
|
||||||
|
val view = LocalView.current
|
||||||
Surface(
|
Surface(
|
||||||
onClick = onClick,
|
onClick = {
|
||||||
|
if (hapticFeedbackEnabled) {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
|
}
|
||||||
|
onClick()
|
||||||
|
},
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(4.dp),
|
||||||
color = color,
|
color = color,
|
||||||
shadowElevation = 1.dp,
|
shadowElevation = 1.dp,
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package org.pubvm.a3270.ui
|
package org.pubvm.a3270.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -16,11 +18,13 @@ import androidx.compose.ui.window.Dialog
|
|||||||
fun SettingsDialog(
|
fun SettingsDialog(
|
||||||
initialMaskHiddenInput: Boolean,
|
initialMaskHiddenInput: Boolean,
|
||||||
initialCursorBlink: Boolean,
|
initialCursorBlink: Boolean,
|
||||||
|
initialHapticFeedback: Boolean = true,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onSave: (maskHiddenInput: Boolean, cursorBlink: Boolean) -> Unit
|
onSave: (maskHiddenInput: Boolean, cursorBlink: Boolean, hapticFeedback: Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
var maskHiddenInput by remember { mutableStateOf(initialMaskHiddenInput) }
|
var maskHiddenInput by remember { mutableStateOf(initialMaskHiddenInput) }
|
||||||
var cursorBlink by remember { mutableStateOf(initialCursorBlink) }
|
var cursorBlink by remember { mutableStateOf(initialCursorBlink) }
|
||||||
|
var hapticFeedback by remember { mutableStateOf(initialHapticFeedback) }
|
||||||
|
|
||||||
Dialog(onDismissRequest = onDismiss) {
|
Dialog(onDismissRequest = onDismiss) {
|
||||||
Card(
|
Card(
|
||||||
@@ -33,6 +37,7 @@ fun SettingsDialog(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(20.dp)
|
.padding(20.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
@@ -106,6 +111,39 @@ fun SettingsDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
HorizontalDivider(color = Color(0xFF2C2D30))
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// Setting 3: Button Haptic Feedback
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f).padding(end = 12.dp)) {
|
||||||
|
Text(
|
||||||
|
text = "Button Haptic Feedback",
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Vibrate softly when tapping function and navigation buttons to match keyboard tactile feedback.",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = Color.LightGray
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = hapticFeedback,
|
||||||
|
onCheckedChange = { hapticFeedback = it },
|
||||||
|
colors = SwitchDefaults.colors(
|
||||||
|
checkedThumbColor = Color.White,
|
||||||
|
checkedTrackColor = Color(0xFF2B8A3E)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
// Action Buttons
|
// Action Buttons
|
||||||
@@ -119,7 +157,7 @@ fun SettingsDialog(
|
|||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
onSave(maskHiddenInput, cursorBlink)
|
onSave(maskHiddenInput, cursorBlink, hapticFeedback)
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF2B8A3E))
|
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF2B8A3E))
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user