mirror of
https://github.com/M4TH1EU/DJI-FCC-HACK.git
synced 2025-07-03 02:03:49 +00:00
fix dark mode, improved icons and status reports, readded disclaimer and instructions
This commit is contained in:
parent
3513b5ab0b
commit
0942019869
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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>
|
15
app/src/main/res/drawable/dji_light.xml
Normal file
15
app/src/main/res/drawable/dji_light.xml
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user