418 lines
17 KiB
Kotlin
418 lines
17 KiB
Kotlin
package haus.nightmare.a3270.storage
|
|
|
|
import android.content.Context
|
|
import android.content.SharedPreferences
|
|
import java.io.BufferedReader
|
|
import java.io.PrintWriter
|
|
import java.io.Reader
|
|
import java.io.Writer
|
|
import java.text.SimpleDateFormat
|
|
import java.util.Date
|
|
import java.util.Locale
|
|
|
|
object AppSettings {
|
|
private const val PREFS_NAME = "a3270_settings"
|
|
private const val KEY_MASK_HIDDEN_INPUT = "mask_hidden_input"
|
|
private const val KEY_CURSOR_BLINK = "cursor_blink"
|
|
private const val KEY_HAPTIC_FEEDBACK = "haptic_feedback"
|
|
|
|
private const val KEY_VERIFY_CERTS = "verify_certs"
|
|
private const val KEY_DEFAULT_GRAPHICS_MODE = "default_graphics_mode"
|
|
private const val KEY_DEFAULT_CODE_PAGE = "default_code_page"
|
|
|
|
// New settings aligned with j3270
|
|
private const val KEY_UI_THEME = "ui_theme"
|
|
private const val KEY_CROSSHAIR_RULER = "crosshair_ruler"
|
|
private const val KEY_CURSOR_STYLE = "cursor_style"
|
|
private const val KEY_BLOCK_SELECT_MODE = "block_select_mode"
|
|
private const val KEY_BLOCK_PASTE = "block_paste"
|
|
private const val KEY_STARTUP_BEHAVIOR = "startup_behavior"
|
|
private const val KEY_DYNAMIC_ROWS = "dynamic_rows"
|
|
private const val KEY_DYNAMIC_COLS = "dynamic_cols"
|
|
|
|
// Entry Assist & Modes aligned with j3270
|
|
private const val KEY_ENTRY_ASSIST_DOC_MODE = "entry_assist_doc_mode"
|
|
private const val KEY_ENTRY_ASSIST_WORD_WRAP = "entry_assist_word_wrap"
|
|
private const val KEY_ENTRY_ASSIST_START_COL = "entry_assist_start_col"
|
|
private const val KEY_ENTRY_ASSIST_END_COL = "entry_assist_end_col"
|
|
private const val KEY_ENTRY_ASSIST_BELL = "entry_assist_bell"
|
|
private const val KEY_ENTRY_ASSIST_BELL_COL = "entry_assist_bell_col"
|
|
private const val KEY_ENTRY_ASSIST_TAB_STOPS = "entry_assist_tab_stops"
|
|
private const val KEY_INSERT_OFF_ON_AID = "insert_off_on_aid"
|
|
private const val KEY_FOUR_COLOR_OVERRIDE = "four_color_override"
|
|
private const val KEY_NUMERIC_FIELD_LOCK = "numeric_field_lock"
|
|
private const val KEY_AUTO_SKIP_ENABLED = "auto_skip_enabled"
|
|
|
|
enum class StartupBehavior {
|
|
SHOW_CONNECT,
|
|
DO_NOTHING,
|
|
AUTO_CONNECT
|
|
}
|
|
|
|
private fun getPrefs(context: Context): SharedPreferences {
|
|
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
}
|
|
|
|
fun isMaskHiddenInputEnabled(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_MASK_HIDDEN_INPUT, true)
|
|
}
|
|
|
|
fun setMaskHiddenInputEnabled(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_MASK_HIDDEN_INPUT, enabled).apply()
|
|
}
|
|
|
|
fun isCursorBlinkEnabled(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_CURSOR_BLINK, true)
|
|
}
|
|
|
|
fun setCursorBlinkEnabled(context: Context, enabled: Boolean) {
|
|
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()
|
|
}
|
|
|
|
fun isVerifyCertsEnabled(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_VERIFY_CERTS, true)
|
|
}
|
|
|
|
fun setVerifyCertsEnabled(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_VERIFY_CERTS, enabled).apply()
|
|
}
|
|
|
|
fun getDefaultGraphicsMode(context: Context): String {
|
|
return getPrefs(context).getString(KEY_DEFAULT_GRAPHICS_MODE, "BOTH") ?: "BOTH"
|
|
}
|
|
|
|
fun setDefaultGraphicsMode(context: Context, mode: String) {
|
|
getPrefs(context).edit().putString(KEY_DEFAULT_GRAPHICS_MODE, mode).apply()
|
|
}
|
|
|
|
fun getDefaultCodePage(context: Context): String {
|
|
return getPrefs(context).getString(KEY_DEFAULT_CODE_PAGE, "037") ?: "037"
|
|
}
|
|
|
|
fun setDefaultCodePage(context: Context, codePage: String) {
|
|
getPrefs(context).edit().putString(KEY_DEFAULT_CODE_PAGE, codePage).apply()
|
|
}
|
|
|
|
// ========== UI Theme ==========
|
|
fun getUiTheme(context: Context): String {
|
|
return getPrefs(context).getString(KEY_UI_THEME, "DARK") ?: "DARK"
|
|
}
|
|
|
|
fun setUiTheme(context: Context, theme: String) {
|
|
getPrefs(context).edit().putString(KEY_UI_THEME, if (theme.equals("LIGHT", ignoreCase = true)) "LIGHT" else "DARK").apply()
|
|
}
|
|
|
|
// ========== Crosshair Ruler ==========
|
|
fun isCrosshairRulerEnabled(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_CROSSHAIR_RULER, false)
|
|
}
|
|
|
|
fun setCrosshairRulerEnabled(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_CROSSHAIR_RULER, enabled).apply()
|
|
}
|
|
|
|
// ========== Cursor Style ==========
|
|
fun getCursorStyle(context: Context): String {
|
|
return getPrefs(context).getString(KEY_CURSOR_STYLE, "BLOCK") ?: "BLOCK"
|
|
}
|
|
|
|
fun setCursorStyle(context: Context, style: String) {
|
|
val s = if (style.equals("UNDERLINE", ignoreCase = true)) "UNDERLINE" else "BLOCK"
|
|
getPrefs(context).edit().putString(KEY_CURSOR_STYLE, s).apply()
|
|
}
|
|
|
|
// ========== Block Select Mode ==========
|
|
fun isBlockSelectModeEnabled(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_BLOCK_SELECT_MODE, false)
|
|
}
|
|
|
|
fun setBlockSelectModeEnabled(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_BLOCK_SELECT_MODE, enabled).apply()
|
|
}
|
|
|
|
// ========== Block Paste Mode ==========
|
|
fun isBlockPasteEnabled(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_BLOCK_PASTE, true)
|
|
}
|
|
|
|
fun setBlockPasteEnabled(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_BLOCK_PASTE, enabled).apply()
|
|
}
|
|
|
|
// ========== Startup Behavior ==========
|
|
fun getStartupBehavior(context: Context): StartupBehavior {
|
|
val s = getPrefs(context).getString(KEY_STARTUP_BEHAVIOR, StartupBehavior.SHOW_CONNECT.name)
|
|
return try {
|
|
StartupBehavior.valueOf(s ?: StartupBehavior.SHOW_CONNECT.name)
|
|
} catch (_: IllegalArgumentException) {
|
|
StartupBehavior.SHOW_CONNECT
|
|
}
|
|
}
|
|
|
|
fun setStartupBehavior(context: Context, behavior: StartupBehavior) {
|
|
getPrefs(context).edit().putString(KEY_STARTUP_BEHAVIOR, behavior.name).apply()
|
|
}
|
|
|
|
// ========== Dynamic Screen Dimensions ==========
|
|
fun getDynamicRows(context: Context): Int {
|
|
return getPrefs(context).getInt(KEY_DYNAMIC_ROWS, 62).coerceIn(24, 255)
|
|
}
|
|
|
|
fun setDynamicRows(context: Context, rows: Int) {
|
|
getPrefs(context).edit().putInt(KEY_DYNAMIC_ROWS, rows.coerceIn(24, 255)).apply()
|
|
}
|
|
|
|
fun getDynamicCols(context: Context): Int {
|
|
return getPrefs(context).getInt(KEY_DYNAMIC_COLS, 160).coerceIn(80, 255)
|
|
}
|
|
|
|
fun setDynamicCols(context: Context, cols: Int) {
|
|
getPrefs(context).edit().putInt(KEY_DYNAMIC_COLS, cols.coerceIn(80, 255)).apply()
|
|
}
|
|
|
|
// ========== Entry Assist (Document Mode) ==========
|
|
fun getEntryAssistDocMode(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_ENTRY_ASSIST_DOC_MODE, false)
|
|
}
|
|
|
|
fun setEntryAssistDocMode(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_ENTRY_ASSIST_DOC_MODE, enabled).apply()
|
|
}
|
|
|
|
fun getEntryAssistWordWrap(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_ENTRY_ASSIST_WORD_WRAP, false)
|
|
}
|
|
|
|
fun setEntryAssistWordWrap(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_ENTRY_ASSIST_WORD_WRAP, enabled).apply()
|
|
}
|
|
|
|
fun getEntryAssistStartCol(context: Context): Int {
|
|
return getPrefs(context).getInt(KEY_ENTRY_ASSIST_START_COL, 1).coerceIn(1, 80)
|
|
}
|
|
|
|
fun setEntryAssistStartCol(context: Context, col: Int) {
|
|
getPrefs(context).edit().putInt(KEY_ENTRY_ASSIST_START_COL, col.coerceIn(1, 80)).apply()
|
|
}
|
|
|
|
fun getEntryAssistEndCol(context: Context): Int {
|
|
return getPrefs(context).getInt(KEY_ENTRY_ASSIST_END_COL, 80).coerceIn(1, 80)
|
|
}
|
|
|
|
fun setEntryAssistEndCol(context: Context, col: Int) {
|
|
getPrefs(context).edit().putInt(KEY_ENTRY_ASSIST_END_COL, col.coerceIn(1, 80)).apply()
|
|
}
|
|
|
|
fun getEntryAssistBell(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_ENTRY_ASSIST_BELL, false)
|
|
}
|
|
|
|
fun setEntryAssistBell(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_ENTRY_ASSIST_BELL, enabled).apply()
|
|
}
|
|
|
|
fun getEntryAssistBellCol(context: Context): Int {
|
|
return getPrefs(context).getInt(KEY_ENTRY_ASSIST_BELL_COL, 75).coerceIn(1, 80)
|
|
}
|
|
|
|
fun setEntryAssistBellCol(context: Context, col: Int) {
|
|
getPrefs(context).edit().putInt(KEY_ENTRY_ASSIST_BELL_COL, col.coerceIn(1, 80)).apply()
|
|
}
|
|
|
|
fun getEntryAssistTabStops(context: Context): String {
|
|
return getPrefs(context).getString(KEY_ENTRY_ASSIST_TAB_STOPS, "5,10,15,20,25,30,35,40,45,50,55,60,65,70,75")
|
|
?: "5,10,15,20,25,30,35,40,45,50,55,60,65,70,75"
|
|
}
|
|
|
|
fun setEntryAssistTabStops(context: Context, stops: String) {
|
|
getPrefs(context).edit().putString(KEY_ENTRY_ASSIST_TAB_STOPS, stops).apply()
|
|
}
|
|
|
|
fun getEntryAssistTabStopsArray(context: Context): IntArray {
|
|
val s = getEntryAssistTabStops(context)
|
|
if (s.isBlank()) return intArrayOf()
|
|
val parts = s.split(",")
|
|
val list = mutableListOf<Int>()
|
|
for (p in parts) {
|
|
val v = p.trim().toIntOrNull()
|
|
if (v != null && v > 0) {
|
|
list.add(v - 1)
|
|
}
|
|
}
|
|
return list.toIntArray()
|
|
}
|
|
|
|
// ========== Terminal Operational Modes ==========
|
|
fun getInsertOffOnAid(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_INSERT_OFF_ON_AID, true)
|
|
}
|
|
|
|
fun setInsertOffOnAid(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_INSERT_OFF_ON_AID, enabled).apply()
|
|
}
|
|
|
|
fun getFourColorOverride(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_FOUR_COLOR_OVERRIDE, false)
|
|
}
|
|
|
|
fun setFourColorOverride(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_FOUR_COLOR_OVERRIDE, enabled).apply()
|
|
}
|
|
|
|
fun getNumericFieldLock(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_NUMERIC_FIELD_LOCK, true)
|
|
}
|
|
|
|
fun setNumericFieldLock(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_NUMERIC_FIELD_LOCK, enabled).apply()
|
|
}
|
|
|
|
fun getAutoSkipEnabled(context: Context): Boolean {
|
|
return getPrefs(context).getBoolean(KEY_AUTO_SKIP_ENABLED, true)
|
|
}
|
|
|
|
fun setAutoSkipEnabled(context: Context, enabled: Boolean) {
|
|
getPrefs(context).edit().putBoolean(KEY_AUTO_SKIP_ENABLED, enabled).apply()
|
|
}
|
|
|
|
// ========== INI Config Export / Import ==========
|
|
|
|
fun exportToIni(context: Context, writer: Writer) {
|
|
val p = PrintWriter(writer)
|
|
val timeStamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(Date())
|
|
p.println("; a3270 / j3270 Configuration File")
|
|
p.println("; Exported on $timeStamp")
|
|
p.println()
|
|
|
|
p.println("[appearance]")
|
|
p.println("javaUiTheme = ${getUiTheme(context)}")
|
|
p.println("crosshairRuler = ${isCrosshairRulerEnabled(context)}")
|
|
p.println("cursorStyle = ${getCursorStyle(context)}")
|
|
p.println()
|
|
|
|
p.println("[behavior]")
|
|
p.println("startupBehavior = ${getStartupBehavior(context).name}")
|
|
val autoHost = HostStorage.getAutoConnectHost(context)
|
|
if (autoHost != null) {
|
|
p.println("autoConnectHost = ${autoHost.host}")
|
|
p.println("autoConnectPort = ${autoHost.port}")
|
|
p.println("autoConnectTls = ${autoHost.useTls}")
|
|
p.println("autoConnectVerifyCert = ${autoHost.tlsVerifyCert}")
|
|
p.println("autoConnectTn3270e = ${autoHost.tn3270e}")
|
|
}
|
|
p.println("codePage = ${getDefaultCodePage(context)}")
|
|
p.println("dynamicRows = ${getDynamicRows(context)}")
|
|
p.println("dynamicCols = ${getDynamicCols(context)}")
|
|
p.println("blockSelectMode = ${isBlockSelectModeEnabled(context)}")
|
|
p.println("blockPaste = ${isBlockPasteEnabled(context)}")
|
|
p.println()
|
|
|
|
p.println("[entryassist]")
|
|
p.println("docMode = ${getEntryAssistDocMode(context)}")
|
|
p.println("wordWrap = ${getEntryAssistWordWrap(context)}")
|
|
p.println("startCol = ${getEntryAssistStartCol(context)}")
|
|
p.println("endCol = ${getEntryAssistEndCol(context)}")
|
|
p.println("bell = ${getEntryAssistBell(context)}")
|
|
p.println("bellCol = ${getEntryAssistBellCol(context)}")
|
|
p.println("tabStops = ${getEntryAssistTabStops(context)}")
|
|
p.println()
|
|
|
|
p.println("[modes]")
|
|
p.println("insertOffOnAid = ${getInsertOffOnAid(context)}")
|
|
p.println("fourColorOverride = ${getFourColorOverride(context)}")
|
|
p.println("numericFieldLock = ${getNumericFieldLock(context)}")
|
|
p.println("autoSkip = ${getAutoSkipEnabled(context)}")
|
|
p.println()
|
|
|
|
p.println("[graphics]")
|
|
p.println("graphicsMode = ${getDefaultGraphicsMode(context)}")
|
|
p.println()
|
|
|
|
p.flush()
|
|
}
|
|
|
|
fun loadFromIni(context: Context, reader: Reader) {
|
|
val bufferedReader = BufferedReader(reader)
|
|
var currentSection = ""
|
|
|
|
bufferedReader.forEachLine { rawLine ->
|
|
val line = rawLine.trim()
|
|
if (line.isNotEmpty() && !line.startsWith(";") && !line.startsWith("#")) {
|
|
if (line.startsWith("[") && line.endsWith("]")) {
|
|
currentSection = line.substring(1, line.length - 1).lowercase(Locale.US).trim()
|
|
} else {
|
|
val eq = line.indexOf('=')
|
|
if (eq >= 0) {
|
|
val key = line.substring(0, eq).trim()
|
|
val value = line.substring(eq + 1).trim()
|
|
applyConfigEntry(context, currentSection, key, value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun applyConfigEntry(context: Context, section: String, key: String, value: String) {
|
|
when (section) {
|
|
"appearance" -> {
|
|
when (key.lowercase(Locale.US)) {
|
|
"javauitheme", "theme", "uitheme" -> setUiTheme(context, value)
|
|
"crosshairruler", "ruler" -> setCrosshairRulerEnabled(context, value.toBoolean())
|
|
"cursorstyle" -> setCursorStyle(context, value)
|
|
}
|
|
}
|
|
"behavior", "connection" -> {
|
|
when (key.lowercase(Locale.US)) {
|
|
"startupbehavior" -> {
|
|
try {
|
|
setStartupBehavior(context, StartupBehavior.valueOf(value.uppercase(Locale.US)))
|
|
} catch (_: Exception) {}
|
|
}
|
|
"codepage", "charset" -> setDefaultCodePage(context, value)
|
|
"dynamicrows", "dynamic_rows" -> value.toIntOrNull()?.let { setDynamicRows(context, it) }
|
|
"dynamiccols", "dynamic_cols" -> value.toIntOrNull()?.let { setDynamicCols(context, it) }
|
|
"blockselectmode" -> setBlockSelectModeEnabled(context, value.toBoolean())
|
|
"blockpaste", "blockpastemode" -> setBlockPasteEnabled(context, value.toBoolean())
|
|
}
|
|
}
|
|
"clipboard" -> {
|
|
when (key.lowercase(Locale.US)) {
|
|
"blockpaste", "blockpastemode" -> setBlockPasteEnabled(context, value.toBoolean())
|
|
}
|
|
}
|
|
"entryassist" -> {
|
|
when (key.lowercase(Locale.US)) {
|
|
"docmode", "entryassistdocmode" -> setEntryAssistDocMode(context, value.toBoolean())
|
|
"wordwrap", "entryassistwordwrap" -> setEntryAssistWordWrap(context, value.toBoolean())
|
|
"startcol", "entryassiststartcol" -> value.toIntOrNull()?.let { setEntryAssistStartCol(context, it) }
|
|
"endcol", "entryassistendcol" -> value.toIntOrNull()?.let { setEntryAssistEndCol(context, it) }
|
|
"bell", "entryassistbell" -> setEntryAssistBell(context, value.toBoolean())
|
|
"bellcol", "entryassistbellcol" -> value.toIntOrNull()?.let { setEntryAssistBellCol(context, it) }
|
|
"tabstops", "entryassisttabstops" -> setEntryAssistTabStops(context, value)
|
|
}
|
|
}
|
|
"modes" -> {
|
|
when (key.lowercase(Locale.US)) {
|
|
"insertoffonaid" -> setInsertOffOnAid(context, value.toBoolean())
|
|
"fourcoloroverride" -> setFourColorOverride(context, value.toBoolean())
|
|
"numericfieldlock" -> setNumericFieldLock(context, value.toBoolean())
|
|
"autoskip", "autoskipenabled" -> setAutoSkipEnabled(context, value.toBoolean())
|
|
}
|
|
}
|
|
"graphics" -> {
|
|
if (key.equals("graphicsmode", ignoreCase = true) || key.equals("mode", ignoreCase = true)) {
|
|
setDefaultGraphicsMode(context, value.uppercase(Locale.US))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|