From 0942019869231fa2f1b201ca20c91ac68091891a Mon Sep 17 00:00:00 2001 From: Mathieu Broillet Date: Sat, 8 Feb 2025 18:09:11 +0100 Subject: [PATCH] fix dark mode, improved icons and status reports, readded disclaimer and instructions --- .../djiffchack/MainActivity.kt | 98 ++++++++++++++++--- ...{dji_innovations_logo.xml => dji_dark.xml} | 6 +- app/src/main/res/drawable/dji_light.xml | 15 +++ 3 files changed, 103 insertions(+), 16 deletions(-) rename app/src/main/res/drawable/{dji_innovations_logo.xml => dji_dark.xml} (88%) create mode 100644 app/src/main/res/drawable/dji_light.xml diff --git a/app/src/main/java/ch/mathieubroillet/djiffchack/MainActivity.kt b/app/src/main/java/ch/mathieubroillet/djiffchack/MainActivity.kt index 06350a0..de3d738 100644 --- a/app/src/main/java/ch/mathieubroillet/djiffchack/MainActivity.kt +++ b/app/src/main/java/ch/mathieubroillet/djiffchack/MainActivity.kt @@ -14,6 +14,7 @@ import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -27,6 +28,8 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.Button @@ -42,6 +45,7 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -54,11 +58,16 @@ import com.hoho.android.usbserial.driver.CdcAcmSerialDriver import com.hoho.android.usbserial.driver.ProbeTable import com.hoho.android.usbserial.driver.UsbSerialPort import com.hoho.android.usbserial.driver.UsbSerialProber +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { private lateinit var usbManager: UsbManager private var usbConnection by mutableStateOf(null as UsbDeviceConnection?) + private var isPatching by mutableStateOf(false) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -82,7 +91,9 @@ class MainActivity : ComponentActivity() { refreshUsbConnection() setContent { - MainScreen(usbConnection != null, ::refreshUsbConnection, ::sendPatch) + DJI_FCC_HACK_Theme { + MainScreen(usbConnection != null, ::refreshUsbConnection, ::sendPatch, isPatching) + } } } @@ -120,8 +131,11 @@ class MainActivity : ComponentActivity() { * Handles sending the patch via USB communication */ private fun sendPatch() { + isPatching = true + if (usbConnection == null) { Toast.makeText(this, "No USB device connected!", Toast.LENGTH_SHORT).show() + isPatching = false return } @@ -156,6 +170,8 @@ class MainActivity : ComponentActivity() { Toast.makeText(this, "Patch failed: ${e.message}", Toast.LENGTH_SHORT).show() } } + + isPatching = false } /** @@ -202,13 +218,21 @@ class MainActivity : ComponentActivity() { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () -> Unit) { +fun MainScreen( + usbConnected: Boolean, + onRefresh: () -> Unit, + onSendPatch: () -> Unit, + isPatching: Boolean = false +) { + var buttonText by remember { mutableStateOf("Send FCC Patch") } + var buttonEnabled by remember { mutableStateOf(true) } + Scaffold( topBar = { TopAppBar( title = { Text("DJI FCC Hack") }, actions = { - IconButton(onClick = onRefresh) { + IconButton(onClick = onRefresh, enabled = !isPatching) { Icon(Icons.Default.Refresh, contentDescription = "Refresh USB Connection") } IconButton(onClick = { /* Open Settings */ }) { @@ -228,9 +252,9 @@ fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () -> ) { // App Logo Image( - painter = painterResource(id = R.drawable.dji_innovations_logo), + painter = painterResource(id = isSystemInDarkTheme().let { if (it) R.drawable.dji_light else R.drawable.dji_dark }), contentDescription = "DJI Logo", - modifier = Modifier.size(100.dp) + modifier = Modifier.size(75.dp), ) // USB Connection Status @@ -246,10 +270,9 @@ fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () -> verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { - Image( - painter = painterResource(id = R.drawable.usb_c_port), - contentDescription = "USB Status", - modifier = Modifier.size(24.dp), + Icon( + if (usbConnected) Icons.Default.CheckCircle else Icons.Default.Clear, + contentDescription = "USB Status" ) Spacer(Modifier.width(8.dp)) Text( @@ -261,16 +284,65 @@ fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () -> // Send Patch Button Button( - onClick = onSendPatch, + onClick = { + buttonText = "Successfully patched" + buttonEnabled = false + onSendPatch() + + CoroutineScope(Dispatchers.Main).launch { + delay(5000) + buttonText = "Send FCC Patch" + buttonEnabled = true + } + }, shape = RoundedCornerShape(24.dp), modifier = Modifier .fillMaxWidth() .height(56.dp), - enabled = usbConnected + enabled = usbConnected && !isPatching && buttonEnabled ) { Icon(Icons.Default.Build, contentDescription = "Patch") Spacer(Modifier.width(8.dp)) - Text("Send FCC Patch") + Text(buttonText) + } + + // Instructions Section + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "Instructions", + style = MaterialTheme.typography.titleMedium + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "1. Connect your phone to the bottom port of the RC remote.\n" + + "2. Tap 'Send FCC Patch'.\n" + + "3. Disconnect from the bottom port and connect to the top port.", + style = MaterialTheme.typography.bodyMedium + ) + } + } + + // Disclaimer Section + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "Disclaimer", + style = MaterialTheme.typography.titleMedium, + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "This app is provided as-is and is not affiliated with DJI. Use at your own risk.", + style = MaterialTheme.typography.bodyMedium + ) + } } } } @@ -281,6 +353,6 @@ fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () -> @Composable fun PreviewMainScreen() { DJI_FCC_HACK_Theme { - MainScreen(usbConnected = true, onRefresh = {}, onSendPatch = {}) + MainScreen(usbConnected = true, onRefresh = {}, onSendPatch = {}, isPatching = false) } } \ No newline at end of file diff --git a/app/src/main/res/drawable/dji_innovations_logo.xml b/app/src/main/res/drawable/dji_dark.xml similarity index 88% rename from app/src/main/res/drawable/dji_innovations_logo.xml rename to app/src/main/res/drawable/dji_dark.xml index 9695a7f..057b4f4 100644 --- a/app/src/main/res/drawable/dji_innovations_logo.xml +++ b/app/src/main/res/drawable/dji_dark.xml @@ -5,11 +5,11 @@ android:viewportHeight="95.2"> + android:fillColor="#000000"/> + android:fillColor="#000000"/> + android:fillColor="#000000"/> diff --git a/app/src/main/res/drawable/dji_light.xml b/app/src/main/res/drawable/dji_light.xml new file mode 100644 index 0000000..339c244 --- /dev/null +++ b/app/src/main/res/drawable/dji_light.xml @@ -0,0 +1,15 @@ + + + + +