This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://git.hugfreevikings.wtf/rudi/a3270/actions)
|
||||
[](https://developer.android.com)
|
||||
[](LICENSE)
|
||||
[](LICENSE)
|
||||
|
||||
An **x3270-aligned IBM 3270 mainframe terminal emulator for Android**, written in Kotlin using **Jetpack Compose** and powered by the `lib3270j` protocol engine from [j3270](https://git.hugfreevikings.wtf/rudi/j3270). Designed for mobile access to IBM z/OS, z/VM, CMS, and TSO systems over TN3270 and TN3270E.
|
||||
|
||||
@@ -93,14 +93,6 @@ build/outputs/apk/debug/a3270-debug.apk
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to open a bug report or feature request via the Gitea issue tracker.
|
||||
|
||||
I have contributed no code to this project, it was entirely written by LLMs with my guidance only.
|
||||
|
||||
---
|
||||
|
||||
## 📜 Acknowledgements
|
||||
|
||||
- [j3270](https://git.hugfreevikings.wtf/rudi/j3270) — Desktop emulator and `lib3270j` protocol engine
|
||||
|
||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -17,6 +18,9 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -25,6 +29,12 @@ import androidx.compose.ui.window.DialogProperties
|
||||
import org.pubvm.a3270.storage.HostStorage
|
||||
import org.pubvm.a3270.storage.SavedHost
|
||||
|
||||
private val TerminalKeyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.None,
|
||||
autoCorrect = false,
|
||||
keyboardType = KeyboardType.Ascii
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ConnectDialog(
|
||||
onDismiss: () -> Unit,
|
||||
@@ -87,13 +97,14 @@ fun ConnectDialog(
|
||||
}
|
||||
|
||||
fun saveCurrentProfile(): SavedHost? {
|
||||
val port = portStr.toIntOrNull() ?: if (useTls) 992 else 23
|
||||
if (host.isBlank()) return null
|
||||
val nameToSave = profileName.ifBlank { "${host.trim()}:$port" }
|
||||
val port = portStr.trim().toIntOrNull() ?: if (useTls) 992 else 23
|
||||
val trimmedHost = host.trim()
|
||||
if (trimmedHost.isBlank()) return null
|
||||
val nameToSave = profileName.trim().ifBlank { "$trimmedHost:$port" }
|
||||
val hostToSave = SavedHost(
|
||||
id = selectedHostId ?: java.util.UUID.randomUUID().toString(),
|
||||
name = nameToSave,
|
||||
host = host.trim(),
|
||||
host = trimmedHost,
|
||||
port = port,
|
||||
model = modelNum,
|
||||
luName = luName.trim(),
|
||||
@@ -273,6 +284,7 @@ fun ConnectDialog(
|
||||
onValueChange = { profileName = it },
|
||||
label = { Text("Profile Name (Optional)") },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(imeAction = ImeAction.Next),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
@@ -282,18 +294,20 @@ fun ConnectDialog(
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = host,
|
||||
onValueChange = { host = it },
|
||||
onValueChange = { host = it.filterNot { c -> c.isWhitespace() } },
|
||||
label = { Text("Host / IP Address") },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(imeAction = ImeAction.Next),
|
||||
modifier = Modifier
|
||||
.weight(0.68f)
|
||||
.focusRequester(focusRequester)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = portStr,
|
||||
onValueChange = { portStr = it },
|
||||
onValueChange = { portStr = it.filter { c -> c.isDigit() } },
|
||||
label = { Text("Port") },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(imeAction = ImeAction.Next),
|
||||
modifier = Modifier.weight(0.32f)
|
||||
)
|
||||
}
|
||||
@@ -417,6 +431,7 @@ fun ConnectDialog(
|
||||
onValueChange = { luName = it },
|
||||
label = { Text("LU Name / Device Pool (Optional)") },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(imeAction = ImeAction.Done),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.pubvm.a3270.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -9,11 +10,20 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.lib3270j.ft.FTConfig
|
||||
import org.pubvm.a3270.FTProgressState
|
||||
|
||||
private val TerminalKeyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.None,
|
||||
autoCorrect = false,
|
||||
keyboardType = KeyboardType.Ascii
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun FileTransferDialog(
|
||||
initialHostType: String = "TSO",
|
||||
@@ -101,6 +111,7 @@ fun FileTransferDialog(
|
||||
label = { Text("Host Dataset / File Name") },
|
||||
placeholder = { Text(if (hostType == FTConfig.HostType.TSO) "'USER.DATA'" else "PROFILE EXEC A") },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(imeAction = ImeAction.Next),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
@@ -111,6 +122,9 @@ fun FileTransferDialog(
|
||||
placeholder = { Text("sample.txt") },
|
||||
supportingText = { Text("Relative names will be stored in app Downloads/Files storage", fontSize = 10.sp) },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(
|
||||
imeAction = if (!isReceive && hostType == FTConfig.HostType.TSO) ImeAction.Next else ImeAction.Done
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
@@ -208,16 +222,18 @@ fun FileTransferDialog(
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = lreclStr,
|
||||
onValueChange = { lreclStr = it },
|
||||
onValueChange = { lreclStr = it.filter { c -> c.isDigit() } },
|
||||
label = { Text("LRECL", fontSize = 11.sp) },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(imeAction = ImeAction.Next),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = blksizeStr,
|
||||
onValueChange = { blksizeStr = it },
|
||||
onValueChange = { blksizeStr = it.filter { c -> c.isDigit() } },
|
||||
label = { Text("BLKSIZE", fontSize = 11.sp) },
|
||||
singleLine = true,
|
||||
keyboardOptions = TerminalKeyboardOptions.copy(imeAction = ImeAction.Done),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
@@ -237,8 +253,8 @@ fun FileTransferDialog(
|
||||
setExistAction(if (overwrite) FTConfig.ExistAction.REPLACE else FTConfig.ExistAction.KEEP)
|
||||
if (!isReceive && hostType == FTConfig.HostType.TSO) {
|
||||
setRecfm(recfm)
|
||||
lreclStr.toIntOrNull()?.let { setLrecl(it) }
|
||||
blksizeStr.toIntOrNull()?.let { setBlksize(it) }
|
||||
lreclStr.trim().toIntOrNull()?.let { setLrecl(it) }
|
||||
blksizeStr.trim().toIntOrNull()?.let { setBlksize(it) }
|
||||
}
|
||||
}
|
||||
onStartTransfer(config)
|
||||
|
||||
Reference in New Issue
Block a user