fix dark mode, improved icons and status reports, readded disclaimer and instructions

This commit is contained in:
Mathieu Broillet 2025-02-08 18:09:11 +01:00
parent 3513b5ab0b
commit 0942019869
3 changed files with 103 additions and 16 deletions

View File

@ -14,6 +14,7 @@ import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row 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.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build 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.MoreVert
import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.Button import androidx.compose.material3.Button
@ -42,6 +45,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.ProbeTable
import com.hoho.android.usbserial.driver.UsbSerialPort import com.hoho.android.usbserial.driver.UsbSerialPort
import com.hoho.android.usbserial.driver.UsbSerialProber 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() { class MainActivity : ComponentActivity() {
private lateinit var usbManager: UsbManager private lateinit var usbManager: UsbManager
private var usbConnection by mutableStateOf(null as UsbDeviceConnection?) private var usbConnection by mutableStateOf(null as UsbDeviceConnection?)
private var isPatching by mutableStateOf(false)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -82,7 +91,9 @@ class MainActivity : ComponentActivity() {
refreshUsbConnection() refreshUsbConnection()
setContent { 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 * Handles sending the patch via USB communication
*/ */
private fun sendPatch() { private fun sendPatch() {
isPatching = true
if (usbConnection == null) { if (usbConnection == null) {
Toast.makeText(this, "No USB device connected!", Toast.LENGTH_SHORT).show() Toast.makeText(this, "No USB device connected!", Toast.LENGTH_SHORT).show()
isPatching = false
return return
} }
@ -156,6 +170,8 @@ class MainActivity : ComponentActivity() {
Toast.makeText(this, "Patch failed: ${e.message}", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Patch failed: ${e.message}", Toast.LENGTH_SHORT).show()
} }
} }
isPatching = false
} }
/** /**
@ -202,13 +218,21 @@ class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @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( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text("DJI FCC Hack") }, title = { Text("DJI FCC Hack") },
actions = { actions = {
IconButton(onClick = onRefresh) { IconButton(onClick = onRefresh, enabled = !isPatching) {
Icon(Icons.Default.Refresh, contentDescription = "Refresh USB Connection") Icon(Icons.Default.Refresh, contentDescription = "Refresh USB Connection")
} }
IconButton(onClick = { /* Open Settings */ }) { IconButton(onClick = { /* Open Settings */ }) {
@ -228,9 +252,9 @@ fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () ->
) { ) {
// App Logo // App Logo
Image( 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", contentDescription = "DJI Logo",
modifier = Modifier.size(100.dp) modifier = Modifier.size(75.dp),
) )
// USB Connection Status // USB Connection Status
@ -246,10 +270,9 @@ fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () ->
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
Image( Icon(
painter = painterResource(id = R.drawable.usb_c_port), if (usbConnected) Icons.Default.CheckCircle else Icons.Default.Clear,
contentDescription = "USB Status", contentDescription = "USB Status"
modifier = Modifier.size(24.dp),
) )
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(8.dp))
Text( Text(
@ -261,16 +284,65 @@ fun MainScreen(usbConnected: Boolean, onRefresh: () -> Unit, onSendPatch: () ->
// Send Patch Button // Send Patch Button
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), shape = RoundedCornerShape(24.dp),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(56.dp), .height(56.dp),
enabled = usbConnected enabled = usbConnected && !isPatching && buttonEnabled
) { ) {
Icon(Icons.Default.Build, contentDescription = "Patch") Icon(Icons.Default.Build, contentDescription = "Patch")
Spacer(Modifier.width(8.dp)) 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 @Composable
fun PreviewMainScreen() { fun PreviewMainScreen() {
DJI_FCC_HACK_Theme { DJI_FCC_HACK_Theme {
MainScreen(usbConnected = true, onRefresh = {}, onSendPatch = {}) MainScreen(usbConnected = true, onRefresh = {}, onSendPatch = {}, isPatching = false)
} }
} }

View File

@ -5,11 +5,11 @@
android:viewportHeight="95.2"> android:viewportHeight="95.2">
<path <path
android:pathData="m120.64,69.13 l12.16,-50.5l-26.1,-0l-11.12,45.29c-1.61,8.83 -11.1,12.96 -17.84,13.06l-18.49,-0l-6.27,18.2l38.86,-0c9.59,-0 23.74,-4.91 28.81,-26.06" android:pathData="m120.64,69.13 l12.16,-50.5l-26.1,-0l-11.12,45.29c-1.61,8.83 -11.1,12.96 -17.84,13.06l-18.49,-0l-6.27,18.2l38.86,-0c9.59,-0 23.74,-4.91 28.81,-26.06"
android:fillColor="#ffffff"/> android:fillColor="#000000"/>
<path <path
android:pathData="m65.86,51.32 l12.27,-51.33l26.86,-0l-13.96,58.39c-2.69,11.27 -11.07,13.98 -18.81,13.98l-61.91,-0c-6.82,-0 -12.54,-2.9 -9.44,-15.92l5.57,-23.29c2.83,-11.81 11.61,-14.51 17.96,-14.51l43.21,-0l-3.48,14.55l-22.06,-0c-3.24,-0 -5.02,0.7 -5.93,4.49l-3.56,14.87c-1.27,5.34 0.59,5.71 4.5,5.71l20.21,-0c3.7,-0 6.95,-0.23 8.56,-6.94" android:pathData="m65.86,51.32 l12.27,-51.33l26.86,-0l-13.96,58.39c-2.69,11.27 -11.07,13.98 -18.81,13.98l-61.91,-0c-6.82,-0 -12.54,-2.9 -9.44,-15.92l5.57,-23.29c2.83,-11.81 11.61,-14.51 17.96,-14.51l43.21,-0l-3.48,14.55l-22.06,-0c-3.24,-0 -5.02,0.7 -5.93,4.49l-3.56,14.87c-1.27,5.34 0.59,5.71 4.5,5.71l20.21,-0c3.7,-0 6.95,-0.23 8.56,-6.94"
android:fillColor="#ffffff"/> android:fillColor="#000000"/>
<path <path
android:pathData="m138.69,18.63 l-12.64,53.73l26.09,-0l12.64,-53.73l-26.09,-0z" android:pathData="m138.69,18.63 l-12.64,53.73l26.09,-0l12.64,-53.73l-26.09,-0z"
android:fillColor="#ffffff"/> android:fillColor="#000000"/>
</vector> </vector>

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="46.51dp"
android:height="26.87dp"
android:viewportWidth="164.78"
android:viewportHeight="95.2">
<path
android:pathData="m120.64,69.13 l12.16,-50.5l-26.1,-0l-11.12,45.29c-1.61,8.83 -11.1,12.96 -17.84,13.06l-18.49,-0l-6.27,18.2l38.86,-0c9.59,-0 23.74,-4.91 28.81,-26.06"
android:fillColor="#fff"/>
<path
android:pathData="m65.86,51.32 l12.27,-51.33l26.86,-0l-13.96,58.39c-2.69,11.27 -11.07,13.98 -18.81,13.98l-61.91,-0c-6.82,-0 -12.54,-2.9 -9.44,-15.92l5.57,-23.29c2.83,-11.81 11.61,-14.51 17.96,-14.51l43.21,-0l-3.48,14.55l-22.06,-0c-3.24,-0 -5.02,0.7 -5.93,4.49l-3.56,14.87c-1.27,5.34 0.59,5.71 4.5,5.71l20.21,-0c3.7,-0 6.95,-0.23 8.56,-6.94"
android:fillColor="#fff"/>
<path
android:pathData="m138.69,18.63 l-12.64,53.73l26.09,-0l12.64,-53.73l-26.09,-0z"
android:fillColor="#fff"/>
</vector>