Pith - wedge_android
wedge_android/app/src/main/java/com/vgmlr/wedge/WedgeSync.kt [7.6 kb]
Modified: 18:56:46 154 026 (20 Aug 026)
3 Days Ago
package com.vgmlr.wedge

import android.content.Context
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import java.net.Inet4Address
import java.net.NetworkInterface
import androidx.compose.ui.graphics.RectangleShape

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SyncView(vm: MainViewModel, prefs: PreferenceManager, onBack: () -> Unit) {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Sync", color = colorResource(id = R.color.title_color), fontSize = 18.sp) },
                colors = TopAppBarDefaults.topAppBarColors(containerColor = Color(0xFF486860)),
                navigationIcon = {
                    IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, null, tint = colorResource(id = R.color.title_color)) }
                }
            )
        }
    ) { p ->
        Column(
            Modifier.padding(p).fillMaxSize().imePadding().background(CBg)
                .padding(horizontal = 16.dp).verticalScroll(rememberScrollState())
        ) {
            Spacer(Modifier.height(24.dp))
            SyncViewContent(vm, prefs)
            Spacer(Modifier.height(24.dp))
        }
    }
}

@Composable
fun SyncViewContent(vm: MainViewModel, prefs: PreferenceManager) {
    val context = LocalContext.current
    val sharedPrefs = remember { context.getSharedPreferences("sync_prefs", Context.MODE_PRIVATE) }
    val noteEntity by vm.dao.getNote().collectAsState(initial = null)
    val lastModifiedStr = WedgeDate.getOtcMetadata(
        noteEntity?.lastModified ?: System.currentTimeMillis()
    )

    var selectedRoute by remember { mutableStateOf(sharedPrefs.getString("route", "USB") ?: "USB") }
    var peerIp by remember { mutableStateOf(sharedPrefs.getString("peer_ip", "") ?: "") }
    var autoMerge by remember { mutableStateOf(sharedPrefs.getBoolean("auto_merge", false)) }
    val passKeyFlow by prefs.passKey.collectAsState(initial = "wedge03569")
    var localPassKey by remember { mutableStateOf<String?>(null) }
    val displayPassKey = localPassKey ?: passKeyFlow
    val syncStatusText by vm.syncStatus.collectAsState()
    var showOlderDataDialog by remember { mutableStateOf(false) }

    val syncStamp = remember(syncStatusText) { sharedPrefs.getLong("sync_last_stamp", 0L) }
    val syncedStr = if (syncStamp == 0L) "[none]" else WedgeDate.getOtcMetadata(syncStamp)

    val localIpAddress = remember {
        try {
            NetworkInterface.getNetworkInterfaces().asSequence()
                .flatMap { it.inetAddresses.asSequence() }
                .firstOrNull { !it.isLoopbackAddress && it is Inet4Address }
                ?.hostAddress ?: "Unknown"
        } catch (_: Exception) {
            "Unknown"
        }
    }

    LaunchedEffect(selectedRoute) { sharedPrefs.edit { putString("route", selectedRoute) } }
    LaunchedEffect(peerIp) { sharedPrefs.edit { putString("peer_ip", peerIp) } }
    
    Column(modifier = Modifier.fillMaxWidth()) {

        WgCard {
            WgRow("Route") {
                WgRadio(
                    value = selectedRoute,
                    options = listOf("USB", "WIFI", "BT"),
                    disabledOptions = listOf("BT")
                ) { selectedRoute = it }
            }
            WgDivider()
            WgRow("Pass Key", edit = true) {
                WgField(
                    value = displayPassKey,
                    onValueChange = {
                        localPassKey = it
                        vm.updatePassKey(it)
                    },
                    placeholder = "",
                    modifier = Modifier.weight(1f)
                )
            }
            WgDivider()
            WgRow("Auto-Merge") {
                WgRadio(
                    value = if (autoMerge) "True" else "False",
                    options = listOf("True", "False")
                ) {
                    autoMerge = it == "True"
                    vm.setAutoMerge(autoMerge)
                }
            }
            WgDivider()
            WgRow("Local IP") {
                WgValue(localIpAddress, mono = true, color = CText)
            }
            WgDivider()
            WgRow("Peer IP", edit = true) {
                WgField(
                    value = peerIp,
                    onValueChange = { peerIp = it },
                    placeholder = "0.0.0.0",
                    modifier = Modifier.weight(1f)
                )
            }
            WgDivider()
            WgRow("Modified") {
                WgValue(lastModifiedStr, mono = true)
            }
            WgDivider()
            WgRow("Synced") {
                WgValue(syncedStr, mono = true)
            }
            WgDivider()
            WgRow("Status") {
                WgValue(syncStatusText, mono = true, color = CText)
            }
            WgDivider()
            Button(
                onClick = {
                    vm.trySendNoteText(selectedRoute, peerIp, onOlderThanDestination = {
                        showOlderDataDialog = true
                    })
                },
                enabled = selectedRoute == "USB" || selectedRoute == "WIFI",
                modifier = Modifier.fillMaxWidth().height(48.dp),
                shape = RectangleShape,
                elevation = null,
                colors = ButtonDefaults.buttonColors(
                    containerColor = CChip,
                    contentColor = CText,
                    disabledContainerColor = CCard,
                    disabledContentColor = CChip
                )
            ) {
                Text("Send", fontSize = FS)
            }
        }
    }

    if (showOlderDataDialog) {
        val btnShape = RoundedCornerShape(8.dp)
        val btnColors = ButtonDefaults.buttonColors(
            containerColor = CChip,
            contentColor = CText
        )
        AlertDialog(
            onDismissRequest = { showOlderDataDialog = false },
            shape = RoundedCornerShape(14.dp),
            containerColor = CCard,
            text = {
                Text(
                    text = "Local data is older than destination",
                    fontSize = 17.sp
                )
            },
            dismissButton = {
                Button(
                    onClick = { showOlderDataDialog = false },
                    modifier = Modifier.height(44.dp),
                    colors = btnColors,
                    shape = btnShape
                ) { Text("Oops", color = CText, fontSize = 15.sp) }
            },
            confirmButton = {
                Button(
                    onClick = {
                        showOlderDataDialog = false
                        vm.confirmSendNoteText()
                    },
                    modifier = Modifier.height(44.dp),
                    colors = btnColors,
                    shape = btnShape
                ) { Text("Force", color = CText, fontSize = 15.sp) }
            }
        )
    }
}
Updates
Kerf - Android 157.026
Kiln - Android 157.026
Wedge - Android 156.026
Whittle - Linux 155.026
Grit - Firefox/Chrome 154.026

Menu
Calendar
Project Tin (024/029)
Miter
RSS Feed
User Avatar
@vgmlr
=SUM(parts)
0.01385
256,881 (+135)